@atproto/ozone 0.1.46 → 0.1.47

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 (71) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +4 -0
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/moderation/getRecord.d.ts.map +1 -1
  6. package/dist/api/moderation/getRecord.js +5 -4
  7. package/dist/api/moderation/getRecord.js.map +1 -1
  8. package/dist/api/moderation/getRecords.d.ts +4 -0
  9. package/dist/api/moderation/getRecords.d.ts.map +1 -0
  10. package/dist/api/moderation/getRecords.js +37 -0
  11. package/dist/api/moderation/getRecords.js.map +1 -0
  12. package/dist/api/moderation/getRepo.d.ts.map +1 -1
  13. package/dist/api/moderation/getRepo.js +5 -4
  14. package/dist/api/moderation/getRepo.js.map +1 -1
  15. package/dist/api/moderation/getRepos.d.ts +4 -0
  16. package/dist/api/moderation/getRepos.d.ts.map +1 -0
  17. package/dist/api/moderation/getRepos.js +36 -0
  18. package/dist/api/moderation/getRepos.js.map +1 -0
  19. package/dist/api/util.d.ts +1 -1
  20. package/dist/api/util.d.ts.map +1 -1
  21. package/dist/api/util.js +13 -9
  22. package/dist/api/util.js.map +1 -1
  23. package/dist/db/migrations/20241001T205730722Z-subject-status-review-state-index.d.ts +4 -0
  24. package/dist/db/migrations/20241001T205730722Z-subject-status-review-state-index.d.ts.map +1 -0
  25. package/dist/db/migrations/20241001T205730722Z-subject-status-review-state-index.js +18 -0
  26. package/dist/db/migrations/20241001T205730722Z-subject-status-review-state-index.js.map +1 -0
  27. package/dist/db/migrations/index.d.ts +1 -0
  28. package/dist/db/migrations/index.d.ts.map +1 -1
  29. package/dist/db/migrations/index.js +2 -1
  30. package/dist/db/migrations/index.js.map +1 -1
  31. package/dist/lexicon/index.d.ts +4 -0
  32. package/dist/lexicon/index.d.ts.map +1 -1
  33. package/dist/lexicon/index.js +8 -0
  34. package/dist/lexicon/index.js.map +1 -1
  35. package/dist/lexicon/lexicons.d.ts +85 -0
  36. package/dist/lexicon/lexicons.d.ts.map +1 -1
  37. package/dist/lexicon/lexicons.js +93 -0
  38. package/dist/lexicon/lexicons.js.map +1 -1
  39. package/dist/lexicon/types/com/atproto/repo/getRecord.d.ts +1 -0
  40. package/dist/lexicon/types/com/atproto/repo/getRecord.d.ts.map +1 -1
  41. package/dist/lexicon/types/tools/ozone/moderation/getRecords.d.ts +39 -0
  42. package/dist/lexicon/types/tools/ozone/moderation/getRecords.d.ts.map +1 -0
  43. package/dist/lexicon/types/tools/ozone/moderation/getRecords.js +3 -0
  44. package/dist/lexicon/types/tools/ozone/moderation/getRecords.js.map +1 -0
  45. package/dist/lexicon/types/tools/ozone/moderation/getRepos.d.ts +39 -0
  46. package/dist/lexicon/types/tools/ozone/moderation/getRepos.d.ts.map +1 -0
  47. package/dist/lexicon/types/tools/ozone/moderation/getRepos.js +3 -0
  48. package/dist/lexicon/types/tools/ozone/moderation/getRepos.js.map +1 -0
  49. package/dist/mod-service/views.d.ts +5 -5
  50. package/dist/mod-service/views.d.ts.map +1 -1
  51. package/dist/mod-service/views.js +75 -48
  52. package/dist/mod-service/views.js.map +1 -1
  53. package/package.json +5 -5
  54. package/src/api/index.ts +4 -0
  55. package/src/api/moderation/getRecord.ts +7 -5
  56. package/src/api/moderation/getRecords.ts +50 -0
  57. package/src/api/moderation/getRepo.ts +7 -5
  58. package/src/api/moderation/getRepos.ts +41 -0
  59. package/src/api/util.ts +16 -9
  60. package/src/db/migrations/20241001T205730722Z-subject-status-review-state-index.ts +15 -0
  61. package/src/db/migrations/index.ts +1 -0
  62. package/src/lexicon/index.ts +24 -0
  63. package/src/lexicon/lexicons.ts +93 -0
  64. package/src/lexicon/types/com/atproto/repo/getRecord.ts +1 -0
  65. package/src/lexicon/types/tools/ozone/moderation/getRecords.ts +50 -0
  66. package/src/lexicon/types/tools/ozone/moderation/getRepos.ts +50 -0
  67. package/src/mod-service/views.ts +93 -52
  68. package/tests/__snapshots__/get-records.test.ts.snap +153 -0
  69. package/tests/__snapshots__/get-repos.test.ts.snap +108 -0
  70. package/tests/get-records.test.ts +87 -0
  71. package/tests/get-repos.test.ts +87 -0
@@ -0,0 +1,41 @@
1
+ import { Server } from '../../lexicon'
2
+ import AppContext from '../../context'
3
+ import { addAccountInfoToRepoViewDetail, getPdsAccountInfos } from '../util'
4
+
5
+ export default function (server: Server, ctx: AppContext) {
6
+ server.tools.ozone.moderation.getRepos({
7
+ auth: ctx.authVerifier.modOrAdminToken,
8
+ handler: async ({ params, auth, req }) => {
9
+ const { dids } = params
10
+ const db = ctx.db
11
+ const labelers = ctx.reqLabelers(req)
12
+ const [partialRepos, accountInfo] = await Promise.all([
13
+ ctx.modService(db).views.repoDetails(dids, labelers),
14
+ getPdsAccountInfos(ctx, dids),
15
+ ])
16
+
17
+ const repos = dids.map((did) => {
18
+ const partialRepo = partialRepos.get(did)
19
+ if (!partialRepo) {
20
+ return {
21
+ did,
22
+ $type: 'tools.ozone.moderation.defs#repoViewNotFound',
23
+ }
24
+ }
25
+ return {
26
+ $type: 'tools.ozone.moderation.defs#repoViewDetail',
27
+ ...addAccountInfoToRepoViewDetail(
28
+ partialRepo,
29
+ accountInfo.get(did) || null,
30
+ auth.credentials.isModerator,
31
+ ),
32
+ }
33
+ })
34
+
35
+ return {
36
+ encoding: 'application/json',
37
+ body: { repos },
38
+ }
39
+ },
40
+ })
41
+ }
package/src/api/util.ts CHANGED
@@ -28,19 +28,26 @@ import {
28
28
  } from '../lexicon/types/tools/ozone/team/defs'
29
29
  import { ids } from '../lexicon/lexicons'
30
30
 
31
- export const getPdsAccountInfo = async (
31
+ export const getPdsAccountInfos = async (
32
32
  ctx: AppContext,
33
- did: string,
34
- ): Promise<AccountView | null> => {
33
+ dids: string[],
34
+ ): Promise<Map<string, AccountView | null>> => {
35
+ const results = new Map<string, AccountView | null>()
36
+
35
37
  const agent = ctx.pdsAgent
36
- if (!agent) return null
37
- const auth = await ctx.pdsAuth(ids.ComAtprotoAdminGetAccountInfo)
38
- if (!auth) return null
38
+ if (!agent) return results
39
+
40
+ const auth = await ctx.pdsAuth(ids.ComAtprotoAdminGetAccountInfos)
41
+ if (!auth) return results
42
+
39
43
  try {
40
- const res = await agent.api.com.atproto.admin.getAccountInfo({ did }, auth)
41
- return res.data
44
+ const res = await agent.com.atproto.admin.getAccountInfos({ dids }, auth)
45
+ res.data.infos.forEach((info) => {
46
+ results.set(info.did, info)
47
+ })
48
+ return results
42
49
  } catch {
43
- return null
50
+ return results
44
51
  }
45
52
  }
46
53
 
@@ -0,0 +1,15 @@
1
+ import { Kysely } from 'kysely'
2
+
3
+ export async function up(db: Kysely<unknown>): Promise<void> {
4
+ await db.schema
5
+ .createIndex('moderation_subject_status_review_state_idx')
6
+ .on('moderation_subject_status')
7
+ .column('reviewState')
8
+ .execute()
9
+ }
10
+
11
+ export async function down(db: Kysely<unknown>): Promise<void> {
12
+ await db.schema
13
+ .dropIndex('moderation_subject_status_review_state_idx')
14
+ .execute()
15
+ }
@@ -13,3 +13,4 @@ export * as _20240430T211332580Z from './20240521T211332580Z-member'
13
13
  export * as _20240814T003647759Z from './20240814T003647759Z-event-created-at-index'
14
14
  export * as _20240903T205730722Z from './20240903T205730722Z-add-template-lang'
15
15
  export * as _20240904T205730722Z from './20240904T205730722Z-add-subject-did-index'
16
+ export * as _20241001T205730722Z from './20241001T205730722Z-subject-status-review-state-index'
@@ -166,7 +166,9 @@ import * as ToolsOzoneCommunicationUpdateTemplate from './types/tools/ozone/comm
166
166
  import * as ToolsOzoneModerationEmitEvent from './types/tools/ozone/moderation/emitEvent'
167
167
  import * as ToolsOzoneModerationGetEvent from './types/tools/ozone/moderation/getEvent'
168
168
  import * as ToolsOzoneModerationGetRecord from './types/tools/ozone/moderation/getRecord'
169
+ import * as ToolsOzoneModerationGetRecords from './types/tools/ozone/moderation/getRecords'
169
170
  import * as ToolsOzoneModerationGetRepo from './types/tools/ozone/moderation/getRepo'
171
+ import * as ToolsOzoneModerationGetRepos from './types/tools/ozone/moderation/getRepos'
170
172
  import * as ToolsOzoneModerationQueryEvents from './types/tools/ozone/moderation/queryEvents'
171
173
  import * as ToolsOzoneModerationQueryStatuses from './types/tools/ozone/moderation/queryStatuses'
172
174
  import * as ToolsOzoneModerationSearchRepos from './types/tools/ozone/moderation/searchRepos'
@@ -2262,6 +2264,17 @@ export class ToolsOzoneModerationNS {
2262
2264
  return this._server.xrpc.method(nsid, cfg)
2263
2265
  }
2264
2266
 
2267
+ getRecords<AV extends AuthVerifier>(
2268
+ cfg: ConfigOf<
2269
+ AV,
2270
+ ToolsOzoneModerationGetRecords.Handler<ExtractAuth<AV>>,
2271
+ ToolsOzoneModerationGetRecords.HandlerReqCtx<ExtractAuth<AV>>
2272
+ >,
2273
+ ) {
2274
+ const nsid = 'tools.ozone.moderation.getRecords' // @ts-ignore
2275
+ return this._server.xrpc.method(nsid, cfg)
2276
+ }
2277
+
2265
2278
  getRepo<AV extends AuthVerifier>(
2266
2279
  cfg: ConfigOf<
2267
2280
  AV,
@@ -2273,6 +2286,17 @@ export class ToolsOzoneModerationNS {
2273
2286
  return this._server.xrpc.method(nsid, cfg)
2274
2287
  }
2275
2288
 
2289
+ getRepos<AV extends AuthVerifier>(
2290
+ cfg: ConfigOf<
2291
+ AV,
2292
+ ToolsOzoneModerationGetRepos.Handler<ExtractAuth<AV>>,
2293
+ ToolsOzoneModerationGetRepos.HandlerReqCtx<ExtractAuth<AV>>
2294
+ >,
2295
+ ) {
2296
+ const nsid = 'tools.ozone.moderation.getRepos' // @ts-ignore
2297
+ return this._server.xrpc.method(nsid, cfg)
2298
+ }
2299
+
2276
2300
  queryEvents<AV extends AuthVerifier>(
2277
2301
  cfg: ConfigOf<
2278
2302
  AV,
@@ -1686,6 +1686,11 @@ export const schemaDict = {
1686
1686
  },
1687
1687
  },
1688
1688
  },
1689
+ errors: [
1690
+ {
1691
+ name: 'RecordNotFound',
1692
+ },
1693
+ ],
1689
1694
  },
1690
1695
  },
1691
1696
  },
@@ -11643,6 +11648,49 @@ export const schemaDict = {
11643
11648
  },
11644
11649
  },
11645
11650
  },
11651
+ ToolsOzoneModerationGetRecords: {
11652
+ lexicon: 1,
11653
+ id: 'tools.ozone.moderation.getRecords',
11654
+ defs: {
11655
+ main: {
11656
+ type: 'query',
11657
+ description: 'Get details about some records.',
11658
+ parameters: {
11659
+ type: 'params',
11660
+ required: ['uris'],
11661
+ properties: {
11662
+ uris: {
11663
+ type: 'array',
11664
+ maxLength: 100,
11665
+ items: {
11666
+ type: 'string',
11667
+ format: 'at-uri',
11668
+ },
11669
+ },
11670
+ },
11671
+ },
11672
+ output: {
11673
+ encoding: 'application/json',
11674
+ schema: {
11675
+ type: 'object',
11676
+ required: ['records'],
11677
+ properties: {
11678
+ records: {
11679
+ type: 'array',
11680
+ items: {
11681
+ type: 'union',
11682
+ refs: [
11683
+ 'lex:tools.ozone.moderation.defs#recordViewDetail',
11684
+ 'lex:tools.ozone.moderation.defs#recordViewNotFound',
11685
+ ],
11686
+ },
11687
+ },
11688
+ },
11689
+ },
11690
+ },
11691
+ },
11692
+ },
11693
+ },
11646
11694
  ToolsOzoneModerationGetRepo: {
11647
11695
  lexicon: 1,
11648
11696
  id: 'tools.ozone.moderation.getRepo',
@@ -11675,6 +11723,49 @@ export const schemaDict = {
11675
11723
  },
11676
11724
  },
11677
11725
  },
11726
+ ToolsOzoneModerationGetRepos: {
11727
+ lexicon: 1,
11728
+ id: 'tools.ozone.moderation.getRepos',
11729
+ defs: {
11730
+ main: {
11731
+ type: 'query',
11732
+ description: 'Get details about some repositories.',
11733
+ parameters: {
11734
+ type: 'params',
11735
+ required: ['dids'],
11736
+ properties: {
11737
+ dids: {
11738
+ type: 'array',
11739
+ maxLength: 100,
11740
+ items: {
11741
+ type: 'string',
11742
+ format: 'did',
11743
+ },
11744
+ },
11745
+ },
11746
+ },
11747
+ output: {
11748
+ encoding: 'application/json',
11749
+ schema: {
11750
+ type: 'object',
11751
+ required: ['repos'],
11752
+ properties: {
11753
+ repos: {
11754
+ type: 'array',
11755
+ items: {
11756
+ type: 'union',
11757
+ refs: [
11758
+ 'lex:tools.ozone.moderation.defs#repoViewDetail',
11759
+ 'lex:tools.ozone.moderation.defs#repoViewNotFound',
11760
+ ],
11761
+ },
11762
+ },
11763
+ },
11764
+ },
11765
+ },
11766
+ },
11767
+ },
11768
+ },
11678
11769
  ToolsOzoneModerationQueryEvents: {
11679
11770
  lexicon: 1,
11680
11771
  id: 'tools.ozone.moderation.queryEvents',
@@ -12491,7 +12582,9 @@ export const ids = {
12491
12582
  ToolsOzoneModerationEmitEvent: 'tools.ozone.moderation.emitEvent',
12492
12583
  ToolsOzoneModerationGetEvent: 'tools.ozone.moderation.getEvent',
12493
12584
  ToolsOzoneModerationGetRecord: 'tools.ozone.moderation.getRecord',
12585
+ ToolsOzoneModerationGetRecords: 'tools.ozone.moderation.getRecords',
12494
12586
  ToolsOzoneModerationGetRepo: 'tools.ozone.moderation.getRepo',
12587
+ ToolsOzoneModerationGetRepos: 'tools.ozone.moderation.getRepos',
12495
12588
  ToolsOzoneModerationQueryEvents: 'tools.ozone.moderation.queryEvents',
12496
12589
  ToolsOzoneModerationQueryStatuses: 'tools.ozone.moderation.queryStatuses',
12497
12590
  ToolsOzoneModerationSearchRepos: 'tools.ozone.moderation.searchRepos',
@@ -39,6 +39,7 @@ export interface HandlerSuccess {
39
39
  export interface HandlerError {
40
40
  status: number
41
41
  message?: string
42
+ error?: 'RecordNotFound'
42
43
  }
43
44
 
44
45
  export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
@@ -0,0 +1,50 @@
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, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+ import * as ToolsOzoneModerationDefs from './defs'
11
+
12
+ export interface QueryParams {
13
+ uris: string[]
14
+ }
15
+
16
+ export type InputSchema = undefined
17
+
18
+ export interface OutputSchema {
19
+ records: (
20
+ | ToolsOzoneModerationDefs.RecordViewDetail
21
+ | ToolsOzoneModerationDefs.RecordViewNotFound
22
+ | { $type: string; [k: string]: unknown }
23
+ )[]
24
+ [k: string]: unknown
25
+ }
26
+
27
+ export type HandlerInput = undefined
28
+
29
+ export interface HandlerSuccess {
30
+ encoding: 'application/json'
31
+ body: OutputSchema
32
+ headers?: { [key: string]: string }
33
+ }
34
+
35
+ export interface HandlerError {
36
+ status: number
37
+ message?: string
38
+ }
39
+
40
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
41
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
42
+ auth: HA
43
+ params: QueryParams
44
+ input: HandlerInput
45
+ req: express.Request
46
+ res: express.Response
47
+ }
48
+ export type Handler<HA extends HandlerAuth = never> = (
49
+ ctx: HandlerReqCtx<HA>,
50
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -0,0 +1,50 @@
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, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+ import * as ToolsOzoneModerationDefs from './defs'
11
+
12
+ export interface QueryParams {
13
+ dids: string[]
14
+ }
15
+
16
+ export type InputSchema = undefined
17
+
18
+ export interface OutputSchema {
19
+ repos: (
20
+ | ToolsOzoneModerationDefs.RepoViewDetail
21
+ | ToolsOzoneModerationDefs.RepoViewNotFound
22
+ | { $type: string; [k: string]: unknown }
23
+ )[]
24
+ [k: string]: unknown
25
+ }
26
+
27
+ export type HandlerInput = undefined
28
+
29
+ export interface HandlerSuccess {
30
+ encoding: 'application/json'
31
+ body: OutputSchema
32
+ headers?: { [key: string]: string }
33
+ }
34
+
35
+ export interface HandlerError {
36
+ status: number
37
+ message?: string
38
+ }
39
+
40
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
41
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
42
+ auth: HA
43
+ params: QueryParams
44
+ input: HandlerInput
45
+ req: express.Request
46
+ res: express.Response
47
+ }
48
+ export type Handler<HA extends HandlerAuth = never> = (
49
+ ctx: HandlerReqCtx<HA>,
50
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -223,25 +223,33 @@ export class ModerationViews {
223
223
  }
224
224
  }
225
225
 
226
- async repoDetail(
227
- did: string,
226
+ async repoDetails(
227
+ dids: string[],
228
228
  labelers?: ParsedLabelers,
229
- ): Promise<RepoViewDetail | undefined> {
229
+ ): Promise<Map<string, RepoView>> {
230
+ const results = new Map<string, RepoView>()
230
231
  const [repos, localLabels, externalLabels] = await Promise.all([
231
- this.repos([did]),
232
- this.labels(did),
233
- this.getExternalLabels([did], labelers),
232
+ this.repos(dids),
233
+ this.labels(dids),
234
+ this.getExternalLabels(dids, labelers),
234
235
  ])
235
- const repo = repos.get(did)
236
- if (!repo) return
237
236
 
238
- return {
239
- ...repo,
240
- moderation: {
241
- ...repo.moderation,
242
- },
243
- labels: [...localLabels, ...externalLabels],
244
- }
237
+ repos.forEach((repo, did) => {
238
+ const labels = [
239
+ ...(localLabels.get(did) || []),
240
+ ...(externalLabels.get(did) || []),
241
+ ]
242
+ const repoView = {
243
+ ...repo,
244
+ labels,
245
+ moderation: {
246
+ ...repo.moderation,
247
+ },
248
+ }
249
+ results.set(did, repoView)
250
+ })
251
+
252
+ return results
245
253
  }
246
254
 
247
255
  async fetchRecords(
@@ -308,47 +316,59 @@ export class ModerationViews {
308
316
  }, new Map<string, RecordView>())
309
317
  }
310
318
 
311
- async recordDetail(
312
- subject: RecordSubject,
319
+ async recordDetails(
320
+ subjects: RecordSubject[],
313
321
  labelers?: ParsedLabelers,
314
- ): Promise<RecordViewDetail | undefined> {
315
- const [records, subjectStatusesResult] = await Promise.all([
316
- this.records([subject]),
317
- this.getSubjectStatus([subject.uri]),
318
- ])
319
- const record = records.get(subject.uri)
320
- if (!record) return undefined
322
+ ): Promise<Map<string, RecordViewDetail>> {
323
+ const subjectUris = subjects.map((s) => s.uri)
324
+ const [records, subjectStatusesResult, localLabels, externalLabels] =
325
+ await Promise.all([
326
+ this.records(subjects),
327
+ this.getSubjectStatus(subjectUris),
328
+ this.labels(subjectUris),
329
+ this.getExternalLabels(subjectUris, labelers),
330
+ ])
331
+
332
+ const results = new Map<string, RecordViewDetail>()
333
+
334
+ await Promise.all(
335
+ Array.from(records.entries()).map(async ([uri, record]) => {
336
+ const selfLabels = getSelfLabels({
337
+ uri: record.uri,
338
+ cid: record.cid,
339
+ record: record.value,
340
+ })
321
341
 
322
- const status = subjectStatusesResult.get(subject.uri)
342
+ const status = subjectStatusesResult.get(uri)
343
+ const blobs = await this.blob(findBlobRefs(record.value))
323
344
 
324
- const [blobs, labels, externalLabels, subjectStatus] = await Promise.all([
325
- this.blob(findBlobRefs(record.value)),
326
- this.labels(record.uri),
327
- this.getExternalLabels([record.uri], labelers),
328
- status ? this.formatSubjectStatus(status) : Promise.resolve(undefined),
329
- ])
330
- const selfLabels = getSelfLabels({
331
- uri: record.uri,
332
- cid: record.cid,
333
- record: record.value,
334
- })
345
+ results.set(uri, {
346
+ ...record,
347
+ blobs,
348
+ moderation: {
349
+ ...record.moderation,
350
+ subjectStatus: status
351
+ ? this.formatSubjectStatus(status)
352
+ : undefined,
353
+ },
354
+ labels: [
355
+ ...(localLabels.get(uri) || []),
356
+ ...selfLabels,
357
+ ...(externalLabels.get(uri) || []),
358
+ ],
359
+ })
360
+ }),
361
+ )
335
362
 
336
- return {
337
- ...record,
338
- blobs,
339
- moderation: {
340
- ...record.moderation,
341
- subjectStatus,
342
- },
343
- labels: [...labels, ...selfLabels, ...externalLabels],
344
- }
363
+ return results
345
364
  }
346
365
 
347
366
  async getExternalLabels(
348
367
  subjects: string[],
349
368
  labelers?: ParsedLabelers,
350
- ): Promise<Label[]> {
351
- if (!labelers?.dids.length && !labelers?.redact.size) return []
369
+ ): Promise<Map<string, Label[]>> {
370
+ const results = new Map<string, Label[]>()
371
+ if (!labelers?.dids.length && !labelers?.redact.size) return results
352
372
  try {
353
373
  const {
354
374
  data: { labels },
@@ -356,13 +376,20 @@ export class ModerationViews {
356
376
  uriPatterns: subjects,
357
377
  sources: labelers.dids,
358
378
  })
359
- return labels
379
+ labels.forEach((label) => {
380
+ if (!results.has(label.uri)) {
381
+ results.set(label.uri, [label])
382
+ return
383
+ }
384
+ results.get(label.uri)?.push(label)
385
+ })
386
+ return results
360
387
  } catch (err) {
361
388
  httpLogger.error(
362
389
  { err, subjects, labelers },
363
390
  'failed to resolve labels from appview',
364
391
  )
365
- return []
392
+ return results
366
393
  }
367
394
  }
368
395
 
@@ -452,14 +479,28 @@ export class ModerationViews {
452
479
  })
453
480
  }
454
481
 
455
- async labels(subject: string, includeNeg?: boolean): Promise<Label[]> {
482
+ async labels(
483
+ subjects: string[],
484
+ includeNeg?: boolean,
485
+ ): Promise<Map<string, Label[]>> {
486
+ const labels = new Map<string, Label[]>()
456
487
  const res = await this.db.db
457
488
  .selectFrom('label')
458
- .where('label.uri', '=', subject)
489
+ .where('label.uri', 'in', subjects)
459
490
  .if(!includeNeg, (qb) => qb.where('neg', '=', false))
460
491
  .selectAll()
461
492
  .execute()
462
- return Promise.all(res.map((l) => this.formatLabelAndEnsureSig(l)))
493
+
494
+ await Promise.all(
495
+ res.map(async (labelRow) => {
496
+ const signedLabel = await this.formatLabelAndEnsureSig(labelRow)
497
+ if (!labels.has(labelRow.uri)) {
498
+ labels.set(labelRow.uri, [])
499
+ }
500
+ labels.get(labelRow.uri)?.push(signedLabel)
501
+ }),
502
+ )
503
+ return labels
463
504
  }
464
505
 
465
506
  async formatLabelAndEnsureSig(row: LabelRow) {