Fix retrieving of deleted comments when subscribing to a new instance

This commit is contained in:
Julien Maulny 2019-12-03 21:48:31 +01:00 committed by Chocobozzz
parent 69222afac8
commit b5206dfc45
5 changed files with 87 additions and 19 deletions

View File

@ -3,11 +3,28 @@ import { ACTIVITY_PUB } from '../../../initializers/constants'
import { exists, isArray, isDateValid } from '../misc' import { exists, isArray, isDateValid } from '../misc'
import { isActivityPubUrlValid } from './misc' import { isActivityPubUrlValid } from './misc'
function isTypeValid (comment: any): boolean {
if (comment.type === 'Note') return true
if (comment.type === 'Tombstone' && comment.formerType === 'Note') return true
return false
}
function sanitizeAndCheckVideoCommentObject (comment: any) { function sanitizeAndCheckVideoCommentObject (comment: any) {
if (!comment || comment.type !== 'Note') return false if (!comment) return false
if (!isTypeValid(comment)) return false
normalizeComment(comment) normalizeComment(comment)
if (comment.type === 'Tombstone') {
return isActivityPubUrlValid(comment.id) &&
isDateValid(comment.published) &&
isDateValid(comment.deleted) &&
isActivityPubUrlValid(comment.url)
}
return isActivityPubUrlValid(comment.id) && return isActivityPubUrlValid(comment.id) &&
isCommentContentValid(comment.content) && isCommentContentValid(comment.content) &&
isActivityPubUrlValid(comment.inReplyTo) && isActivityPubUrlValid(comment.inReplyTo) &&

View File

@ -131,9 +131,9 @@ async function resolveParentComment (params: ResolveThreadParams) {
} }
const actorUrl = body.attributedTo const actorUrl = body.attributedTo
if (!actorUrl) throw new Error('Miss attributed to in comment') if (!actorUrl && body.type !== 'Tombstone') throw new Error('Miss attributed to in comment')
if (checkUrlsSameHost(url, actorUrl) !== true) { if (actorUrl && checkUrlsSameHost(url, actorUrl) !== true) {
throw new Error(`Actor url ${actorUrl} has not the same host than the comment url ${url}`) throw new Error(`Actor url ${actorUrl} has not the same host than the comment url ${url}`)
} }
@ -141,18 +141,19 @@ async function resolveParentComment (params: ResolveThreadParams) {
throw new Error(`Comment url ${url} host is different from the AP object id ${body.id}`) throw new Error(`Comment url ${url} host is different from the AP object id ${body.id}`)
} }
const actor = await getOrCreateActorAndServerAndModel(actorUrl, 'all') const actor = actorUrl ? await getOrCreateActorAndServerAndModel(actorUrl, 'all') : null
const comment = new VideoCommentModel({ const comment = new VideoCommentModel({
url: body.id, url: body.id,
text: body.content, text: body.content ? body.content : '',
videoId: null, videoId: null,
accountId: actor.Account.id, accountId: actor ? actor.Account.id : null,
inReplyToCommentId: null, inReplyToCommentId: null,
originCommentId: null, originCommentId: null,
createdAt: new Date(body.published), createdAt: new Date(body.published),
updatedAt: new Date(body.updated) updatedAt: new Date(body.updated),
deletedAt: body.deleted ? new Date(body.deleted) : null
}) as MCommentOwner }) as MCommentOwner
comment.Account = actor.Account comment.Account = actor ? actor.Account : null
return resolveThread({ return resolveThread({
url: body.inReplyTo, url: body.inReplyTo,

View File

@ -507,17 +507,6 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
} }
toActivityPubObject (this: MCommentAP, threadParentComments: MCommentOwner[]): VideoCommentObject | ActivityTombstoneObject { toActivityPubObject (this: MCommentAP, threadParentComments: MCommentOwner[]): VideoCommentObject | ActivityTombstoneObject {
if (this.isDeleted()) {
return {
id: this.url,
type: 'Tombstone',
formerType: 'Note',
published: this.createdAt.toISOString(),
updated: this.updatedAt.toISOString(),
deleted: this.deletedAt.toISOString()
}
}
let inReplyTo: string let inReplyTo: string
// New thread, so in AS we reply to the video // New thread, so in AS we reply to the video
if (this.inReplyToCommentId === null) { if (this.inReplyToCommentId === null) {
@ -526,8 +515,22 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
inReplyTo = this.InReplyToVideoComment.url inReplyTo = this.InReplyToVideoComment.url
} }
if (this.isDeleted()) {
return {
id: this.url,
type: 'Tombstone',
formerType: 'Note',
inReplyTo,
published: this.createdAt.toISOString(),
updated: this.updatedAt.toISOString(),
deleted: this.deletedAt.toISOString()
}
}
const tag: ActivityTagObject[] = [] const tag: ActivityTagObject[] = []
for (const parentComment of threadParentComments) { for (const parentComment of threadParentComments) {
if (!parentComment.Account) continue
const actor = parentComment.Account.Actor const actor = parentComment.Account.Actor
tag.push({ tag.push({

View File

@ -15,6 +15,7 @@ import {
createUser, createUser,
dateIsValid, dateIsValid,
doubleFollow, doubleFollow,
flushAndRunServer,
flushAndRunMultipleServers, flushAndRunMultipleServers,
getLocalVideos, getLocalVideos,
getVideo, getVideo,
@ -938,7 +939,51 @@ describe('Test multiple servers', function () {
} }
}) })
it('Should retrieve all comments when subscribing to a new server', async function () {
this.timeout(120000)
const newServer = await flushAndRunServer(4)
await setAccessTokensToServers([newServer])
await doubleFollow(newServer, servers[0])
await doubleFollow(newServer, servers[2])
await waitJobs([newServer, ...servers])
const res = await getVideoCommentThreads(newServer.url, videoUUID, 0, 5)
expect(res.body.total).to.equal(2)
expect(res.body.data).to.be.an('array')
expect(res.body.data).to.have.lengthOf(2)
{
const comment: VideoComment = res.body.data[0]
expect(comment).to.not.be.undefined
expect(comment.inReplyToCommentId).to.be.null
expect(comment.account.name).to.equal('root')
expect(comment.account.host).to.equal('localhost:' + servers[2].port)
expect(comment.totalReplies).to.equal(0)
expect(dateIsValid(comment.createdAt as string)).to.be.true
expect(dateIsValid(comment.updatedAt as string)).to.be.true
}
{
const deletedComment: VideoComment = res.body.data[1]
expect(deletedComment).to.not.be.undefined
expect(deletedComment.isDeleted).to.be.true
expect(deletedComment.deletedAt).to.not.be.null
expect(deletedComment.text).to.equal('')
expect(deletedComment.inReplyToCommentId).to.be.null
expect(deletedComment.account).to.be.null
expect(deletedComment.totalReplies).to.equal(3)
expect(dateIsValid(deletedComment.createdAt as string)).to.be.true
expect(dateIsValid(deletedComment.updatedAt as string)).to.be.true
expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true
}
})
it('Should delete a remote thread by the origin server', async function () { it('Should delete a remote thread by the origin server', async function () {
this.timeout(5000)
const res = await getVideoCommentThreads(servers[ 0 ].url, videoUUID, 0, 5) const res = await getVideoCommentThreads(servers[ 0 ].url, videoUUID, 0, 5)
const threadId = res.body.data.find(c => c.text === 'my super second comment').id const threadId = res.body.data.find(c => c.text === 'my super second comment').id
await deleteVideoComment(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID, threadId) await deleteVideoComment(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID, threadId)

View File

@ -93,9 +93,11 @@ export interface ActivityPubAttributedTo {
export interface ActivityTombstoneObject { export interface ActivityTombstoneObject {
'@context'?: any '@context'?: any
id: string id: string
url?: string
type: 'Tombstone' type: 'Tombstone'
name?: string name?: string
formerType?: string formerType?: string
inReplyTo?: string
published: string published: string
updated: string updated: string
deleted: string deleted: string