@atproto/ozone 0.0.16 → 0.0.17-next.1

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 (157) hide show
  1. package/dist/api/util.d.ts +10 -0
  2. package/dist/auth-verifier.d.ts +8 -12
  3. package/dist/communication-service/template.d.ts +2 -2
  4. package/dist/config/config.d.ts +6 -0
  5. package/dist/config/env.d.ts +3 -2
  6. package/dist/config/secrets.d.ts +0 -2
  7. package/dist/context.d.ts +6 -0
  8. package/dist/daemon/blob-diverter.d.ts +26 -0
  9. package/dist/daemon/event-pusher.d.ts +6 -0
  10. package/dist/daemon/index.d.ts +1 -0
  11. package/dist/db/index.js +21 -1
  12. package/dist/db/index.js.map +3 -3
  13. package/dist/db/migrations/20240228T003647759Z-add-label-sigs.d.ts +3 -0
  14. package/dist/db/migrations/index.d.ts +1 -0
  15. package/dist/db/schema/index.d.ts +2 -1
  16. package/dist/db/schema/label.d.ts +4 -0
  17. package/dist/db/schema/moderation_event.d.ts +1 -1
  18. package/dist/db/schema/moderation_subject_status.d.ts +2 -2
  19. package/dist/db/schema/signing_key.d.ts +9 -0
  20. package/dist/index.js +10400 -10313
  21. package/dist/index.js.map +3 -3
  22. package/dist/lexicon/index.d.ts +55 -27
  23. package/dist/lexicon/lexicons.d.ts +5046 -4757
  24. package/dist/lexicon/types/app/bsky/actor/defs.d.ts +23 -1
  25. package/dist/lexicon/types/app/bsky/embed/record.d.ts +2 -1
  26. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +1 -0
  27. package/dist/lexicon/types/app/bsky/graph/defs.d.ts +3 -0
  28. package/dist/lexicon/types/app/bsky/labeler/defs.d.ts +41 -0
  29. package/dist/lexicon/types/app/bsky/labeler/getServices.d.ts +36 -0
  30. package/dist/lexicon/types/app/bsky/labeler/service.d.ts +14 -0
  31. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +0 -304
  32. package/dist/lexicon/types/com/atproto/label/defs.d.ts +23 -0
  33. package/dist/lexicon/types/{com/atproto/admin/createCommunicationTemplate.d.ts → tools/ozone/communication/createTemplate.d.ts} +2 -2
  34. package/dist/lexicon/types/tools/ozone/communication/defs.d.ts +14 -0
  35. package/dist/lexicon/types/{com/atproto/admin/listCommunicationTemplates.d.ts → tools/ozone/communication/listTemplates.d.ts} +2 -2
  36. package/dist/lexicon/types/{com/atproto/admin/updateCommunicationTemplate.d.ts → tools/ozone/communication/updateTemplate.d.ts} +2 -2
  37. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +269 -0
  38. package/dist/lexicon/types/{com/atproto/admin/emitModerationEvent.d.ts → tools/ozone/moderation/emitEvent.d.ts} +5 -4
  39. package/dist/lexicon/types/{com/atproto/admin/getModerationEvent.d.ts → tools/ozone/moderation/getEvent.d.ts} +2 -2
  40. package/dist/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRecord.d.ts +2 -2
  41. package/dist/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRepo.d.ts +2 -2
  42. package/dist/lexicon/types/{com/atproto/admin/queryModerationEvents.d.ts → tools/ozone/moderation/queryEvents.d.ts} +2 -2
  43. package/dist/lexicon/types/{com/atproto/admin/queryModerationStatuses.d.ts → tools/ozone/moderation/queryStatuses.d.ts} +2 -2
  44. package/dist/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/searchRepos.d.ts +2 -2
  45. package/dist/mod-service/index.d.ts +16 -15
  46. package/dist/mod-service/subject.d.ts +1 -1
  47. package/dist/mod-service/types.d.ts +2 -2
  48. package/dist/mod-service/util.d.ts +6 -0
  49. package/dist/mod-service/views.d.ts +9 -3
  50. package/dist/sequencer/sequencer.d.ts +6 -4
  51. package/dist/util.d.ts +2 -0
  52. package/package.json +9 -8
  53. package/src/api/{admin/createCommunicationTemplate.ts → communication/createTemplate.ts} +2 -2
  54. package/src/api/{admin/deleteCommunicationTemplate.ts → communication/deleteTemplate.ts} +2 -2
  55. package/src/api/{admin/listCommunicationTemplates.ts → communication/listTemplates.ts} +2 -2
  56. package/src/api/{admin/updateCommunicationTemplate.ts → communication/updateTemplate.ts} +2 -2
  57. package/src/api/index.ts +21 -21
  58. package/src/api/{temp → label}/fetchLabels.ts +5 -3
  59. package/src/api/label/queryLabels.ts +4 -2
  60. package/src/api/moderation/emitEvent.ts +218 -0
  61. package/src/api/{admin/getModerationEvent.ts → moderation/getEvent.ts} +2 -2
  62. package/src/api/{admin → moderation}/getRecord.ts +3 -3
  63. package/src/api/{admin → moderation}/getRepo.ts +3 -3
  64. package/src/api/{admin/queryModerationEvents.ts → moderation/queryEvents.ts} +3 -3
  65. package/src/api/{admin/queryModerationStatuses.ts → moderation/queryStatuses.ts} +3 -3
  66. package/src/api/{admin → moderation}/searchRepos.ts +2 -2
  67. package/src/api/proxied.ts +8 -8
  68. package/src/api/{moderation → report}/createReport.ts +2 -3
  69. package/src/api/util.ts +119 -0
  70. package/src/auth-verifier.ts +20 -30
  71. package/src/communication-service/template.ts +2 -2
  72. package/src/config/config.ts +24 -7
  73. package/src/config/env.ts +6 -4
  74. package/src/config/secrets.ts +0 -6
  75. package/src/context.ts +36 -12
  76. package/src/daemon/blob-diverter.ts +150 -0
  77. package/src/daemon/context.ts +11 -7
  78. package/src/daemon/event-pusher.ts +58 -15
  79. package/src/daemon/index.ts +1 -0
  80. package/src/db/migrations/20240228T003647759Z-add-label-sigs.ts +25 -0
  81. package/src/db/migrations/index.ts +1 -0
  82. package/src/db/schema/index.ts +2 -0
  83. package/src/db/schema/label.ts +3 -0
  84. package/src/db/schema/moderation_event.ts +11 -11
  85. package/src/db/schema/moderation_subject_status.ts +7 -2
  86. package/src/db/schema/signing_key.ts +10 -0
  87. package/src/lexicon/index.ts +200 -137
  88. package/src/lexicon/lexicons.ts +6310 -6012
  89. package/src/lexicon/types/app/bsky/actor/defs.ts +57 -1
  90. package/src/lexicon/types/app/bsky/embed/record.ts +2 -0
  91. package/src/lexicon/types/app/bsky/feed/defs.ts +1 -0
  92. package/src/lexicon/types/app/bsky/graph/defs.ts +3 -0
  93. package/src/lexicon/types/app/bsky/labeler/defs.ts +93 -0
  94. package/src/lexicon/types/app/bsky/labeler/getServices.ts +51 -0
  95. package/src/lexicon/types/app/bsky/labeler/service.ts +31 -0
  96. package/src/lexicon/types/com/atproto/admin/defs.ts +0 -694
  97. package/src/lexicon/types/com/atproto/label/defs.ts +78 -0
  98. package/src/lexicon/types/{com/atproto/admin/createCommunicationTemplate.ts → tools/ozone/communication/createTemplate.ts} +2 -2
  99. package/src/lexicon/types/tools/ozone/communication/defs.ts +35 -0
  100. package/src/lexicon/types/{com/atproto/admin/listCommunicationTemplates.ts → tools/ozone/communication/listTemplates.ts} +2 -2
  101. package/src/lexicon/types/{com/atproto/admin/updateCommunicationTemplate.ts → tools/ozone/communication/updateTemplate.ts} +2 -2
  102. package/src/lexicon/types/tools/ozone/moderation/defs.ts +641 -0
  103. package/src/lexicon/types/{com/atproto/admin/emitModerationEvent.ts → tools/ozone/moderation/emitEvent.ts} +15 -14
  104. package/src/lexicon/types/{com/atproto/admin/getModerationEvent.ts → tools/ozone/moderation/getEvent.ts} +2 -2
  105. package/src/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRecord.ts +2 -2
  106. package/src/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/getRepo.ts +2 -2
  107. package/src/lexicon/types/{com/atproto/admin/queryModerationEvents.ts → tools/ozone/moderation/queryEvents.ts} +3 -3
  108. package/src/lexicon/types/{com/atproto/admin/queryModerationStatuses.ts → tools/ozone/moderation/queryStatuses.ts} +2 -2
  109. package/src/lexicon/types/{com/atproto/admin → tools/ozone/moderation}/searchRepos.ts +2 -2
  110. package/src/mod-service/index.ts +46 -50
  111. package/src/mod-service/lang.ts +1 -1
  112. package/src/mod-service/status.ts +60 -41
  113. package/src/mod-service/subject.ts +1 -1
  114. package/src/mod-service/types.ts +10 -10
  115. package/src/mod-service/util.ts +49 -5
  116. package/src/mod-service/views.ts +45 -18
  117. package/src/sequencer/sequencer.ts +12 -11
  118. package/src/util.ts +21 -0
  119. package/tests/__snapshots__/blob-divert.test.ts.snap +22 -0
  120. package/tests/__snapshots__/get-record.test.ts.snap +14 -6
  121. package/tests/__snapshots__/get-repo.test.ts.snap +7 -3
  122. package/tests/__snapshots__/moderation-events.test.ts.snap +8 -8
  123. package/tests/__snapshots__/moderation-statuses.test.ts.snap +6 -6
  124. package/tests/_util.ts +5 -0
  125. package/tests/blob-divert.test.ts +87 -0
  126. package/tests/communication-templates.test.ts +33 -37
  127. package/tests/db.test.ts +6 -6
  128. package/tests/get-record.test.ts +22 -12
  129. package/tests/get-repo.test.ts +33 -21
  130. package/tests/moderation-appeals.test.ts +39 -67
  131. package/tests/moderation-events.test.ts +99 -142
  132. package/tests/moderation-status-tags.test.ts +20 -37
  133. package/tests/moderation-statuses.test.ts +132 -65
  134. package/tests/moderation.test.ts +147 -301
  135. package/tests/query-labels.test.ts +86 -10
  136. package/tests/repo-search.test.ts +18 -11
  137. package/tests/sequencer.test.ts +6 -3
  138. package/dist/api/admin/util.d.ts +0 -5
  139. package/dist/api/moderation/util.d.ts +0 -4
  140. package/src/api/admin/emitModerationEvent.ts +0 -170
  141. package/src/api/admin/util.ts +0 -54
  142. package/src/api/moderation/util.ts +0 -67
  143. /package/dist/api/{admin/createCommunicationTemplate.d.ts → communication/createTemplate.d.ts} +0 -0
  144. /package/dist/api/{admin/deleteCommunicationTemplate.d.ts → communication/deleteTemplate.d.ts} +0 -0
  145. /package/dist/api/{admin/emitModerationEvent.d.ts → communication/listTemplates.d.ts} +0 -0
  146. /package/dist/api/{admin/getModerationEvent.d.ts → communication/updateTemplate.d.ts} +0 -0
  147. /package/dist/api/{temp → label}/fetchLabels.d.ts +0 -0
  148. /package/dist/api/{admin/getRecord.d.ts → moderation/emitEvent.d.ts} +0 -0
  149. /package/dist/api/{admin/getRepo.d.ts → moderation/getEvent.d.ts} +0 -0
  150. /package/dist/api/{admin/listCommunicationTemplates.d.ts → moderation/getRecord.d.ts} +0 -0
  151. /package/dist/api/{admin/queryModerationEvents.d.ts → moderation/getRepo.d.ts} +0 -0
  152. /package/dist/api/{admin/queryModerationStatuses.d.ts → moderation/queryEvents.d.ts} +0 -0
  153. /package/dist/api/{admin/searchRepos.d.ts → moderation/queryStatuses.d.ts} +0 -0
  154. /package/dist/api/{admin/updateCommunicationTemplate.d.ts → moderation/searchRepos.d.ts} +0 -0
  155. /package/dist/api/{moderation → report}/createReport.d.ts +0 -0
  156. /package/dist/lexicon/types/{com/atproto/admin/deleteCommunicationTemplate.d.ts → tools/ozone/communication/deleteTemplate.d.ts} +0 -0
  157. /package/src/lexicon/types/{com/atproto/admin/deleteCommunicationTemplate.ts → tools/ozone/communication/deleteTemplate.ts} +0 -0
@@ -27,37 +27,34 @@ describe('communication-templates', () => {
27
27
  }
28
28
 
29
29
  const listTemplates = async () => {
30
- const { data } =
31
- await agent.api.com.atproto.admin.listCommunicationTemplates(
32
- {},
33
- {
34
- headers: network.ozone.adminAuthHeaders('moderator'),
35
- },
36
- )
30
+ const { data } = await agent.api.tools.ozone.communication.listTemplates(
31
+ {},
32
+ {
33
+ headers: await network.ozone.modHeaders('moderator'),
34
+ },
35
+ )
37
36
  return data.communicationTemplates
38
37
  }
39
38
 
40
39
  describe('create templates', () => {
41
40
  it('only allows admins to create new templates', async () => {
42
- const moderatorReq =
43
- agent.api.com.atproto.admin.createCommunicationTemplate(
44
- { ...templateOne, createdBy: sc.dids.bob },
45
- {
46
- encoding: 'application/json',
47
- headers: network.ozone.adminAuthHeaders('moderator'),
48
- },
49
- )
41
+ const moderatorReq = agent.api.tools.ozone.communication.createTemplate(
42
+ { ...templateOne, createdBy: sc.dids.bob },
43
+ {
44
+ encoding: 'application/json',
45
+ headers: await network.ozone.modHeaders('moderator'),
46
+ },
47
+ )
50
48
  await expect(moderatorReq).rejects.toThrow(
51
49
  'Must be an admin to create a communication template',
52
50
  )
53
- const modReq =
54
- await agent.api.com.atproto.admin.createCommunicationTemplate(
55
- { ...templateOne, createdBy: sc.dids.bob },
56
- {
57
- encoding: 'application/json',
58
- headers: network.ozone.adminAuthHeaders('admin'),
59
- },
60
- )
51
+ const modReq = await agent.api.tools.ozone.communication.createTemplate(
52
+ { ...templateOne, createdBy: sc.dids.bob },
53
+ {
54
+ encoding: 'application/json',
55
+ headers: await network.ozone.modHeaders('admin'),
56
+ },
57
+ )
61
58
 
62
59
  expect(modReq.data).toMatchObject({
63
60
  ...templateOne,
@@ -75,11 +72,11 @@ describe('communication-templates', () => {
75
72
  ...templateOne,
76
73
  name: 'Test template 2',
77
74
  }
78
- await agent.api.com.atproto.admin.createCommunicationTemplate(
75
+ await agent.api.tools.ozone.communication.createTemplate(
79
76
  { ...templateTwo, createdBy: sc.dids.bob },
80
77
  {
81
78
  encoding: 'application/json',
82
- headers: network.ozone.adminAuthHeaders('admin'),
79
+ headers: await network.ozone.modHeaders('admin'),
83
80
  },
84
81
  )
85
82
 
@@ -90,14 +87,13 @@ describe('communication-templates', () => {
90
87
  })
91
88
  describe('update template', () => {
92
89
  it('allows moderators to update a template by id', async () => {
93
- const { data } =
94
- await agent.api.com.atproto.admin.updateCommunicationTemplate(
95
- { id: '1', updatedBy: sc.dids.bob, name: '1 Test template' },
96
- {
97
- encoding: 'application/json',
98
- headers: network.ozone.adminAuthHeaders('admin'),
99
- },
100
- )
90
+ const { data } = await agent.api.tools.ozone.communication.updateTemplate(
91
+ { id: '1', updatedBy: sc.dids.bob, name: '1 Test template' },
92
+ {
93
+ encoding: 'application/json',
94
+ headers: await network.ozone.modHeaders('admin'),
95
+ },
96
+ )
101
97
 
102
98
  expect(data.name).not.toEqual(templateOne.name)
103
99
  expect(data.name).toEqual('1 Test template')
@@ -105,11 +101,11 @@ describe('communication-templates', () => {
105
101
  })
106
102
  describe('delete template', () => {
107
103
  it('allows admins to remove a template by id', async () => {
108
- const modReq = agent.api.com.atproto.admin.deleteCommunicationTemplate(
104
+ const modReq = agent.api.tools.ozone.communication.deleteTemplate(
109
105
  { id: '1' },
110
106
  {
111
107
  encoding: 'application/json',
112
- headers: network.ozone.adminAuthHeaders('moderator'),
108
+ headers: await network.ozone.modHeaders('moderator'),
113
109
  },
114
110
  )
115
111
 
@@ -117,11 +113,11 @@ describe('communication-templates', () => {
117
113
  'Must be an admin to delete a communication template',
118
114
  )
119
115
 
120
- await agent.api.com.atproto.admin.deleteCommunicationTemplate(
116
+ await agent.api.tools.ozone.communication.deleteTemplate(
121
117
  { id: '1' },
122
118
  {
123
119
  encoding: 'application/json',
124
- headers: network.ozone.adminAuthHeaders('admin'),
120
+ headers: await network.ozone.modHeaders('admin'),
125
121
  },
126
122
  )
127
123
  const list = await listTemplates()
package/tests/db.test.ts CHANGED
@@ -48,7 +48,7 @@ describe('db', () => {
48
48
  return await dbTxn.db
49
49
  .insertInto('repo_push_event')
50
50
  .values({
51
- eventType: 'takedown',
51
+ eventType: 'pds_takedown',
52
52
  subjectDid: 'x',
53
53
  })
54
54
  .returning('subjectDid')
@@ -68,7 +68,7 @@ describe('db', () => {
68
68
  .executeTakeFirst()
69
69
 
70
70
  expect(row).toMatchObject({
71
- eventType: 'takedown',
71
+ eventType: 'pds_takedown',
72
72
  subjectDid: 'x',
73
73
  })
74
74
  })
@@ -78,7 +78,7 @@ describe('db', () => {
78
78
  await dbTxn.db
79
79
  .insertInto('repo_push_event')
80
80
  .values({
81
- eventType: 'takedown',
81
+ eventType: 'pds_takedown',
82
82
  subjectDid: 'y',
83
83
  })
84
84
  .returning('subjectDid')
@@ -124,7 +124,7 @@ describe('db', () => {
124
124
  leakedTx = dbTxn
125
125
  await dbTxn.db
126
126
  .insertInto('repo_push_event')
127
- .values({ eventType: 'takedown', subjectDid: 'a' })
127
+ .values({ eventType: 'pds_takedown', subjectDid: 'a' })
128
128
  .execute()
129
129
  throw new Error('test tx failed')
130
130
  })
@@ -132,7 +132,7 @@ describe('db', () => {
132
132
 
133
133
  const attempt = leakedTx?.db
134
134
  .insertInto('repo_push_event')
135
- .values({ eventType: 'takedown', subjectDid: 'b' })
135
+ .values({ eventType: 'pds_takedown', subjectDid: 'b' })
136
136
  .execute()
137
137
  await expect(attempt).rejects.toThrow('tx already failed')
138
138
 
@@ -156,7 +156,7 @@ describe('db', () => {
156
156
  const query = dbTxn.db
157
157
  .insertInto('repo_push_event')
158
158
  .values({
159
- eventType: 'takedown',
159
+ eventType: 'pds_takedown',
160
160
  subjectDid: name,
161
161
  })
162
162
  .execute()
@@ -1,4 +1,10 @@
1
- import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
1
+ import {
2
+ SeedClient,
3
+ TestNetwork,
4
+ basicSeed,
5
+ TestOzone,
6
+ ModeratorClient,
7
+ } from '@atproto/dev-env'
2
8
  import AtpAgent from '@atproto/api'
3
9
  import { AtUri } from '@atproto/syntax'
4
10
  import {
@@ -9,15 +15,19 @@ import { forSnapshot } from './_util'
9
15
 
10
16
  describe('admin get record view', () => {
11
17
  let network: TestNetwork
18
+ let ozone: TestOzone
12
19
  let agent: AtpAgent
13
20
  let sc: SeedClient
21
+ let modClient: ModeratorClient
14
22
 
15
23
  beforeAll(async () => {
16
24
  network = await TestNetwork.create({
17
25
  dbPostgresSchema: 'ozone_admin_get_record',
18
26
  })
19
- agent = network.pds.getClient()
27
+ ozone = network.ozone
28
+ agent = ozone.getClient()
20
29
  sc = network.getSeedClient()
30
+ modClient = ozone.getModClient()
21
31
  await basicSeed(sc)
22
32
  await network.processAll()
23
33
  })
@@ -46,8 +56,8 @@ describe('admin get record view', () => {
46
56
  cid: sc.posts[sc.dids.alice][0].ref.cidStr,
47
57
  },
48
58
  })
49
- await sc.emitModerationEvent({
50
- event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
59
+ await modClient.emitEvent({
60
+ event: { $type: 'tools.ozone.moderation.defs#modEventTakedown' },
51
61
  subject: {
52
62
  $type: 'com.atproto.repo.strongRef',
53
63
  uri: sc.posts[sc.dids.alice][0].ref.uriStr,
@@ -57,26 +67,26 @@ describe('admin get record view', () => {
57
67
  })
58
68
 
59
69
  it('gets a record by uri, even when taken down.', async () => {
60
- const result = await agent.api.com.atproto.admin.getRecord(
70
+ const result = await agent.api.tools.ozone.moderation.getRecord(
61
71
  { uri: sc.posts[sc.dids.alice][0].ref.uriStr },
62
- { headers: network.pds.adminAuthHeaders() },
72
+ { headers: await ozone.modHeaders() },
63
73
  )
64
74
  expect(forSnapshot(result.data)).toMatchSnapshot()
65
75
  })
66
76
 
67
77
  it('gets a record by uri and cid.', async () => {
68
- const result = await agent.api.com.atproto.admin.getRecord(
78
+ const result = await agent.api.tools.ozone.moderation.getRecord(
69
79
  {
70
80
  uri: sc.posts[sc.dids.alice][0].ref.uriStr,
71
81
  cid: sc.posts[sc.dids.alice][0].ref.cidStr,
72
82
  },
73
- { headers: network.pds.adminAuthHeaders() },
83
+ { headers: await ozone.modHeaders() },
74
84
  )
75
85
  expect(forSnapshot(result.data)).toMatchSnapshot()
76
86
  })
77
87
 
78
88
  it('fails when record does not exist.', async () => {
79
- const promise = agent.api.com.atproto.admin.getRecord(
89
+ const promise = agent.api.tools.ozone.moderation.getRecord(
80
90
  {
81
91
  uri: AtUri.make(
82
92
  sc.dids.alice,
@@ -84,18 +94,18 @@ describe('admin get record view', () => {
84
94
  'badrkey',
85
95
  ).toString(),
86
96
  },
87
- { headers: network.pds.adminAuthHeaders() },
97
+ { headers: await ozone.modHeaders() },
88
98
  )
89
99
  await expect(promise).rejects.toThrow('Record not found')
90
100
  })
91
101
 
92
102
  it('fails when record cid does not exist.', async () => {
93
- const promise = agent.api.com.atproto.admin.getRecord(
103
+ const promise = agent.api.tools.ozone.moderation.getRecord(
94
104
  {
95
105
  uri: sc.posts[sc.dids.alice][0].ref.uriStr,
96
106
  cid: sc.posts[sc.dids.alice][1].ref.cidStr, // Mismatching cid
97
107
  },
98
- { headers: network.pds.adminAuthHeaders() },
108
+ { headers: await ozone.modHeaders() },
99
109
  )
100
110
  await expect(promise).rejects.toThrow('Record not found')
101
111
  })
@@ -1,4 +1,10 @@
1
- import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
1
+ import {
2
+ SeedClient,
3
+ TestNetwork,
4
+ TestOzone,
5
+ basicSeed,
6
+ ModeratorClient,
7
+ } from '@atproto/dev-env'
2
8
  import AtpAgent from '@atproto/api'
3
9
  import {
4
10
  REASONOTHER,
@@ -8,15 +14,21 @@ import { forSnapshot } from './_util'
8
14
 
9
15
  describe('admin get repo view', () => {
10
16
  let network: TestNetwork
17
+ let ozone: TestOzone
11
18
  let agent: AtpAgent
19
+ let pdsAgent: AtpAgent
12
20
  let sc: SeedClient
21
+ let modClient: ModeratorClient
13
22
 
14
23
  beforeAll(async () => {
15
24
  network = await TestNetwork.create({
16
25
  dbPostgresSchema: 'ozone_admin_get_repo',
17
26
  })
18
- agent = network.pds.getClient()
27
+ ozone = network.ozone
28
+ agent = ozone.getClient()
29
+ pdsAgent = network.pds.getClient()
19
30
  sc = network.getSeedClient()
31
+ modClient = ozone.getModClient()
20
32
  await basicSeed(sc)
21
33
  await network.processAll()
22
34
  })
@@ -26,8 +38,8 @@ describe('admin get repo view', () => {
26
38
  })
27
39
 
28
40
  beforeAll(async () => {
29
- await sc.emitModerationEvent({
30
- event: { $type: 'com.atproto.admin.defs#modEventAcknowledge' },
41
+ await modClient.emitEvent({
42
+ event: { $type: 'tools.ozone.moderation.defs#modEventAcknowledge' },
31
43
  subject: {
32
44
  $type: 'com.atproto.admin.defs#repoRef',
33
45
  did: sc.dids.alice,
@@ -50,8 +62,8 @@ describe('admin get repo view', () => {
50
62
  did: sc.dids.alice,
51
63
  },
52
64
  })
53
- await sc.emitModerationEvent({
54
- event: { $type: 'com.atproto.admin.defs#modEventTakedown' },
65
+ await modClient.emitEvent({
66
+ event: { $type: 'tools.ozone.moderation.defs#modEventTakedown' },
55
67
  subject: {
56
68
  $type: 'com.atproto.admin.defs#repoRef',
57
69
  did: sc.dids.alice,
@@ -60,25 +72,25 @@ describe('admin get repo view', () => {
60
72
  })
61
73
 
62
74
  it('gets a repo by did, even when taken down.', async () => {
63
- const result = await agent.api.com.atproto.admin.getRepo(
75
+ const result = await agent.api.tools.ozone.moderation.getRepo(
64
76
  { did: sc.dids.alice },
65
- { headers: network.pds.adminAuthHeaders() },
77
+ { headers: await ozone.modHeaders() },
66
78
  )
67
79
  expect(forSnapshot(result.data)).toMatchSnapshot()
68
80
  })
69
81
 
70
82
  it('does not include account emails for triage mods.', async () => {
71
- const { data: admin } = await agent.api.com.atproto.admin.getRepo(
83
+ const { data: admin } = await agent.api.tools.ozone.moderation.getRepo(
72
84
  { did: sc.dids.bob },
73
- { headers: network.pds.adminAuthHeaders() },
85
+ { headers: await ozone.modHeaders() },
74
86
  )
75
- const { data: moderator } = await agent.api.com.atproto.admin.getRepo(
87
+ const { data: moderator } = await agent.api.tools.ozone.moderation.getRepo(
76
88
  { did: sc.dids.bob },
77
- { headers: network.pds.adminAuthHeaders('moderator') },
89
+ { headers: await ozone.modHeaders('moderator') },
78
90
  )
79
- const { data: triage } = await agent.api.com.atproto.admin.getRepo(
91
+ const { data: triage } = await agent.api.tools.ozone.moderation.getRepo(
80
92
  { did: sc.dids.bob },
81
- { headers: network.pds.adminAuthHeaders('triage') },
93
+ { headers: await ozone.modHeaders('triage') },
82
94
  )
83
95
  expect(admin.email).toEqual('bob@test.com')
84
96
  expect(moderator.email).toEqual('bob@test.com')
@@ -88,9 +100,9 @@ describe('admin get repo view', () => {
88
100
 
89
101
  it('includes emailConfirmedAt timestamp', async () => {
90
102
  const { data: beforeEmailVerification } =
91
- await agent.api.com.atproto.admin.getRepo(
103
+ await agent.api.tools.ozone.moderation.getRepo(
92
104
  { did: sc.dids.bob },
93
- { headers: network.pds.adminAuthHeaders() },
105
+ { headers: await ozone.modHeaders() },
94
106
  )
95
107
 
96
108
  expect(beforeEmailVerification.emailConfirmedAt).toBeUndefined()
@@ -101,7 +113,7 @@ describe('admin get repo view', () => {
101
113
  sc.dids.bob,
102
114
  'confirm_email',
103
115
  )
104
- await agent.api.com.atproto.server.confirmEmail(
116
+ await pdsAgent.api.com.atproto.server.confirmEmail(
105
117
  { email: bobsAccount.email, token: verificationToken },
106
118
  {
107
119
  encoding: 'application/json',
@@ -110,9 +122,9 @@ describe('admin get repo view', () => {
110
122
  },
111
123
  )
112
124
  const { data: afterEmailVerification } =
113
- await agent.api.com.atproto.admin.getRepo(
125
+ await agent.api.tools.ozone.moderation.getRepo(
114
126
  { did: sc.dids.bob },
115
- { headers: network.pds.adminAuthHeaders() },
127
+ { headers: await ozone.modHeaders() },
116
128
  )
117
129
 
118
130
  expect(afterEmailVerification.emailConfirmedAt).toBeTruthy()
@@ -122,9 +134,9 @@ describe('admin get repo view', () => {
122
134
  })
123
135
 
124
136
  it('fails when repo does not exist.', async () => {
125
- const promise = agent.api.com.atproto.admin.getRepo(
137
+ const promise = agent.api.tools.ozone.moderation.getRepo(
126
138
  { did: 'did:plc:doesnotexist' },
127
- { headers: network.pds.adminAuthHeaders() },
139
+ { headers: await ozone.modHeaders() },
128
140
  )
129
141
  await expect(promise).rejects.toThrow('Repo not found')
130
142
  })
@@ -1,49 +1,32 @@
1
- import { TestNetwork, SeedClient, basicSeed } from '@atproto/dev-env'
2
- import AtpAgent, {
3
- ComAtprotoAdminDefs,
4
- ComAtprotoAdminEmitModerationEvent,
5
- ComAtprotoAdminQueryModerationStatuses,
6
- } from '@atproto/api'
1
+ import {
2
+ TestNetwork,
3
+ SeedClient,
4
+ basicSeed,
5
+ ModeratorClient,
6
+ } from '@atproto/dev-env'
7
+ import { ToolsOzoneModerationDefs } from '@atproto/api'
7
8
  import {
8
9
  REASONMISLEADING,
9
10
  REASONSPAM,
11
+ REASONAPPEAL,
10
12
  } from '../src/lexicon/types/com/atproto/moderation/defs'
11
13
  import {
12
14
  REVIEWCLOSED,
13
15
  REVIEWOPEN,
14
- } from '@atproto/api/src/client/types/com/atproto/admin/defs'
15
- import { REASONAPPEAL } from '@atproto/api/src/client/types/com/atproto/moderation/defs'
16
- import { REVIEWESCALATED } from '../src/lexicon/types/com/atproto/admin/defs'
16
+ REVIEWESCALATED,
17
+ } from '@atproto/api/src/client/types/tools/ozone/moderation/defs'
17
18
 
18
19
  describe('moderation-appeals', () => {
19
20
  let network: TestNetwork
20
- let agent: AtpAgent
21
- let pdsAgent: AtpAgent
22
21
  let sc: SeedClient
23
-
24
- const emitModerationEvent = async (
25
- eventData: ComAtprotoAdminEmitModerationEvent.InputSchema,
26
- ) => {
27
- return pdsAgent.api.com.atproto.admin.emitModerationEvent(eventData, {
28
- encoding: 'application/json',
29
- headers: network.ozone.adminAuthHeaders('moderator'),
30
- })
31
- }
32
-
33
- const queryModerationStatuses = (
34
- statusQuery: ComAtprotoAdminQueryModerationStatuses.QueryParams,
35
- ) =>
36
- agent.api.com.atproto.admin.queryModerationStatuses(statusQuery, {
37
- headers: network.ozone.adminAuthHeaders('moderator'),
38
- })
22
+ let modClient: ModeratorClient
39
23
 
40
24
  beforeAll(async () => {
41
25
  network = await TestNetwork.create({
42
26
  dbPostgresSchema: 'ozone_moderation_appeals',
43
27
  })
44
- agent = network.ozone.getClient()
45
- pdsAgent = network.pds.getClient()
46
28
  sc = network.getSeedClient()
29
+ modClient = network.ozone.getModClient()
47
30
  await basicSeed(sc)
48
31
  await network.processAll()
49
32
  })
@@ -56,13 +39,13 @@ describe('moderation-appeals', () => {
56
39
  subject: string,
57
40
  status: string,
58
41
  appealed: boolean | undefined,
59
- ): Promise<ComAtprotoAdminDefs.SubjectStatusView | undefined> => {
60
- const { data } = await queryModerationStatuses({
42
+ ): Promise<ToolsOzoneModerationDefs.SubjectStatusView | undefined> => {
43
+ const res = await modClient.queryStatuses({
61
44
  subject,
62
45
  })
63
- expect(data.subjectStatuses[0]?.reviewState).toEqual(status)
64
- expect(data.subjectStatuses[0]?.appealed).toEqual(appealed)
65
- return data.subjectStatuses[0]
46
+ expect(res.subjectStatuses[0]?.reviewState).toEqual(status)
47
+ expect(res.subjectStatuses[0]?.appealed).toEqual(appealed)
48
+ return res.subjectStatuses[0]
66
49
  }
67
50
 
68
51
  describe('appeals from users', () => {
@@ -83,13 +66,12 @@ describe('moderation-appeals', () => {
83
66
 
84
67
  it('only changes subject status if original author of the content or a moderator is appealing', async () => {
85
68
  // Create a report by alice
86
- await emitModerationEvent({
69
+ await modClient.emitEvent({
87
70
  event: {
88
- $type: 'com.atproto.admin.defs#modEventReport',
71
+ $type: 'tools.ozone.moderation.defs#modEventReport',
89
72
  reportType: REASONMISLEADING,
90
73
  },
91
74
  subject: getBobsPostSubject(),
92
- createdBy: sc.dids.alice,
93
75
  })
94
76
 
95
77
  await assertBobsPostStatus(REVIEWOPEN, undefined)
@@ -108,13 +90,12 @@ describe('moderation-appeals', () => {
108
90
  await assertBobsPostStatus(REVIEWOPEN, undefined)
109
91
 
110
92
  // Emit report event as moderator
111
- await emitModerationEvent({
93
+ await modClient.emitEvent({
112
94
  event: {
113
- $type: 'com.atproto.admin.defs#modEventReport',
95
+ $type: 'tools.ozone.moderation.defs#modEventReport',
114
96
  reportType: REASONAPPEAL,
115
97
  },
116
98
  subject: getBobsPostSubject(),
117
- createdBy: sc.dids.alice,
118
99
  })
119
100
 
120
101
  // Verify that appeal status changed when appeal report was emitted by moderator
@@ -151,23 +132,21 @@ describe('moderation-appeals', () => {
151
132
  })
152
133
  it('allows multiple appeals and updates last appealed timestamp', async () => {
153
134
  // Resolve appeal with acknowledge
154
- await emitModerationEvent({
135
+ await modClient.emitEvent({
155
136
  event: {
156
- $type: 'com.atproto.admin.defs#modEventResolveAppeal',
137
+ $type: 'tools.ozone.moderation.defs#modEventResolveAppeal',
157
138
  },
158
139
  subject: getBobsPostSubject(),
159
- createdBy: sc.dids.carol,
160
140
  })
161
141
 
162
142
  const previousStatus = await assertBobsPostStatus(REVIEWESCALATED, false)
163
143
 
164
- await emitModerationEvent({
144
+ await modClient.emitEvent({
165
145
  event: {
166
- $type: 'com.atproto.admin.defs#modEventReport',
146
+ $type: 'tools.ozone.moderation.defs#modEventReport',
167
147
  reportType: REASONAPPEAL,
168
148
  },
169
149
  subject: getBobsPostSubject(),
170
- createdBy: sc.dids.bob,
171
150
  })
172
151
 
173
152
  // Verify that even after the appeal event by bob for his post, the appeal status is true again with new timestamp
@@ -186,32 +165,29 @@ describe('moderation-appeals', () => {
186
165
  })
187
166
  it('appeal status is maintained while review state changes based on incoming events', async () => {
188
167
  // Bob reports alice's post
189
- await emitModerationEvent({
168
+ await modClient.emitEvent({
190
169
  event: {
191
- $type: 'com.atproto.admin.defs#modEventReport',
170
+ $type: 'tools.ozone.moderation.defs#modEventReport',
192
171
  reportType: REASONMISLEADING,
193
172
  },
194
173
  subject: getAlicesPostSubject(),
195
- createdBy: sc.dids.bob,
196
174
  })
197
175
 
198
176
  // Moderator acknowledges the report, assume a label was applied too
199
- await emitModerationEvent({
177
+ await modClient.emitEvent({
200
178
  event: {
201
- $type: 'com.atproto.admin.defs#modEventAcknowledge',
179
+ $type: 'tools.ozone.moderation.defs#modEventAcknowledge',
202
180
  },
203
181
  subject: getAlicesPostSubject(),
204
- createdBy: sc.dids.carol,
205
182
  })
206
183
 
207
184
  // Alice appeals the report
208
- await emitModerationEvent({
185
+ await modClient.emitEvent({
209
186
  event: {
210
- $type: 'com.atproto.admin.defs#modEventReport',
187
+ $type: 'tools.ozone.moderation.defs#modEventReport',
211
188
  reportType: REASONAPPEAL,
212
189
  },
213
190
  subject: getAlicesPostSubject(),
214
- createdBy: sc.dids.alice,
215
191
  })
216
192
 
217
193
  await assertSubjectStatus(
@@ -221,13 +197,12 @@ describe('moderation-appeals', () => {
221
197
  )
222
198
 
223
199
  // Bob reports it again
224
- await emitModerationEvent({
200
+ await modClient.emitEvent({
225
201
  event: {
226
- $type: 'com.atproto.admin.defs#modEventReport',
202
+ $type: 'tools.ozone.moderation.defs#modEventReport',
227
203
  reportType: REASONSPAM,
228
204
  },
229
205
  subject: getAlicesPostSubject(),
230
- createdBy: sc.dids.bob,
231
206
  })
232
207
 
233
208
  // Assert that the status is still REVIEWESCALATED, as report events are meant to do
@@ -238,12 +213,11 @@ describe('moderation-appeals', () => {
238
213
  )
239
214
 
240
215
  // Emit an escalation event
241
- await emitModerationEvent({
216
+ await modClient.emitEvent({
242
217
  event: {
243
- $type: 'com.atproto.admin.defs#modEventEscalate',
218
+ $type: 'tools.ozone.moderation.defs#modEventEscalate',
244
219
  },
245
220
  subject: getAlicesPostSubject(),
246
- createdBy: sc.dids.carol,
247
221
  })
248
222
 
249
223
  await assertSubjectStatus(
@@ -253,25 +227,23 @@ describe('moderation-appeals', () => {
253
227
  )
254
228
 
255
229
  // Emit an acknowledge event
256
- await emitModerationEvent({
230
+ await modClient.emitEvent({
257
231
  event: {
258
- $type: 'com.atproto.admin.defs#modEventAcknowledge',
232
+ $type: 'tools.ozone.moderation.defs#modEventAcknowledge',
259
233
  },
260
234
  subject: getAlicesPostSubject(),
261
- createdBy: sc.dids.carol,
262
235
  })
263
236
 
264
237
  // Assert that status moved on to reviewClosed while appealed status is still true
265
238
  await assertSubjectStatus(getAlicesPostSubject().uri, REVIEWCLOSED, true)
266
239
 
267
240
  // Emit a resolveAppeal event
268
- await emitModerationEvent({
241
+ await modClient.emitEvent({
269
242
  event: {
270
- $type: 'com.atproto.admin.defs#modEventResolveAppeal',
243
+ $type: 'tools.ozone.moderation.defs#modEventResolveAppeal',
271
244
  comment: 'lgtm',
272
245
  },
273
246
  subject: getAlicesPostSubject(),
274
- createdBy: sc.dids.carol,
275
247
  })
276
248
 
277
249
  // Assert that status stayed the same while appealed status is still true