Skip to content

Commit

Permalink
Added profile URL to actor
Browse files Browse the repository at this point in the history
  • Loading branch information
mike182uk committed Aug 14, 2024
1 parent 62aef77 commit badb89f
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type PersonData = {
outbox: string;
following: string;
followers: string;
url: string;
};

export async function getUserData(ctx: RequestContext<ContextData>, handle: string) {
Expand All @@ -36,6 +37,14 @@ export async function getUserData(ctx: RequestContext<ContextData>, handle: stri
console.log('Could not create Image from Icon value', existing.icon);
console.log(err);
}

let url = null;
try {
url = new URL(existing.url);
} catch (err) {
console.log('Could not create url from value', existing.url);
console.log(err);
}
return {
id: new URL(existing.id),
name: existing.name,
Expand All @@ -49,6 +58,7 @@ export async function getUserData(ctx: RequestContext<ContextData>, handle: stri
publicKeys: (await ctx.getActorKeyPairs(handle)).map(
(key) => key.cryptographicKey,
),
url,
};
}

Expand All @@ -65,6 +75,7 @@ export async function getUserData(ctx: RequestContext<ContextData>, handle: stri
publicKeys: (await ctx.getActorKeyPairs(handle)).map(
(key) => key.cryptographicKey,
),
url: new URL(`https://${ctx.host}`),
};

const dataToStore: PersonData = {
Expand All @@ -77,6 +88,7 @@ export async function getUserData(ctx: RequestContext<ContextData>, handle: stri
outbox: data.outbox.href,
following: data.following.href,
followers: data.followers.href,
url: data.url.href,
};

await ctx.data.db.set(['handle', handle], dataToStore);
Expand Down
201 changes: 201 additions & 0 deletions src/user.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import { Image } from '@fedify/fedify';
import assert from 'assert';
import sinon from 'sinon';

import {
ACTOR_DEFAULT_ICON,
ACTOR_DEFAULT_NAME,
ACTOR_DEFAULT_SUMMARY
} from './constants';
import { getUserData } from './user';

const HANDLE = 'foo';
const ACTOR_URI = `https://example.com/${HANDLE}`;
const INBOX_URI = `https://example.com/${HANDLE}/inbox`;
const OUTBOX_URI = `https://example.com/${HANDLE}/outbox`;
const FOLLOWING_URI = `https://example.com/${HANDLE}/following`;
const FOLLOWERS_URI = `https://example.com/${HANDLE}/followers`;

function getCtx() {
const host = 'example.com';

const ctx = {
data: {
db: {
get: sinon.stub(),
set: sinon.stub(),
},
},
getActorKeyPairs: sinon.stub(),
getActorUri: sinon.stub(),
getInboxUri: sinon.stub(),
getOutboxUri: sinon.stub(),
getFollowingUri: sinon.stub(),
getFollowersUri: sinon.stub(),
host,
};

ctx.getActorKeyPairs.withArgs(HANDLE).resolves([
{ cryptographicKey: 'abc123' }
]);

ctx.getActorUri.withArgs(HANDLE).returns(new URL(ACTOR_URI));
ctx.getInboxUri.withArgs(HANDLE).returns(new URL(INBOX_URI));
ctx.getOutboxUri.withArgs(HANDLE).returns(new URL(OUTBOX_URI));
ctx.getFollowingUri.withArgs(HANDLE).returns(new URL(FOLLOWING_URI));
ctx.getFollowersUri.withArgs(HANDLE).returns(new URL(FOLLOWERS_URI));

return ctx;
}

describe('getUserData', function () {
it('persists a user to the database if it does not exist', async function () {
const ctx = getCtx();

ctx.data.db.get.resolves(null);

const result = await getUserData(ctx, HANDLE);

const expectedUserData = {
id: new URL(`https://${ctx.host}/${HANDLE}`),
name: ACTOR_DEFAULT_NAME,
summary: ACTOR_DEFAULT_SUMMARY,
preferredUsername: HANDLE,
icon: new Image({ url: new URL(ACTOR_DEFAULT_ICON) }),
inbox: new URL(INBOX_URI),
outbox: new URL(OUTBOX_URI),
following: new URL(FOLLOWING_URI),
followers: new URL(FOLLOWERS_URI),
publicKeys: ['abc123'],
url: new URL(`https://${ctx.host}`),
}

assert.ok(
ctx.data.db.set.calledOnceWith(['handle', HANDLE], {
id: expectedUserData.id.href,
name: expectedUserData.name,
summary: expectedUserData.summary,
preferredUsername: expectedUserData.preferredUsername,
icon: ACTOR_DEFAULT_ICON,
inbox: expectedUserData.inbox.href,
outbox: expectedUserData.outbox.href,
following: expectedUserData.following.href,
followers: expectedUserData.followers.href,
url: expectedUserData.url.href,
})
);
assert.deepStrictEqual(result, expectedUserData);
});

it('retrievs a user from the database', async function () {
const ctx = getCtx();

const persistedUser = {
id: `https://${ctx.host}/${HANDLE}`,
name: 'foo',
summary: 'bar',
preferredUsername: HANDLE,
icon: `https://${ctx.host}/icon.png`,
inbox: INBOX_URI,
outbox: OUTBOX_URI,
following: FOLLOWING_URI,
followers: FOLLOWERS_URI,
url: `https://${ctx.host}`,
}

ctx.data.db.get.resolves(persistedUser);

const result = await getUserData(ctx, HANDLE);

const expectedUserData = {
id: new URL(`https://${ctx.host}/${HANDLE}`),
name: 'foo',
summary: 'bar',
preferredUsername: HANDLE,
icon: new Image({ url: new URL(`https://${ctx.host}/icon.png`) }),
inbox: new URL(INBOX_URI),
outbox: new URL(OUTBOX_URI),
following: new URL(FOLLOWING_URI),
followers: new URL(FOLLOWERS_URI),
publicKeys: ['abc123'],
url: new URL(`https://${ctx.host}`),
}

assert.ok(ctx.data.db.set.notCalled);
assert.deepStrictEqual(result, expectedUserData);
});

it('handles retrieving a user with an invalid icon', async function () {
const ctx = getCtx();

const persistedUser = {
id: `https://${ctx.host}/${HANDLE}`,
name: 'foo',
summary: 'bar',
preferredUsername: HANDLE,
inbox: INBOX_URI,
outbox: OUTBOX_URI,
following: FOLLOWING_URI,
followers: FOLLOWERS_URI,
url: `https://${ctx.host}`,
}

ctx.data.db.get.resolves(persistedUser);

const result = await getUserData(ctx, HANDLE);

const expectedUserData = {
id: new URL(`https://${ctx.host}/${HANDLE}`),
name: 'foo',
summary: 'bar',
preferredUsername: HANDLE,
icon: null,
inbox: new URL(INBOX_URI),
outbox: new URL(OUTBOX_URI),
following: new URL(FOLLOWING_URI),
followers: new URL(FOLLOWERS_URI),
publicKeys: ['abc123'],
url: new URL(`https://${ctx.host}`),
}

assert.ok(ctx.data.db.set.notCalled);
assert.deepStrictEqual(result, expectedUserData);
});

it('handles retrieving a user with an invalid URL', async function () {
const ctx = getCtx();

const persistedUser = {
id: `https://${ctx.host}/${HANDLE}`,
name: 'foo',
summary: 'bar',
preferredUsername: HANDLE,
icon: `https://${ctx.host}/icon.png`,
inbox: INBOX_URI,
outbox: OUTBOX_URI,
following: FOLLOWING_URI,
followers: FOLLOWERS_URI
}

ctx.data.db.get.resolves(persistedUser);

const result = await getUserData(ctx, HANDLE);

const expectedUserData = {
id: new URL(`https://${ctx.host}/${HANDLE}`),
name: 'foo',
summary: 'bar',
preferredUsername: HANDLE,
icon: new Image({ url: new URL(`https://${ctx.host}/icon.png`) }),
inbox: new URL(INBOX_URI),
outbox: new URL(OUTBOX_URI),
following: new URL(FOLLOWING_URI),
followers: new URL(FOLLOWERS_URI),
publicKeys: ['abc123'],
url: null,
}

assert.ok(ctx.data.db.set.notCalled);
assert.deepStrictEqual(result, expectedUserData);
});
});

0 comments on commit badb89f

Please sign in to comment.