refactor: remove announceObject in favour of feps.announce, added create activity mock to support

This commit is contained in:
Julian Lam
2025-03-11 12:27:26 -04:00
parent 866cd5398f
commit 74443c3b15
6 changed files with 84 additions and 59 deletions

View File

@@ -3,7 +3,6 @@
const nconf = require('nconf'); const nconf = require('nconf');
const posts = require('../posts'); const posts = require('../posts');
const utils = require('../utils');
const activitypub = module.parent.exports; const activitypub = module.parent.exports;
const Feps = module.exports; const Feps = module.exports;
@@ -53,36 +52,3 @@ Feps.announce = async function announce(id, activity) {
object: activity, object: activity,
}); });
}; };
Feps.announceObject = async function announceObject(id) {
let localId;
if (String(id).startsWith(nconf.get('url'))) {
({ id: localId } = await activitypub.helpers.resolveLocalId(id));
}
const cid = await posts.getCidByPid(localId || id);
if (cid === -1) {
return;
}
const followers = await activitypub.notes.getCategoryFollowers(cid);
if (!followers.length) {
return;
}
let author = await posts.getPostField(id, 'uid');
if (utils.isNumber(author)) {
author = `${nconf.get('url')}/uid/${author}`;
} else if (!author.startsWith(nconf.get('url'))) {
followers.unshift(author);
}
activitypub.helpers.log(`[activitypub/inbox.announce(1b12)] Announcing object (${id}) to followers of cid ${cid}`);
await activitypub.send('cid', cid, followers, {
id: `${nconf.get('url')}/post/${encodeURIComponent(id)}#activity/announce/${Date.now()}`,
type: 'Announce',
actor: `${nconf.get('url')}/category/${cid}`,
to: [`${nconf.get('url')}/category/${cid}/followers`],
cc: [author, activitypub._constants.publicAddress],
object: utils.isNumber(id) ? `${nconf.get('url')}/post/${id}` : id,
});
};

View File

@@ -754,6 +754,38 @@ Mocks.notes.private = async ({ messageObj }) => {
return object; return object;
}; };
Mocks.activities = {};
Mocks.activities.create = async (pid, uid, post) => {
// Local objects only, post optional
if (!utils.isNumber(pid)) {
throw new Error('[[error:invalid-pid]]');
}
if (!post) {
post = (await posts.getPostSummaryByPids([pid], uid, { stripTags: false })).pop();
if (!post) {
throw new Error('[[error:invalid-pid]]');
}
}
const object = await activitypub.mocks.notes.public(post);
const { to, cc, targets } = await activitypub.buildRecipients(object, { pid, uid: post.user.uid });
object.to = to;
object.cc = cc;
const activity = {
id: `${object.id}#activity/create/${Date.now()}`,
type: 'Create',
actor: object.attributedTo,
to,
cc,
object,
};
return { activity, targets };
};
Mocks.tombstone = async properties => ({ Mocks.tombstone = async properties => ({
'@context': 'https://www.w3.org/ns/activitystreams', '@context': 'https://www.w3.org/ns/activitystreams',
type: 'Tombstone', type: 'Tombstone',

View File

@@ -124,23 +124,11 @@ activitypubApi.create.note = enabledCheck(async (caller, { pid, post }) => {
return; return;
} }
const object = await activitypub.mocks.notes.public(post); const { payload: activity, targets } = await activitypub.mocks.activities.create(pid, caller.uid, post);
const { to, cc, targets } = await activitypub.buildRecipients(object, { pid, uid: post.user.uid });
object.to = to;
object.cc = cc;
const payload = {
id: `${object.id}#activity/create/${Date.now()}`,
type: 'Create',
actor: object.attributedTo,
to,
cc,
object,
};
await Promise.all([ await Promise.all([
activitypub.send('uid', caller.uid, Array.from(targets), payload), activitypub.send('uid', caller.uid, Array.from(targets), activity),
activitypub.feps.announce(pid, payload), activitypub.feps.announce(pid, activity),
activitypubApi.add(caller, { pid }), activitypubApi.add(caller, { pid }),
]); ]);
}); });

View File

@@ -342,7 +342,8 @@ topicsAPI.move = async (caller, { tid, cid }) => {
if (!topicData.deleted) { if (!topicData.deleted) {
socketHelpers.sendNotificationToTopicOwner(tid, caller.uid, 'move', 'notifications:moved-your-topic'); socketHelpers.sendNotificationToTopicOwner(tid, caller.uid, 'move', 'notifications:moved-your-topic');
activitypubApi.announce.note(caller, { tid }); activitypubApi.announce.note(caller, { tid });
activitypub.feps.announceObject(topicData.mainPid); const { activity } = await activitypub.mocks.activities.create(topicData.mainPid, caller.uid);
await activitypub.feps.announce(topicData.mainPid, activity);
} }
await events.log({ await events.log({

View File

@@ -88,9 +88,11 @@ module.exports = function (Topics) {
}), }),
db.sortedSetsAdd(['topics:votes', `cid:${cid}:tids:votes`], mainPost.votes, tid), db.sortedSetsAdd(['topics:votes', `cid:${cid}:tids:votes`], mainPost.votes, tid),
Topics.events.log(fromTid, { type: 'fork', uid, href: `/topic/${tid}` }), Topics.events.log(fromTid, { type: 'fork', uid, href: `/topic/${tid}` }),
activitypub.feps.announceObject(pids[0]),
]); ]);
const { activity } = await activitypub.mocks.activities.create(pids[0], uid);
await activitypub.feps.announce(pids[0], activity);
plugins.hooks.fire('action:topic.fork', { tid, fromTid, uid }); plugins.hooks.fire('action:topic.fork', { tid, fromTid, uid });
return await Topics.getTopicData(tid); return await Topics.getTopicData(tid);

View File

@@ -22,8 +22,8 @@ describe('FEPs', () => {
await install.giveWorldPrivileges(); await install.giveWorldPrivileges();
}); });
describe.only('1b12', () => { describe('1b12', () => {
describe('announceObject()', () => { describe('announce()', () => {
let cid; let cid;
let uid; let uid;
let adminUid; let adminUid;
@@ -49,7 +49,7 @@ describe('FEPs', () => {
}); });
it('should be called when a topic is moved from uncategorized to another category', async () => { it('should be called when a topic is moved from uncategorized to another category', async () => {
const { topicData } = await topics.post({ const { topicData, postData } = await topics.post({
uid, uid,
cid: -1, cid: -1,
title: utils.generateUUID(), title: utils.generateUUID(),
@@ -63,7 +63,13 @@ describe('FEPs', () => {
cid, cid,
}); });
assert.strictEqual(activitypub._sent.size, 1); assert.strictEqual(activitypub._sent.size, 2);
const key = Array.from(activitypub._sent.keys())[0];
const activity = activitypub._sent.get(key);
assert(activity && activity.object && typeof activity.object === 'object');
assert.strictEqual(activity.object.id, `${nconf.get('url')}/post/${postData.pid}`);
}); });
it('should be called for a newly forked topic', async () => { it('should be called for a newly forked topic', async () => {
@@ -78,17 +84,47 @@ describe('FEPs', () => {
topics.reply({ uid, tid, content: utils.generateUUID() }), topics.reply({ uid, tid, content: utils.generateUUID() }),
topics.reply({ uid, tid, content: utils.generateUUID() }), topics.reply({ uid, tid, content: utils.generateUUID() }),
]); ]);
const forked = await topics.createTopicFromPosts( await topics.createTopicFromPosts(
adminUid, utils.generateUUID(), [reply1Pid, reply2Pid], tid, cid adminUid, utils.generateUUID(), [reply1Pid, reply2Pid], tid, cid
); );
assert.strictEqual(activitypub._sent.size, 1); assert.strictEqual(activitypub._sent.size, 2);
const key = Array.from(activitypub._sent.keys())[0]; const key = Array.from(activitypub._sent.keys())[0];
const activity = activitypub._sent.get(key); const activity = activitypub._sent.get(key);
assert(activity); assert(activity && activity.object && typeof activity.object === 'object');
assert.strictEqual(activity.object, `${nconf.get('url')}/post/${reply1Pid}`); assert.strictEqual(activity.object.id, `${nconf.get('url')}/post/${reply1Pid}`);
});
// it('should be called when a post is moved to another topic', async () => {
//
// });
});
describe('announceObject()', () => {
let cid;
let uid;
let adminUid;
before(async () => {
const name = utils.generateUUID();
const description = utils.generateUUID();
({ cid } = await categories.create({ name, description }));
adminUid = await user.create({ username: utils.generateUUID() });
await groups.join('administrators', adminUid);
uid = await user.create({ username: utils.generateUUID() });
const { id: followerId, actor } = helpers.mocks.actor();
activitypub._cache.set(`0;${followerId}`, actor);
user.setCategoryWatchState(followerId, [cid], categories.watchStates.tracking);
activitypub._sent.clear();
});
afterEach(() => {
activitypub._sent.clear();
}); });
}); });
}); });