From 6321e7839f42b68cd548abcbd4e7b682fd21422b Mon Sep 17 00:00:00 2001
From: devin ivy <devinivy@gmail.com>
Date: Mon, 7 Aug 2023 17:20:26 -0400
Subject: [PATCH] Include pds account info on mod report and action details
 (#1441)

hydrate pds repo state onto mod report and action subject details
---
 .../com/atproto/admin/getModerationAction.ts  | 34 ++++++++---
 .../com/atproto/admin/getModerationReport.ts  | 33 +++++++++--
 .../proxied/__snapshots__/admin.test.ts.snap  | 56 +++++++++++++++++++
 3 files changed, 110 insertions(+), 13 deletions(-)

diff --git a/packages/pds/src/api/com/atproto/admin/getModerationAction.ts b/packages/pds/src/api/com/atproto/admin/getModerationAction.ts
index 60058169..251979cb 100644
--- a/packages/pds/src/api/com/atproto/admin/getModerationAction.ts
+++ b/packages/pds/src/api/com/atproto/admin/getModerationAction.ts
@@ -1,28 +1,48 @@
 import { Server } from '../../../../lexicon'
 import AppContext from '../../../../context'
-import { authPassthru } from './util'
+import { authPassthru, mergeRepoViewPdsDetails } from './util'
+import { isRepoView } from '@atproto/api/src/client/types/com/atproto/admin/defs'
 
 export default function (server: Server, ctx: AppContext) {
   server.com.atproto.admin.getModerationAction({
     auth: ctx.roleVerifier,
     handler: async ({ req, params, auth }) => {
+      const access = auth.credentials
+      const { db, services } = ctx
+      const accountService = services.account(db)
+      const moderationService = services.moderation(db)
+
       if (ctx.shouldProxyModeration()) {
-        // @TODO merge invite details into action subject
-        const { data: result } =
+        const { data: resultAppview } =
           await ctx.appviewAgent.com.atproto.admin.getModerationAction(
             params,
             authPassthru(req),
           )
+        // merge local repo state for subject if available
+        if (isRepoView(resultAppview.subject)) {
+          const account = await accountService.getAccount(
+            resultAppview.subject.did,
+            true,
+          )
+          const repo =
+            account &&
+            (await moderationService.views.repo(account, {
+              includeEmails: access.moderator,
+            }))
+          if (repo) {
+            resultAppview.subject = mergeRepoViewPdsDetails(
+              resultAppview.subject,
+              repo,
+            )
+          }
+        }
         return {
           encoding: 'application/json',
-          body: result,
+          body: resultAppview,
         }
       }
 
-      const access = auth.credentials
-      const { db, services } = ctx
       const { id } = params
-      const moderationService = services.moderation(db)
       const result = await moderationService.getActionOrThrow(id)
       return {
         encoding: 'application/json',
diff --git a/packages/pds/src/api/com/atproto/admin/getModerationReport.ts b/packages/pds/src/api/com/atproto/admin/getModerationReport.ts
index 0368a337..5af7381f 100644
--- a/packages/pds/src/api/com/atproto/admin/getModerationReport.ts
+++ b/packages/pds/src/api/com/atproto/admin/getModerationReport.ts
@@ -1,27 +1,48 @@
+import { isRepoView } from '@atproto/api/src/client/types/com/atproto/admin/defs'
 import { Server } from '../../../../lexicon'
 import AppContext from '../../../../context'
-import { authPassthru } from './util'
+import { authPassthru, mergeRepoViewPdsDetails } from './util'
 
 export default function (server: Server, ctx: AppContext) {
   server.com.atproto.admin.getModerationReport({
     auth: ctx.roleVerifier,
     handler: async ({ req, params, auth }) => {
+      const access = auth.credentials
+      const { db, services } = ctx
+      const accountService = services.account(db)
+      const moderationService = services.moderation(db)
+
       if (ctx.shouldProxyModeration()) {
-        const { data: result } =
+        const { data: resultAppview } =
           await ctx.appviewAgent.com.atproto.admin.getModerationReport(
             params,
             authPassthru(req),
           )
+        // merge local repo state for subject if available
+        if (isRepoView(resultAppview.subject)) {
+          const account = await accountService.getAccount(
+            resultAppview.subject.did,
+            true,
+          )
+          const repo =
+            account &&
+            (await moderationService.views.repo(account, {
+              includeEmails: access.moderator,
+            }))
+          if (repo) {
+            resultAppview.subject = mergeRepoViewPdsDetails(
+              resultAppview.subject,
+              repo,
+            )
+          }
+        }
         return {
           encoding: 'application/json',
-          body: result,
+          body: resultAppview,
         }
       }
 
-      const access = auth.credentials
-      const { db, services } = ctx
       const { id } = params
-      const moderationService = services.moderation(db)
       const result = await moderationService.getReportOrThrow(id)
       return {
         encoding: 'application/json',
diff --git a/packages/pds/tests/proxied/__snapshots__/admin.test.ts.snap b/packages/pds/tests/proxied/__snapshots__/admin.test.ts.snap
index ebdd162f..46a88622 100644
--- a/packages/pds/tests/proxied/__snapshots__/admin.test.ts.snap
+++ b/packages/pds/tests/proxied/__snapshots__/admin.test.ts.snap
@@ -122,8 +122,36 @@ Object {
   "subject": Object {
     "$type": "com.atproto.admin.defs#repoView",
     "did": "user(0)",
+    "email": "bob@test.com",
     "handle": "bob.test",
     "indexedAt": "1970-01-01T00:00:00.000Z",
+    "invitedBy": Object {
+      "available": 10,
+      "code": "invite-code",
+      "createdAt": "1970-01-01T00:00:00.000Z",
+      "createdBy": "admin",
+      "disabled": false,
+      "forAccount": "admin",
+      "uses": Array [
+        Object {
+          "usedAt": "1970-01-01T00:00:00.000Z",
+          "usedBy": "user(1)",
+        },
+        Object {
+          "usedAt": "1970-01-01T00:00:00.000Z",
+          "usedBy": "user(0)",
+        },
+        Object {
+          "usedAt": "1970-01-01T00:00:00.000Z",
+          "usedBy": "user(2)",
+        },
+        Object {
+          "usedAt": "1970-01-01T00:00:00.000Z",
+          "usedBy": "user(3)",
+        },
+      ],
+    },
+    "invitesDisabled": true,
     "moderation": Object {},
     "relatedRecords": Array [
       Object {
@@ -300,8 +328,36 @@ Object {
   "subject": Object {
     "$type": "com.atproto.admin.defs#repoView",
     "did": "user(1)",
+    "email": "bob@test.com",
     "handle": "bob.test",
     "indexedAt": "1970-01-01T00:00:00.000Z",
+    "invitedBy": Object {
+      "available": 10,
+      "code": "invite-code",
+      "createdAt": "1970-01-01T00:00:00.000Z",
+      "createdBy": "admin",
+      "disabled": false,
+      "forAccount": "admin",
+      "uses": Array [
+        Object {
+          "usedAt": "1970-01-01T00:00:00.000Z",
+          "usedBy": "user(0)",
+        },
+        Object {
+          "usedAt": "1970-01-01T00:00:00.000Z",
+          "usedBy": "user(1)",
+        },
+        Object {
+          "usedAt": "1970-01-01T00:00:00.000Z",
+          "usedBy": "user(2)",
+        },
+        Object {
+          "usedAt": "1970-01-01T00:00:00.000Z",
+          "usedBy": "user(3)",
+        },
+      ],
+    },
+    "invitesDisabled": true,
     "moderation": Object {
       "currentAction": Object {
         "action": "com.atproto.admin.defs#acknowledge",