mirror of
https://github.com/Permissionless-Software-Foundation/psf-memo-db.git
synced 2026-09-21 16:52:02 -07:00
Merge pull request #1 from Permissionless-Software-Foundation/add-post-thread-endpoint
feat: add Memo post thread endpoint
This commit is contained in:
@@ -90,6 +90,30 @@ class PostQuery {
|
||||
|
||||
return posts
|
||||
}
|
||||
|
||||
async buildReplyCountMap () {
|
||||
const counts = new Map()
|
||||
let total = 0
|
||||
|
||||
for await (const [childTxid, child] of this.postChildrenDb.iterator()) {
|
||||
total++
|
||||
|
||||
console.log('Indexed reply:', {
|
||||
childTxid,
|
||||
child,
|
||||
parentTxid: child?.parentTxid
|
||||
})
|
||||
|
||||
const parentTxid = child?.parentTxid
|
||||
if (!parentTxid) continue
|
||||
|
||||
counts.set(parentTxid, (counts.get(parentTxid) || 0) + 1)
|
||||
}
|
||||
|
||||
console.log(`Total postChildrenDb records: ${total}`)
|
||||
|
||||
return counts
|
||||
}
|
||||
}
|
||||
|
||||
export default PostQuery
|
||||
|
||||
@@ -17,6 +17,7 @@ class PostsRESTControllerLib {
|
||||
|
||||
this.getRecentPosts = this.getRecentPosts.bind(this)
|
||||
this.getPostsByAddr = this.getPostsByAddr.bind(this)
|
||||
this.getPostThread = this.getPostThread.bind(this)
|
||||
this.handleError = this.handleError.bind(this)
|
||||
}
|
||||
|
||||
@@ -98,6 +99,18 @@ class PostsRESTControllerLib {
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
async getPostThread (ctx) {
|
||||
try {
|
||||
const { txid } = ctx.params
|
||||
|
||||
ctx.body = await this.useCases.getPostThread.execute({
|
||||
txid
|
||||
})
|
||||
} catch (err) {
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PostsRESTControllerLib
|
||||
|
||||
@@ -26,6 +26,7 @@ class PostsRouter {
|
||||
attach (app) {
|
||||
this.router.get('/recent', this.postsRESTController.getRecentPosts)
|
||||
this.router.get('/by/:addr', this.postsRESTController.getPostsByAddr)
|
||||
this.router.get('/:txid/thread',this.postsRESTController.getPostThread)
|
||||
app.use(this.router.routes())
|
||||
app.use(this.router.allowedMethods())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
Retrieve a Memo post and its nested replies.
|
||||
*/
|
||||
|
||||
class GetPostThread {
|
||||
constructor (localConfig = {}) {
|
||||
this.adapters = localConfig.adapters
|
||||
|
||||
if (!this.adapters) {
|
||||
throw new Error(
|
||||
'Adapters required when instantiating GetPostThread.'
|
||||
)
|
||||
}
|
||||
|
||||
this.execute = this.execute.bind(this)
|
||||
this.buildThreadNode = this.buildThreadNode.bind(this)
|
||||
}
|
||||
|
||||
async execute ({ txid } = {}) {
|
||||
if (!txid || typeof txid !== 'string') {
|
||||
const err = new Error('A transaction ID is required.')
|
||||
err.status = 400
|
||||
throw err
|
||||
}
|
||||
|
||||
const rootPost = await this.buildThreadNode(txid)
|
||||
|
||||
if (!rootPost) {
|
||||
const err = new Error('Post not found.')
|
||||
err.status = 404
|
||||
throw err
|
||||
}
|
||||
|
||||
return {
|
||||
post: rootPost
|
||||
}
|
||||
}
|
||||
|
||||
async buildThreadNode (txid, visited = new Set()) {
|
||||
if (visited.has(txid)) return null
|
||||
|
||||
visited.add(txid)
|
||||
|
||||
let post
|
||||
|
||||
try {
|
||||
post = await this.adapters.postQuery.postsDb.get(txid)
|
||||
} catch (err) {
|
||||
if (err.notFound || err.code === 'LEVEL_NOT_FOUND') {
|
||||
return null
|
||||
}
|
||||
|
||||
throw err
|
||||
}
|
||||
|
||||
const childTxids = []
|
||||
|
||||
for await (
|
||||
const [, child]
|
||||
of this.adapters.postQuery.postChildrenDb.iterator()
|
||||
) {
|
||||
if (child?.parentTxid !== txid) continue
|
||||
if (!child?.childTxid) continue
|
||||
|
||||
childTxids.push(child.childTxid)
|
||||
}
|
||||
|
||||
const replies = []
|
||||
|
||||
for (const childTxid of childTxids) {
|
||||
const reply = await this.buildThreadNode(childTxid, visited)
|
||||
|
||||
if (reply) {
|
||||
replies.push(reply)
|
||||
}
|
||||
}
|
||||
|
||||
replies.sort((a, b) => {
|
||||
const blockDifference =
|
||||
(a.blockHeight ?? 0) - (b.blockHeight ?? 0)
|
||||
|
||||
if (blockDifference !== 0) {
|
||||
return blockDifference
|
||||
}
|
||||
|
||||
return (a.seen ?? 0) - (b.seen ?? 0)
|
||||
})
|
||||
|
||||
return {
|
||||
txid,
|
||||
addr: post.addr,
|
||||
text: post.text,
|
||||
seen: post.seen,
|
||||
blockHeight: post.blockHeight ?? 0,
|
||||
replyCount: replies.length,
|
||||
replies
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default GetPostThread
|
||||
+24
-4
@@ -5,26 +5,46 @@
|
||||
import ListRecentProfiles from './list-recent-profiles.js'
|
||||
import ListRecentPosts from './list-recent-posts.js'
|
||||
import ListPostsByAddr from './list-posts-by-addr.js'
|
||||
import GetPostThread from './get-post-thread.js'
|
||||
|
||||
class UseCases {
|
||||
constructor (localConfig = {}) {
|
||||
this.adapters = localConfig.adapters
|
||||
|
||||
if (!this.adapters) {
|
||||
throw new Error('Adapters required when instantiating UseCases.')
|
||||
throw new Error(
|
||||
'Adapters required when instantiating UseCases.'
|
||||
)
|
||||
}
|
||||
|
||||
this.listRecentProfiles = null
|
||||
this.listRecentPosts = null
|
||||
this.listPostsByAddr = null
|
||||
this.getPostThread = null
|
||||
}
|
||||
|
||||
async start () {
|
||||
this.listRecentProfiles = new ListRecentProfiles({ adapters: this.adapters })
|
||||
this.listRecentPosts = new ListRecentPosts({ adapters: this.adapters })
|
||||
this.listPostsByAddr = new ListPostsByAddr({ adapters: this.adapters })
|
||||
this.listRecentProfiles = new ListRecentProfiles({
|
||||
adapters: this.adapters
|
||||
})
|
||||
|
||||
this.listRecentPosts = new ListRecentPosts({
|
||||
adapters: this.adapters
|
||||
})
|
||||
|
||||
this.listPostsByAddr = new ListPostsByAddr({
|
||||
adapters: this.adapters
|
||||
})
|
||||
|
||||
this.getPostThread = new GetPostThread({
|
||||
adapters: this.adapters
|
||||
})
|
||||
|
||||
console.log('Use cases initialized.')
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export default UseCases
|
||||
|
||||
|
||||
Reference in New Issue
Block a user