0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

Fixed reconstruction of Activity from JSONLD

The use of Article and Actor in Activity meant that we got way more data in the
JSONLD representation, but it wasn't be picked up when reconstructing from data
over the wire. This makes sure that we can recreate the object from the JSONLD.
This commit is contained in:
Fabien O'Carroll 2024-05-16 16:30:01 +07:00 committed by Fabien 'egg' O'Carroll
parent 17fe2395bd
commit a70afcd117
7 changed files with 80 additions and 22 deletions

View file

@ -1,6 +1,9 @@
import assert from 'assert'; import assert from 'assert';
import {Activity} from './activity.entity'; import {Activity} from './activity.entity';
import {URI} from './uri.object'; import {URI} from './uri.object';
import {Article} from './article.object';
import ObjectID from 'bson-objectid';
import {Actor} from './actor.entity';
describe('Activity', function () { describe('Activity', function () {
describe('fromJSONLD', function () { describe('fromJSONLD', function () {
@ -51,5 +54,36 @@ describe('Activity', function () {
}); });
} }
}); });
it('Can correctly reconstruct', function () {
const actor = Actor.create({username: 'testing'});
const article = Article.fromPost({
id: new ObjectID(),
title: 'My Title',
slug: 'my-title',
html: '<p> big boi contents </p>',
excerpt: 'lil contents',
authors: ['Jeremy Paxman'],
url: new URI('blah'),
publishedAt: new Date(),
featuredImage: null,
visibility: 'public'
});
const activity = new Activity({
type: 'Create',
activity: null,
actor: actor,
object: article,
to: new URI('bloo')
});
const baseUrl = new URL('https://ghost.org');
const input = activity.getJSONLD(baseUrl);
const created = Activity.fromJSONLD(input);
assert.deepEqual(created.getJSONLD(baseUrl), input);
});
}); });
}); });

View file

@ -7,7 +7,12 @@ import {URI} from './uri.object';
type ActivityData = { type ActivityData = {
activity: URI | null; activity: URI | null;
type: ActivityPub.ActivityType; type: ActivityPub.ActivityType;
actor: Actor | URI; actor: {
id: URI;
type: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[x: string]: any;
} | Actor;
object: { object: {
id: URI; id: URI;
type: string; type: string;
@ -58,18 +63,14 @@ export class Activity extends Entity<ActivityData> {
if (this.attr.actor instanceof Actor) { if (this.attr.actor instanceof Actor) {
return this.attr.actor.getJSONLD(url); return this.attr.actor.getJSONLD(url);
} }
return { return this.attr.actor;
type: 'Person',
id: this.attr.actor.getValue(url),
preferredUsername: 'index'
};
} }
get actorId() { get actorId() {
if (this.attr.actor instanceof Actor) { if (this.attr.actor instanceof Actor) {
return this.attr.actor.actorId; return this.attr.actor.actorId;
} }
return this.attr.actor; return this.attr.actor.id;
} }
get objectId() { get objectId() {
@ -90,7 +91,10 @@ export class Activity extends Entity<ActivityData> {
'@context': 'https://www.w3.org/ns/activitystreams', '@context': 'https://www.w3.org/ns/activitystreams',
id: this.activityId?.getValue(url) || null, id: this.activityId?.getValue(url) || null,
type: this.attr.type, type: this.attr.type,
actor: actor, actor: {
...actor,
id: this.actorId.getValue(url)
},
object: { object: {
...object, ...object,
id: this.objectId.getValue(url) id: this.objectId.getValue(url)
@ -105,12 +109,17 @@ export class Activity extends Entity<ActivityData> {
throw new Error(`Unknown type ${parsed.type}`); throw new Error(`Unknown type ${parsed.type}`);
} }
return new Activity({ return new Activity({
activity: 'id' in json ? getURI(json.id) : null, activity: 'id' in json && typeof json.id === 'string' ? getURI(json.id) : null,
type: parsed.type as ActivityPub.ActivityType, type: parsed.type as ActivityPub.ActivityType,
actor: getURI(parsed.actor), actor: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...(parsed.actor as any),
id: getURI(parsed.actor)
},
object: { object: {
id: getURI(parsed.object), // eslint-disable-next-line @typescript-eslint/no-explicit-any
type: 'Unknown' ...(parsed.object as any),
id: getURI(parsed.object)
}, },
to: 'to' in json ? getURI(json.to) : null to: 'to' in json ? getURI(json.to) : null
}); });

View file

@ -144,7 +144,10 @@ describe('Actor', function () {
const followActivity = new Activity({ const followActivity = new Activity({
activity: new URI(`https://activitypub.server/activity`), activity: new URI(`https://activitypub.server/activity`),
type: 'Follow', type: 'Follow',
actor: newFollower, actor: {
id: newFollower,
type: 'Person'
},
object: { object: {
id: actor.actorId, id: actor.actorId,
type: 'Person' type: 'Person'
@ -165,7 +168,10 @@ describe('Actor', function () {
const followActivity = new Activity({ const followActivity = new Activity({
activity: null, activity: null,
type: 'Follow', type: 'Follow',
actor: newFollower, actor: {
id: newFollower,
type: 'Person'
},
object: { object: {
id: actor.actorId, id: actor.actorId,
type: 'Person' type: 'Person'
@ -191,7 +197,10 @@ describe('Actor', function () {
const activity = new Activity({ const activity = new Activity({
activity: null, activity: null,
type: 'Accept', type: 'Accept',
actor: newFollower, actor: {
id: newFollower,
type: 'Person'
},
object: { object: {
id: newFollower, id: newFollower,
type: 'Person' type: 'Person'

View file

@ -109,7 +109,7 @@ export class Actor extends Entity<ActorData> {
const activity = new Activity({ const activity = new Activity({
activity: new URI(`activity/${(new ObjectID).toHexString()}`), activity: new URI(`activity/${(new ObjectID).toHexString()}`),
type: 'Follow', type: 'Follow',
actor: this.actorId, actor: this,
object: { object: {
...actor, ...actor,
type: 'Person' type: 'Person'
@ -128,7 +128,7 @@ export class Actor extends Entity<ActorData> {
activity: new URI(`activity/${(new ObjectID).toHexString()}`), activity: new URI(`activity/${(new ObjectID).toHexString()}`),
type: 'Accept', type: 'Accept',
to: activity.actorId, to: activity.actorId,
actor: this.actorId, actor: this,
object: { object: {
id: activity.activityId, id: activity.activityId,
type: 'Follow' type: 'Follow'

View file

@ -30,7 +30,10 @@ describe('InboxService', function () {
type: 'Application', type: 'Application',
id: new URI('https://whatever.com') id: new URI('https://whatever.com')
}, },
actor: new URI('https://blak.com'), actor: {
type: 'Person',
id: new URI('https://blak.com')
},
to: new URI('https://whatever.com') to: new URI('https://whatever.com')
}); });
@ -63,7 +66,10 @@ describe('InboxService', function () {
type: 'Person', type: 'Person',
id: new URI('https://whatever.com') id: new URI('https://whatever.com')
}, },
actor: new URI('https://blak.com'), actor: {
type: 'Person',
id: new URI('https://blak.com')
},
to: new URI('https://whatever.com') to: new URI('https://whatever.com')
}); });

View file

@ -25,7 +25,7 @@ describe('TheWorld', function () {
const activity = new Activity({ const activity = new Activity({
type: 'Follow', type: 'Follow',
activity: null, activity: null,
actor: actor.actorId, actor: actor,
object: { object: {
type: 'Person', type: 'Person',
id: toFollow id: toFollow
@ -61,7 +61,7 @@ describe('TheWorld', function () {
const activity = new Activity({ const activity = new Activity({
type: 'Create', type: 'Create',
activity: null, activity: null,
actor: actor.actorId, actor: actor,
object: { object: {
id: new URI('https://main.ghost.org/hello-world'), id: new URI('https://main.ghost.org/hello-world'),
type: 'Note', type: 'Note',

View file

@ -28,7 +28,7 @@ describe('ActivityListener', function () {
const activity = new Activity({ const activity = new Activity({
type: 'Follow', type: 'Follow',
activity: null, activity: null,
actor: actor.actorId, actor: actor,
object: { object: {
type: 'Person', type: 'Person',
id: toFollow id: toFollow