Barazo AppView backend barazo.forum

fix(topics): enrich author profile in by-author-rkey endpoints (#140) (#140)

The by-author-rkey endpoints for both topics and replies returned raw
authorDid without resolving the author profile, regressing the fix from
#138 which only covered by-rkey and by-uri. The frontend switched to
by-author-rkey in #177, causing DID display instead of profile info.

authored by

Guido X Jansen and committed by
GitHub
92bd5230 b8502ee8

+103 -2
+13 -1
src/routes/replies.ts
··· 741 741 throw notFound('Reply not found') 742 742 } 743 743 744 - return reply.status(200).send(serializeReply(row)) 744 + const serialized = serializeReply(row) 745 + const communityDid = requireCommunityDid(request) 746 + const authorMap = await resolveAuthors([row.authorDid], communityDid, db) 747 + 748 + return reply.status(200).send({ 749 + ...serialized, 750 + author: authorMap.get(row.authorDid) ?? { 751 + did: row.authorDid, 752 + handle: row.authorDid, 753 + displayName: null, 754 + avatarUrl: null, 755 + }, 756 + }) 745 757 } 746 758 ) 747 759
+12 -1
src/routes/topics.ts
··· 921 921 throw forbidden('Content restricted by maturity settings') 922 922 } 923 923 924 - return reply.status(200).send(serializeTopic(row, categoryRating)) 924 + const serialized = serializeTopic(row, categoryRating) 925 + const authorMap = await resolveAuthors([row.authorDid], communityDid, db) 926 + 927 + return reply.status(200).send({ 928 + ...serialized, 929 + author: authorMap.get(row.authorDid) ?? { 930 + did: row.authorDid, 931 + handle: row.authorDid, 932 + displayName: null, 933 + avatarUrl: null, 934 + }, 935 + }) 925 936 } 926 937 ) 927 938
+36
tests/unit/routes/replies.test.ts
··· 2617 2617 expect(response.statusCode).toBe(404) 2618 2618 }) 2619 2619 2620 + it('enriches author profile in by-author-rkey response', async () => { 2621 + resolveHandleToDidFn.mockResolvedValueOnce(TEST_DID) 2622 + const row = sampleReplyRow() 2623 + // 1. find reply by authorDid + rkey 2624 + selectChain.where.mockResolvedValueOnce([row]) 2625 + // 2. resolveAuthors: users table 2626 + selectChain.where.mockResolvedValueOnce([ 2627 + { 2628 + did: TEST_DID, 2629 + handle: TEST_HANDLE, 2630 + displayName: 'Jay', 2631 + avatarUrl: 'https://cdn.example.com/jay.jpg', 2632 + bannerUrl: null, 2633 + bio: null, 2634 + }, 2635 + ]) 2636 + // 3. resolveAuthors: community profiles 2637 + selectChain.where.mockResolvedValueOnce([]) 2638 + 2639 + const response = await app.inject({ 2640 + method: 'GET', 2641 + url: `/api/replies/by-author-rkey/${TEST_HANDLE}/${TEST_REPLY_RKEY}`, 2642 + }) 2643 + 2644 + expect(response.statusCode).toBe(200) 2645 + const body = response.json<{ 2646 + author: { did: string; handle: string; displayName: string; avatarUrl: string } 2647 + }>() 2648 + expect(body.author).toEqual({ 2649 + did: TEST_DID, 2650 + handle: TEST_HANDLE, 2651 + displayName: 'Jay', 2652 + avatarUrl: 'https://cdn.example.com/jay.jpg', 2653 + }) 2654 + }) 2655 + 2620 2656 it('returns 404 when reply not found for author', async () => { 2621 2657 resolveHandleToDidFn.mockResolvedValueOnce(TEST_DID) 2622 2658 selectChain.where.mockResolvedValueOnce([])
+42
tests/unit/routes/topics.test.ts
··· 1280 1280 expect(response.statusCode).toBe(404) 1281 1281 }) 1282 1282 1283 + it('enriches author profile in by-author-rkey response', async () => { 1284 + resolveHandleToDidFn.mockResolvedValueOnce(TEST_DID) 1285 + const row = sampleTopicRow() 1286 + // 1. find topic by authorDid + rkey 1287 + selectChain.where.mockResolvedValueOnce([row]) 1288 + // 2. category maturity rating 1289 + selectChain.where.mockResolvedValueOnce([{ maturityRating: 'safe' }]) 1290 + // 3. user profile (maturity) 1291 + selectChain.where.mockResolvedValueOnce([{ declaredAge: null, maturityPref: 'safe' }]) 1292 + // 4. age threshold 1293 + selectChain.where.mockResolvedValueOnce([{ ageThreshold: 16 }]) 1294 + // 5. resolveAuthors: users table 1295 + selectChain.where.mockResolvedValueOnce([ 1296 + { 1297 + did: TEST_DID, 1298 + handle: TEST_HANDLE, 1299 + displayName: 'Jay', 1300 + avatarUrl: 'https://cdn.example.com/jay.jpg', 1301 + bannerUrl: null, 1302 + bio: null, 1303 + }, 1304 + ]) 1305 + // 6. resolveAuthors: community profiles 1306 + selectChain.where.mockResolvedValueOnce([]) 1307 + 1308 + const response = await app.inject({ 1309 + method: 'GET', 1310 + url: `/api/topics/by-author-rkey/${TEST_HANDLE}/${TEST_RKEY}`, 1311 + }) 1312 + 1313 + expect(response.statusCode).toBe(200) 1314 + const body = response.json<{ 1315 + author: { did: string; handle: string; displayName: string; avatarUrl: string } 1316 + }>() 1317 + expect(body.author).toEqual({ 1318 + did: TEST_DID, 1319 + handle: TEST_HANDLE, 1320 + displayName: 'Jay', 1321 + avatarUrl: 'https://cdn.example.com/jay.jpg', 1322 + }) 1323 + }) 1324 + 1283 1325 it('returns 403 when maturity blocks access', async () => { 1284 1326 const noAuthApp = await buildTestApp(undefined) 1285 1327