@atproto/ozone 0.1.53 → 0.1.54

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 (133) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +6 -0
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/moderation/queryStatuses.d.ts.map +1 -1
  6. package/dist/api/moderation/queryStatuses.js +6 -1
  7. package/dist/api/moderation/queryStatuses.js.map +1 -1
  8. package/dist/api/setting/listOptions.d.ts +4 -0
  9. package/dist/api/setting/listOptions.d.ts.map +1 -0
  10. package/dist/api/setting/listOptions.js +38 -0
  11. package/dist/api/setting/listOptions.js.map +1 -0
  12. package/dist/api/setting/removeOptions.d.ts +4 -0
  13. package/dist/api/setting/removeOptions.d.ts.map +1 -0
  14. package/dist/api/setting/removeOptions.js +55 -0
  15. package/dist/api/setting/removeOptions.js.map +1 -0
  16. package/dist/api/setting/upsertOption.d.ts +4 -0
  17. package/dist/api/setting/upsertOption.d.ts.map +1 -0
  18. package/dist/api/setting/upsertOption.js +109 -0
  19. package/dist/api/setting/upsertOption.js.map +1 -0
  20. package/dist/context.d.ts +3 -0
  21. package/dist/context.d.ts.map +1 -1
  22. package/dist/context.js +6 -0
  23. package/dist/context.js.map +1 -1
  24. package/dist/db/migrations/20241018T205730722Z-setting.d.ts +4 -0
  25. package/dist/db/migrations/20241018T205730722Z-setting.d.ts.map +1 -0
  26. package/dist/db/migrations/20241018T205730722Z-setting.js +26 -0
  27. package/dist/db/migrations/20241018T205730722Z-setting.js.map +1 -0
  28. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.d.ts +4 -0
  29. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.d.ts.map +1 -0
  30. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.js +57 -0
  31. package/dist/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.js.map +1 -0
  32. package/dist/db/migrations/index.d.ts +2 -0
  33. package/dist/db/migrations/index.d.ts.map +1 -1
  34. package/dist/db/migrations/index.js +3 -1
  35. package/dist/db/migrations/index.js.map +1 -1
  36. package/dist/db/schema/index.d.ts +2 -1
  37. package/dist/db/schema/index.d.ts.map +1 -1
  38. package/dist/db/schema/moderation_event.d.ts +1 -1
  39. package/dist/db/schema/moderation_event.d.ts.map +1 -1
  40. package/dist/db/schema/moderation_subject_status.d.ts +6 -0
  41. package/dist/db/schema/moderation_subject_status.d.ts.map +1 -1
  42. package/dist/db/schema/setting.d.ts +21 -0
  43. package/dist/db/schema/setting.d.ts.map +1 -0
  44. package/dist/db/schema/setting.js +5 -0
  45. package/dist/db/schema/setting.js.map +1 -0
  46. package/dist/lexicon/index.d.ts +11 -0
  47. package/dist/lexicon/index.d.ts.map +1 -1
  48. package/dist/lexicon/index.js +32 -1
  49. package/dist/lexicon/index.js.map +1 -1
  50. package/dist/lexicon/lexicons.d.ts +359 -0
  51. package/dist/lexicon/lexicons.d.ts.map +1 -1
  52. package/dist/lexicon/lexicons.js +402 -3
  53. package/dist/lexicon/lexicons.js.map +1 -1
  54. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +58 -2
  55. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
  56. package/dist/lexicon/types/tools/ozone/moderation/defs.js +50 -0
  57. package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
  58. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts +1 -1
  59. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
  60. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts +10 -0
  61. package/dist/lexicon/types/tools/ozone/moderation/queryStatuses.d.ts.map +1 -1
  62. package/dist/lexicon/types/tools/ozone/setting/defs.d.ts +20 -0
  63. package/dist/lexicon/types/tools/ozone/setting/defs.d.ts.map +1 -0
  64. package/dist/lexicon/types/tools/ozone/setting/defs.js +15 -0
  65. package/dist/lexicon/types/tools/ozone/setting/defs.js.map +1 -0
  66. package/dist/lexicon/types/tools/ozone/setting/listOptions.d.ts +43 -0
  67. package/dist/lexicon/types/tools/ozone/setting/listOptions.d.ts.map +1 -0
  68. package/dist/lexicon/types/tools/ozone/setting/listOptions.js +3 -0
  69. package/dist/lexicon/types/tools/ozone/setting/listOptions.js.map +1 -0
  70. package/dist/lexicon/types/tools/ozone/setting/removeOptions.d.ts +40 -0
  71. package/dist/lexicon/types/tools/ozone/setting/removeOptions.d.ts.map +1 -0
  72. package/dist/lexicon/types/tools/ozone/setting/removeOptions.js +3 -0
  73. package/dist/lexicon/types/tools/ozone/setting/removeOptions.js.map +1 -0
  74. package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts +45 -0
  75. package/dist/lexicon/types/tools/ozone/setting/upsertOption.d.ts.map +1 -0
  76. package/dist/lexicon/types/tools/ozone/setting/upsertOption.js +3 -0
  77. package/dist/lexicon/types/tools/ozone/setting/upsertOption.js.map +1 -0
  78. package/dist/mod-service/index.d.ts +19 -2
  79. package/dist/mod-service/index.d.ts.map +1 -1
  80. package/dist/mod-service/index.js +37 -1
  81. package/dist/mod-service/index.js.map +1 -1
  82. package/dist/mod-service/status.d.ts +2 -22
  83. package/dist/mod-service/status.d.ts.map +1 -1
  84. package/dist/mod-service/status.js +91 -1
  85. package/dist/mod-service/status.js.map +1 -1
  86. package/dist/mod-service/types.d.ts +19 -1
  87. package/dist/mod-service/types.d.ts.map +1 -1
  88. package/dist/mod-service/views.d.ts.map +1 -1
  89. package/dist/mod-service/views.js +36 -1
  90. package/dist/mod-service/views.js.map +1 -1
  91. package/dist/setting/service.d.ts +33 -0
  92. package/dist/setting/service.d.ts.map +1 -0
  93. package/dist/setting/service.js +101 -0
  94. package/dist/setting/service.js.map +1 -0
  95. package/package.json +6 -6
  96. package/src/api/index.ts +6 -0
  97. package/src/api/moderation/queryStatuses.ts +10 -0
  98. package/src/api/setting/listOptions.ts +44 -0
  99. package/src/api/setting/removeOptions.ts +63 -0
  100. package/src/api/setting/upsertOption.ts +142 -0
  101. package/src/context.ts +8 -0
  102. package/src/db/migrations/20241018T205730722Z-setting.ts +27 -0
  103. package/src/db/migrations/20241026T205730722Z-add-hosting-status-to-subject-status.ts +57 -0
  104. package/src/db/migrations/index.ts +2 -0
  105. package/src/db/schema/index.ts +3 -1
  106. package/src/db/schema/moderation_event.ts +3 -0
  107. package/src/db/schema/moderation_subject_status.ts +6 -0
  108. package/src/db/schema/setting.ts +24 -0
  109. package/src/lexicon/index.ts +46 -0
  110. package/src/lexicon/lexicons.ts +412 -3
  111. package/src/lexicon/types/tools/ozone/moderation/defs.ts +130 -0
  112. package/src/lexicon/types/tools/ozone/moderation/emitEvent.ts +3 -0
  113. package/src/lexicon/types/tools/ozone/moderation/queryStatuses.ts +10 -0
  114. package/src/lexicon/types/tools/ozone/setting/defs.ts +37 -0
  115. package/src/lexicon/types/tools/ozone/setting/listOptions.ts +53 -0
  116. package/src/lexicon/types/tools/ozone/setting/removeOptions.ts +49 -0
  117. package/src/lexicon/types/tools/ozone/setting/upsertOption.ts +58 -0
  118. package/src/mod-service/index.ts +52 -0
  119. package/src/mod-service/status.ts +114 -2
  120. package/src/mod-service/types.ts +25 -0
  121. package/src/mod-service/views.ts +45 -2
  122. package/src/setting/service.ts +148 -0
  123. package/tests/__snapshots__/get-record.test.ts.snap +8 -0
  124. package/tests/__snapshots__/get-records.test.ts.snap +4 -0
  125. package/tests/__snapshots__/get-repo.test.ts.snap +4 -0
  126. package/tests/__snapshots__/get-repos.test.ts.snap +4 -0
  127. package/tests/__snapshots__/moderation-events.test.ts.snap +4 -0
  128. package/tests/__snapshots__/moderation-statuses.test.ts.snap +24 -0
  129. package/tests/__snapshots__/settings.test.ts.snap +52 -0
  130. package/tests/record-and-account-events.test.ts +185 -0
  131. package/tests/settings.test.ts +310 -0
  132. package/tsconfig.build.tsbuildinfo +1 -1
  133. package/tsconfig.tests.tsbuildinfo +1 -1
@@ -1,6 +1,10 @@
1
1
  import { sql } from 'kysely'
2
2
  import { AtUri, INVALID_HANDLE, normalizeDatetimeAlways } from '@atproto/syntax'
3
- import { AtpAgent, AppBskyFeedDefs } from '@atproto/api'
3
+ import {
4
+ AtpAgent,
5
+ AppBskyFeedDefs,
6
+ ToolsOzoneModerationDefs,
7
+ } from '@atproto/api'
4
8
  import { dedupeStrs } from '@atproto/common'
5
9
  import { BlobRef } from '@atproto/lexicon'
6
10
  import { Keypair } from '@atproto/crypto'
@@ -195,6 +199,25 @@ export class ModerationViews {
195
199
  eventView.event.remove = event.removedTags || []
196
200
  }
197
201
 
202
+ if (event.action === 'tools.ozone.moderation.defs#accountEvent') {
203
+ eventView.event.active = !!event.meta?.active
204
+ eventView.event.timestamp = event.meta?.timestamp
205
+ eventView.event.status = event.meta?.status
206
+ }
207
+
208
+ if (event.action === 'tools.ozone.moderation.defs#identityEvent') {
209
+ eventView.event.timestamp = event.meta?.timestamp
210
+ eventView.event.handle = event.meta?.handle
211
+ eventView.event.pdsHost = event.meta?.pdsHost
212
+ eventView.event.tombstone = !!event.meta?.tombstone
213
+ }
214
+
215
+ if (event.action === 'tools.ozone.moderation.defs#recordEvent') {
216
+ eventView.event.op = event.meta?.op
217
+ eventView.event.cid = event.meta?.cid
218
+ eventView.event.timestamp = event.meta?.timestamp
219
+ }
220
+
198
221
  return eventView
199
222
  }
200
223
 
@@ -574,7 +597,7 @@ export class ModerationViews {
574
597
  formatSubjectStatus(
575
598
  status: ModerationSubjectStatusRowWithHandle,
576
599
  ): SubjectStatusView {
577
- return {
600
+ const statusView: SubjectStatusView = {
578
601
  id: status.id,
579
602
  reviewState: status.reviewState,
580
603
  createdAt: status.createdAt,
@@ -594,6 +617,26 @@ export class ModerationViews {
594
617
  tags: status.tags || [],
595
618
  subject: subjectFromStatusRow(status).lex(),
596
619
  }
620
+
621
+ if (status.recordPath !== '') {
622
+ statusView.hosting = {
623
+ $type: 'tools.ozone.moderation.defs#recordHosting',
624
+ updatedAt: status.hostingUpdatedAt ?? undefined,
625
+ deletedAt: status.hostingDeletedAt ?? undefined,
626
+ status: status.hostingStatus ?? 'unknown',
627
+ }
628
+ } else {
629
+ statusView.hosting = {
630
+ $type: 'tools.ozone.moderation.defs#accountHosting',
631
+ updatedAt: status.hostingUpdatedAt ?? undefined,
632
+ deletedAt: status.hostingDeletedAt ?? undefined,
633
+ status: status.hostingStatus ?? 'unknown',
634
+ deactivatedAt: status.hostingDeactivatedAt ?? undefined,
635
+ reactivatedAt: status.hostingReactivatedAt ?? undefined,
636
+ }
637
+ }
638
+
639
+ return statusView
597
640
  }
598
641
 
599
642
  async fetchAuthorFeed(
@@ -0,0 +1,148 @@
1
+ import Database from '../db'
2
+ import { Selectable } from 'kysely'
3
+ import { Option } from '../lexicon/types/tools/ozone/setting/defs'
4
+ import { Setting, SettingScope } from '../db/schema/setting'
5
+ import { Member } from '../db/schema/member'
6
+ import assert from 'node:assert'
7
+ import { InvalidRequestError } from '@atproto/xrpc-server'
8
+
9
+ export type SettingServiceCreator = (db: Database) => SettingService
10
+
11
+ export class SettingService {
12
+ constructor(public db: Database) {}
13
+
14
+ static creator() {
15
+ return (db: Database) => new SettingService(db)
16
+ }
17
+
18
+ async query({
19
+ limit = 100,
20
+ scope,
21
+ did,
22
+ cursor,
23
+ prefix,
24
+ keys,
25
+ }: {
26
+ limit: number
27
+ scope?: 'personal' | 'instance'
28
+ did?: string
29
+ cursor?: string
30
+ prefix?: string
31
+ keys?: string[]
32
+ }): Promise<{
33
+ options: Selectable<Setting>[]
34
+ cursor?: string
35
+ }> {
36
+ let builder = this.db.db.selectFrom('setting').selectAll()
37
+
38
+ if (prefix) {
39
+ builder = builder.where('key', 'like', `${prefix}%`)
40
+ } else if (keys?.length) {
41
+ builder = builder.where('key', 'in', keys)
42
+ }
43
+
44
+ if (scope) {
45
+ builder = builder.where('scope', '=', scope)
46
+ }
47
+
48
+ if (did) {
49
+ builder = builder.where('did', '=', did)
50
+ }
51
+
52
+ if (cursor) {
53
+ const cursorId = parseInt(cursor, 10)
54
+ if (isNaN(cursorId)) {
55
+ throw new InvalidRequestError('invalid cursor')
56
+ }
57
+ builder = builder.where('id', '<', cursorId)
58
+ }
59
+
60
+ const options = await builder.orderBy('id', 'desc').limit(limit).execute()
61
+
62
+ return {
63
+ options,
64
+ cursor: options[options.length - 1]?.id.toString(),
65
+ }
66
+ }
67
+
68
+ async upsert(
69
+ option: Omit<Setting, 'id' | 'createdAt' | 'updatedAt'> & {
70
+ createdAt: Date
71
+ updatedAt: Date
72
+ },
73
+ ): Promise<void> {
74
+ await this.db.db
75
+ .insertInto('setting')
76
+ .values(option)
77
+ .onConflict((oc) => {
78
+ return oc.columns(['key', 'scope', 'did']).doUpdateSet({
79
+ value: option.value,
80
+ updatedAt: option.updatedAt,
81
+ description: option.description,
82
+ managerRole: option.managerRole,
83
+ lastUpdatedBy: option.lastUpdatedBy,
84
+ })
85
+ })
86
+ .execute()
87
+ }
88
+
89
+ async removeOptions(
90
+ keys: string[],
91
+ filters: {
92
+ did?: string
93
+ scope: SettingScope
94
+ managerRole: Member['role'][]
95
+ },
96
+ ): Promise<void> {
97
+ if (!keys.length) return
98
+
99
+ if (filters.scope === 'personal') {
100
+ assert(filters.did, 'did is required for personal scope')
101
+ }
102
+
103
+ let qb = this.db.db
104
+ .deleteFrom('setting')
105
+ .where('key', 'in', keys)
106
+ .where('scope', '=', filters.scope)
107
+
108
+ if (filters.managerRole.length) {
109
+ qb = qb.where('managerRole', 'in', filters.managerRole)
110
+ } else {
111
+ qb = qb.where('managerRole', 'is', null)
112
+ }
113
+
114
+ if (filters.did) {
115
+ qb = qb.where('did', '=', filters.did)
116
+ }
117
+
118
+ await qb.execute()
119
+ }
120
+
121
+ view(setting: Selectable<Setting>): Option {
122
+ const {
123
+ key,
124
+ value,
125
+ did,
126
+ description,
127
+ createdAt,
128
+ createdBy,
129
+ updatedAt,
130
+ lastUpdatedBy,
131
+ managerRole,
132
+ scope,
133
+ } = setting
134
+
135
+ return {
136
+ key,
137
+ value,
138
+ did,
139
+ scope,
140
+ createdBy,
141
+ lastUpdatedBy,
142
+ managerRole: managerRole || undefined,
143
+ description: description || undefined,
144
+ createdAt: createdAt.toISOString(),
145
+ updatedAt: updatedAt.toISOString(),
146
+ }
147
+ }
148
+ }
@@ -29,6 +29,10 @@ Object {
29
29
  "moderation": Object {
30
30
  "subjectStatus": Object {
31
31
  "createdAt": "1970-01-01T00:00:00.000Z",
32
+ "hosting": Object {
33
+ "$type": "tools.ozone.moderation.defs#recordHosting",
34
+ "status": "unknown",
35
+ },
32
36
  "id": 1,
33
37
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
34
38
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
@@ -129,6 +133,10 @@ Object {
129
133
  "moderation": Object {
130
134
  "subjectStatus": Object {
131
135
  "createdAt": "1970-01-01T00:00:00.000Z",
136
+ "hosting": Object {
137
+ "$type": "tools.ozone.moderation.defs#recordHosting",
138
+ "status": "unknown",
139
+ },
132
140
  "id": 1,
133
141
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
134
142
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
@@ -32,6 +32,10 @@ Object {
32
32
  "moderation": Object {
33
33
  "subjectStatus": Object {
34
34
  "createdAt": "1970-01-01T00:00:00.000Z",
35
+ "hosting": Object {
36
+ "$type": "tools.ozone.moderation.defs#recordHosting",
37
+ "status": "unknown",
38
+ },
35
39
  "id": 1,
36
40
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
37
41
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
@@ -23,6 +23,10 @@ Object {
23
23
  "moderation": Object {
24
24
  "subjectStatus": Object {
25
25
  "createdAt": "1970-01-01T00:00:00.000Z",
26
+ "hosting": Object {
27
+ "$type": "tools.ozone.moderation.defs#accountHosting",
28
+ "status": "unknown",
29
+ },
26
30
  "id": 1,
27
31
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
28
32
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
@@ -26,6 +26,10 @@ Object {
26
26
  "moderation": Object {
27
27
  "subjectStatus": Object {
28
28
  "createdAt": "1970-01-01T00:00:00.000Z",
29
+ "hosting": Object {
30
+ "$type": "tools.ozone.moderation.defs#accountHosting",
31
+ "status": "unknown",
32
+ },
29
33
  "id": 1,
30
34
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
31
35
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
@@ -19,6 +19,10 @@ Object {
19
19
  "moderation": Object {
20
20
  "subjectStatus": Object {
21
21
  "createdAt": "1970-01-01T00:00:00.000Z",
22
+ "hosting": Object {
23
+ "$type": "tools.ozone.moderation.defs#accountHosting",
24
+ "status": "unknown",
25
+ },
22
26
  "id": 1,
23
27
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
24
28
  "lastReviewedAt": "1970-01-01T00:00:00.000Z",
@@ -4,6 +4,10 @@ exports[`moderation-statuses query statuses returns statuses filtered by subject
4
4
  Array [
5
5
  Object {
6
6
  "createdAt": "1970-01-01T00:00:00.000Z",
7
+ "hosting": Object {
8
+ "$type": "tools.ozone.moderation.defs#recordHosting",
9
+ "status": "unknown",
10
+ },
7
11
  "id": 7,
8
12
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
9
13
  "reviewState": "tools.ozone.moderation.defs#reviewOpen",
@@ -23,6 +27,10 @@ Array [
23
27
  },
24
28
  Object {
25
29
  "createdAt": "1970-01-01T00:00:00.000Z",
30
+ "hosting": Object {
31
+ "$type": "tools.ozone.moderation.defs#accountHosting",
32
+ "status": "unknown",
33
+ },
26
34
  "id": 5,
27
35
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
28
36
  "reviewState": "tools.ozone.moderation.defs#reviewOpen",
@@ -46,6 +54,10 @@ exports[`moderation-statuses query statuses returns statuses for subjects that r
46
54
  Array [
47
55
  Object {
48
56
  "createdAt": "1970-01-01T00:00:00.000Z",
57
+ "hosting": Object {
58
+ "$type": "tools.ozone.moderation.defs#recordHosting",
59
+ "status": "unknown",
60
+ },
49
61
  "id": 7,
50
62
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
51
63
  "reviewState": "tools.ozone.moderation.defs#reviewOpen",
@@ -65,6 +77,10 @@ Array [
65
77
  },
66
78
  Object {
67
79
  "createdAt": "1970-01-01T00:00:00.000Z",
80
+ "hosting": Object {
81
+ "$type": "tools.ozone.moderation.defs#accountHosting",
82
+ "status": "unknown",
83
+ },
68
84
  "id": 5,
69
85
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
70
86
  "reviewState": "tools.ozone.moderation.defs#reviewOpen",
@@ -83,6 +99,10 @@ Array [
83
99
  },
84
100
  Object {
85
101
  "createdAt": "1970-01-01T00:00:00.000Z",
102
+ "hosting": Object {
103
+ "$type": "tools.ozone.moderation.defs#recordHosting",
104
+ "status": "unknown",
105
+ },
86
106
  "id": 3,
87
107
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
88
108
  "reviewState": "tools.ozone.moderation.defs#reviewOpen",
@@ -101,6 +121,10 @@ Array [
101
121
  },
102
122
  Object {
103
123
  "createdAt": "1970-01-01T00:00:00.000Z",
124
+ "hosting": Object {
125
+ "$type": "tools.ozone.moderation.defs#accountHosting",
126
+ "status": "unknown",
127
+ },
104
128
  "id": 1,
105
129
  "lastReportedAt": "1970-01-01T00:00:00.000Z",
106
130
  "reviewState": "tools.ozone.moderation.defs#reviewOpen",
@@ -0,0 +1,52 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ozone-settings listOptions returns all personal settings 1`] = `
4
+ Array [
5
+ Object {
6
+ "createdAt": "1970-01-01T00:00:00.000Z",
7
+ "createdBy": "user(1)",
8
+ "description": "List of external labelers that will be plugged into the client views",
9
+ "did": "user(1)",
10
+ "key": "tools.ozone.setting.client.externalLabelers",
11
+ "lastUpdatedBy": "user(1)",
12
+ "managerRole": "tools.ozone.team.defs#roleAdmin",
13
+ "scope": "instance",
14
+ "updatedAt": "1970-01-01T00:00:00.000Z",
15
+ "value": Object {
16
+ "dids": Array [
17
+ "user(0)",
18
+ ],
19
+ },
20
+ },
21
+ Object {
22
+ "createdAt": "1970-01-01T00:00:00.000Z",
23
+ "createdBy": "user(1)",
24
+ "description": "This determines how each queue is balanced when sorted by oldest first",
25
+ "did": "user(1)",
26
+ "key": "tools.ozone.setting.client.queueHash",
27
+ "lastUpdatedBy": "user(1)",
28
+ "managerRole": "tools.ozone.team.defs#roleAdmin",
29
+ "scope": "instance",
30
+ "updatedAt": "1970-01-01T00:00:00.000Z",
31
+ "value": Object {
32
+ "val": 10.5,
33
+ },
34
+ },
35
+ Object {
36
+ "createdAt": "1970-01-01T00:00:00.000Z",
37
+ "createdBy": "user(1)",
38
+ "description": "This determines how many queues the client interface will show",
39
+ "did": "user(1)",
40
+ "key": "tools.ozone.setting.client.queues",
41
+ "lastUpdatedBy": "user(1)",
42
+ "managerRole": "tools.ozone.team.defs#roleAdmin",
43
+ "scope": "instance",
44
+ "updatedAt": "1970-01-01T00:00:00.000Z",
45
+ "value": Object {
46
+ "stratosphere": Object {
47
+ "name": "Stratosphere",
48
+ },
49
+ },
50
+ },
51
+ ]
52
+ `;
@@ -0,0 +1,185 @@
1
+ import {
2
+ TestNetwork,
3
+ SeedClient,
4
+ basicSeed,
5
+ ModeratorClient,
6
+ } from '@atproto/dev-env'
7
+ import {
8
+ ComAtprotoModerationDefs,
9
+ ToolsOzoneModerationDefs,
10
+ } from '@atproto/api'
11
+ import { REVIEWOPEN } from '../src/lexicon/types/tools/ozone/moderation/defs'
12
+ import { ToolsOzoneModerationEmitEvent as EmitModerationEvent } from '@atproto/api'
13
+ describe('record and account events on moderation subjects', () => {
14
+ let network: TestNetwork
15
+ let sc: SeedClient
16
+ let modClient: ModeratorClient
17
+
18
+ beforeAll(async () => {
19
+ network = await TestNetwork.create({
20
+ dbPostgresSchema: 'ozone_record_and_account_events',
21
+ })
22
+ sc = network.getSeedClient()
23
+ modClient = network.ozone.getModClient()
24
+ await basicSeed(sc)
25
+ await network.processAll()
26
+ })
27
+
28
+ afterAll(async () => {
29
+ await network.close()
30
+ })
31
+
32
+ const getSubjectStatus = async (
33
+ subject: string,
34
+ ): Promise<ToolsOzoneModerationDefs.SubjectStatusView | undefined> => {
35
+ const res = await modClient.queryStatuses({
36
+ subject,
37
+ })
38
+ return res.subjectStatuses[0]
39
+ }
40
+
41
+ describe('record events', () => {
42
+ const emitRecordEvent = async (
43
+ subject: EmitModerationEvent.InputSchema['subject'],
44
+ op: 'create' | 'update' | 'delete',
45
+ ) => {
46
+ return await modClient.emitEvent(
47
+ {
48
+ event: {
49
+ op,
50
+ timestamp: new Date().toISOString(),
51
+ $type: 'tools.ozone.moderation.defs#recordEvent',
52
+ },
53
+ subject,
54
+ createdBy: 'did:example:admin',
55
+ },
56
+ 'admin',
57
+ )
58
+ }
59
+
60
+ it('saves updated and deleted timestamps and record status', async () => {
61
+ const bobsPostSubject = {
62
+ $type: 'com.atproto.repo.strongRef',
63
+ uri: sc.posts[sc.dids.bob][1].ref.uriStr,
64
+ cid: sc.posts[sc.dids.bob][1].ref.cidStr,
65
+ }
66
+
67
+ await sc.createReport({
68
+ reportedBy: sc.dids.carol,
69
+ reasonType: ComAtprotoModerationDefs.REASONMISLEADING,
70
+ reason: 'misleading',
71
+ subject: bobsPostSubject,
72
+ })
73
+
74
+ await emitRecordEvent(bobsPostSubject, 'update')
75
+ const statusAfterUpdate = await getSubjectStatus(bobsPostSubject.uri)
76
+ expect(statusAfterUpdate?.hosting?.updatedAt).toBeTruthy()
77
+
78
+ await emitRecordEvent(bobsPostSubject, 'delete')
79
+ const statusAfterDelete = await getSubjectStatus(bobsPostSubject.uri)
80
+ expect(statusAfterDelete?.hosting?.deletedAt).toBeTruthy()
81
+ expect(statusAfterDelete?.hosting?.status).toEqual('deleted')
82
+ // Ensure that due to delete or update event, review state does not change
83
+ expect(statusAfterDelete?.reviewState).toEqual(REVIEWOPEN)
84
+ })
85
+ })
86
+ describe('account/identity events', () => {
87
+ const emitAccountEvent = async (
88
+ subject: EmitModerationEvent.InputSchema['subject'],
89
+ active: boolean,
90
+ status?: 'takendown' | 'deleted' | 'deactivated' | 'suspended',
91
+ ) => {
92
+ return await modClient.emitEvent(
93
+ {
94
+ event: {
95
+ status,
96
+ active,
97
+ timestamp: new Date().toISOString(),
98
+ $type: 'tools.ozone.moderation.defs#accountEvent',
99
+ },
100
+ subject,
101
+ createdBy: 'did:example:admin',
102
+ },
103
+ 'admin',
104
+ )
105
+ }
106
+
107
+ it('saves updated and deleted timestamps and account status', async () => {
108
+ const carolsAccountSubject = {
109
+ $type: 'com.atproto.admin.defs#repoRef',
110
+ did: sc.dids.carol,
111
+ }
112
+
113
+ await sc.createReport({
114
+ reportedBy: sc.dids.carol,
115
+ reasonType: ComAtprotoModerationDefs.REASONMISLEADING,
116
+ reason: 'misleading',
117
+ subject: carolsAccountSubject,
118
+ })
119
+
120
+ await emitAccountEvent(carolsAccountSubject, false, 'deactivated')
121
+ const statusAfterDeactivation = await getSubjectStatus(
122
+ carolsAccountSubject.did,
123
+ )
124
+ expect(statusAfterDeactivation?.hosting?.deactivatedAt).toBeTruthy()
125
+ expect(statusAfterDeactivation?.hosting?.status).toEqual('deactivated')
126
+ expect(statusAfterDeactivation?.reviewState).toEqual(REVIEWOPEN)
127
+
128
+ await emitAccountEvent(carolsAccountSubject, true)
129
+ const statusAfterReactivation = await getSubjectStatus(
130
+ carolsAccountSubject.did,
131
+ )
132
+ expect(statusAfterReactivation?.hosting?.updatedAt).toBeTruthy()
133
+ expect(statusAfterReactivation?.hosting?.status).toEqual('active')
134
+ expect(statusAfterReactivation?.hosting?.deletedAt).toBeFalsy()
135
+ })
136
+
137
+ it('gets statuses by hosting properties', async () => {
138
+ await Promise.all([
139
+ emitAccountEvent(
140
+ {
141
+ $type: 'com.atproto.admin.defs#repoRef',
142
+ did: sc.dids.carol,
143
+ },
144
+ false,
145
+ 'deactivated',
146
+ ),
147
+ emitAccountEvent(
148
+ {
149
+ $type: 'com.atproto.admin.defs#repoRef',
150
+ did: sc.dids.bob,
151
+ },
152
+ false,
153
+ 'deleted',
154
+ ),
155
+ ])
156
+ const [
157
+ { subjectStatuses: deactivatedOrDeletedStatuses },
158
+ { subjectStatuses: deletedStatusesInPastDay },
159
+ { subjectStatuses: deletedStatusesBeforeYesterday },
160
+ ] = await Promise.all([
161
+ modClient.queryStatuses({
162
+ hostingStatuses: ['deactivated', 'deleted'],
163
+ }),
164
+ modClient.queryStatuses({
165
+ hostingDeletedAfter: new Date(
166
+ Date.now() - 1000 * 60 * 60 * 24,
167
+ ).toISOString(),
168
+ }),
169
+ modClient.queryStatuses({
170
+ hostingDeletedBefore: new Date(
171
+ Date.now() - 1000 * 60 * 60 * 24,
172
+ ).toISOString(),
173
+ }),
174
+ ])
175
+
176
+ expect(deactivatedOrDeletedStatuses.length).toEqual(3)
177
+ expect(deletedStatusesInPastDay.length).toEqual(2)
178
+ expect(deletedStatusesInPastDay[0]?.subject.uri).toEqual(
179
+ sc.posts[sc.dids.bob][1].ref.uriStr,
180
+ )
181
+ expect(deletedStatusesInPastDay[1]?.subject.did).toEqual(sc.dids.bob)
182
+ expect(deletedStatusesBeforeYesterday.length).toEqual(0)
183
+ })
184
+ })
185
+ })