Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions apps/discord-bot/.nycrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"src/**/types.ts"
],
"loader": "ts-node/esm",
"branches": 65,
"lines": 65,
"functions": 65,
"statements": 65
"branches": 70,
"lines": 70,
"functions": 70,
"statements": 70
}
3 changes: 2 additions & 1 deletion apps/discord-bot/src/bots/discord/comments/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,6 @@ export const postComment = async (client: Client, payload: PostCommentToSocialPa
throw new Error("unknown server or thread!");
}

return await thread.send(buildMessage({...payload, body: payload.text}));
const message = await thread.send(buildMessage({...payload, body: payload.text}));
return message.id;
};
39 changes: 25 additions & 14 deletions apps/discord-bot/src/core/comments/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,32 @@ import {Resp} from "../utils/response";
import {Client} from "discord.js";
import {commentHandler as discordCommentHandler} from "../../bots/discord";
import {logger} from "../utils/logger";
import {communityHasSocial, getSocialCommunityId} from "../utils/data";
import {communityHasSocial, getSocialCommunityId, getSocialThreadId} from "../utils/data";

export const postComment = async (clients: Clients, req: Request, res: Response) => {
const {commentId} = req.body;
logger.info('core', {body: req.body});
const comment: Node<Comment> = await clients.composeQuery().fetchCommentDetails(commentId);
const socials = _.get(comment, "node.thread.community.socialPlatforms.edges");
try {
logger.info('core', {body: req.body});
const {commentId} = req.body;
const comment: Node<Comment> = await clients.composeQuery().fetchCommentDetails(commentId);
const socials = _.get(comment, "node.thread.community.socialPlatforms.edges");

if (communityHasSocial(socials, constants.PLATFORM_DISCORD_NAME)) {
postCommentToDiscord(clients.discord, comment);
} else {
return Resp.notOk(res, "No discord for this community, bailing out!");
if (communityHasSocial(socials, constants.PLATFORM_DISCORD_NAME)) {
const commentId = await postCommentToDiscord(clients.discord, comment);
const response = [{
platformName: constants.PLATFORM_DISCORD_NAME,
commentId: commentId,
}];
return Resp.okD(res, response, "Created comment on socials");
} else {
return Resp.notOk(res, "No discord for this community, bailing out!");
}
} catch (e) {
logger.error('core', {e, body: req.body});
return Resp.error(res, "Server error occurred");
}
return Resp.ok(res, "Posted to socials!");
};

export const postCommentToDiscord = (discordClient: Client, comment: Node<Comment>) => {
export const postCommentToDiscord = async (discordClient: Client, comment: Node<Comment>) => {
const text = _.get(comment, "node.text");
const socials = _.get(comment, "node.thread.community.socialPlatforms.edges");
const threadStreamId = _.get(comment, "node.threadId");
Expand All @@ -31,9 +40,11 @@ export const postCommentToDiscord = (discordClient: Client, comment: Node<Commen
const userAvatar = _.get(comment, "node.user.userPlatforms[0].platformAvatar");
const userProfileLink = config.devnodeWebsite.concat(`/${userId}/profile`);
const serverId = getSocialCommunityId(socials, constants.PLATFORM_DISCORD_NAME);
const threadId = _.get(comment, "node.thread.threadId");
const threadId = getSocialThreadId(
_.get(comment, "node.thread.socialThreadIds"),
constants.PLATFORM_DISCORD_NAME
);

const payload = {text, userName, serverId, threadId, threadStreamId, userAvatar, userProfileLink};
discordCommentHandler.postComment(discordClient, payload)
.catch((e) => logger.error('core', {payload, e}));
return await discordCommentHandler.postComment(discordClient, payload)
}
7 changes: 6 additions & 1 deletion apps/discord-bot/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export interface Thread {
title: string;
body: string;
userId: string;
threadId: string;
socialThreadIds: SocialThreadId[];
createdAt: string;
community: Community;
communityId: string;
Expand Down Expand Up @@ -90,3 +90,8 @@ export interface PostThreadToSocialPayload {
threadStreamId: string;
serverId: string;
}

export interface SocialThreadId {
platformName: string;
threadId: string;
}
8 changes: 7 additions & 1 deletion apps/discord-bot/src/core/utils/data.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Node, SocialPlatform} from "../types";
import _ from "lodash";
import {Node, SocialPlatform, SocialThreadId} from "../types";

export const communityHasSocial = (socials: Node<SocialPlatform>[], platform: string) => {
const social = socials.filter((d) => d.node.platform === platform);
Expand All @@ -9,3 +10,8 @@ export const getSocialCommunityId = (socials: Node<SocialPlatform>[], platform:
const social = socials.filter((d) => d.node.platform === platform);
return social[0].node.platformId;
}

export const getSocialThreadId = (socials: SocialThreadId[], platformName: string) => {
const id = socials.find((s) => s.platformName === platformName);
return _.get(id,"threadId", "");
}
14 changes: 12 additions & 2 deletions apps/discord-bot/tests/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ export const sampleComment = {
"id": "kjzl6kcym7w8y772zht4omem25y3plfp929wsgumb158oi8uca2fnlhcdri2jqx",
"title": "Is storing secrets in redux store secure?",
"userId": "k2t6wzhkhabz2wixqd45q1wxpoy9fg7wd2dbfp2vzgq0wd2dq9sh5fkzywjxye",
"threadId": "na",
"socialThreadIds": [
{
"platformName": "discord",
"threadId": "1092795497066020864"
}
],
"createdAt": "2023-03-15T09:22:45.468Z",
"community": {
"socialPlatforms": {
Expand Down Expand Up @@ -86,7 +91,12 @@ export const sampleThread = {
"title": "Is storing secrets in redux store secure?",
"body": "From the docs, I understand that Redux store variables in memory so it should be readable to the browser, but is it secure enough to store sensitive information?",
"userId": "k2t6wzhkhabz2wixqd45q1wxpoy9fg7wd2dbfp2vzgq0wd2dq9sh5fkzywjxye",
"threadId": "na",
"socialThreadIds": [
{
"platformName": "discord",
"threadId": "1092795497066020864"
}
],
"createdAt": "2023-03-15T09:22:45.468Z",
"communityId": "kjzl6kcym7w8y8dgfi093n3o6gby2gwq4fs3nltxjl8gutwvr2ol1sf9vpdmn09",
"createdFrom": "devnode",
Expand Down
28 changes: 17 additions & 11 deletions apps/discord-bot/tests/functional/comments.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import chai, {expect} from "../setup";
import {initServer} from "../../src/core";
import {Express} from "express";
import {fakeComposeClient, fakeComposeQueryClient, fakeDiscordClient} from "../mock/fakes";
import {config} from "../../src/config";
import {config, constants} from "../../src/config";
import * as sinon from "sinon";
import {fakeComposeClient, fakeComposeQueryClient, fakeDiscordClient, sendStub} from "../mock/fakes";

describe("comment api", () => {
let server: Express;
const url = "/api/web-comment";
const header = {'x-api-key': config.server.apiKey};

before(() => {
server = initServer({
discord: fakeDiscordClient,
compose: fakeComposeClient,
composeQuery: fakeComposeQueryClient,
});
const server = initServer({
discord: fakeDiscordClient,
compose: fakeComposeClient,
composeQuery: fakeComposeQueryClient,
});

beforeEach(() => sinon.restore());

it("should authenticate the api call", async () => {
const res = await chai.request(server).post(url);
expect(res.status).to.eql(401);
Expand All @@ -32,5 +30,13 @@ describe("comment api", () => {
it("should response with 200 on sent message", async () => {
const res = await chai.request(server).post(url).set(header).send({commentId: "123"});
expect(res.status).to.eql(200);
expect(sendStub).to.be.callCount(1);
expect(res.body).to.eql({
msg: "Created comment on socials",
data: [{
platformName: constants.PLATFORM_DISCORD_NAME,
commentId: 1
}]
})
});
});
21 changes: 12 additions & 9 deletions apps/discord-bot/tests/functional/threads.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import chai, {expect} from "../setup";
import {initServer} from "../../src/core";
import {Express} from "express";
import * as sinon from "sinon";
import {fakeComposeClient, fakeComposeQueryClient, fakeDiscordClient} from "../mock/fakes";
import {config} from "../../src/config";

describe("thread api", () => {
let server: Express;
const url = "/api/web-thread";
const header = {'x-api-key': config.server.apiKey};

before(() => {
server = initServer({
discord: fakeDiscordClient,
compose: fakeComposeClient,
composeQuery: fakeComposeQueryClient,
});
const server = initServer({
discord: fakeDiscordClient,
compose: fakeComposeClient,
composeQuery: fakeComposeQueryClient,
});

beforeEach(() => sinon.restore());

it("should authenticate the api call", async () => {
const res = await chai.request(server).post(url);
expect(res.status).to.eql(401);
Expand All @@ -28,4 +26,9 @@ describe("thread api", () => {
const another = await chai.request(server).post(url).set(header).send({threadId: 123});
expect(another.status).to.eql(400);
});

it("should respond with 200 on thread creation", async () => {
const another = await chai.request(server).post(url).set(header).send({threadId: "123"});
expect(another.status).to.eql(200);
});
});
40 changes: 24 additions & 16 deletions apps/discord-bot/tests/mock/fakes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,32 @@ import {ComposeClient} from "@composedb/client";
import {sampleComment, sampleThread} from "../data";
import * as sinon from 'sinon';

export const fakeDiscordClient = {
guilds: {
cache: {
get: () => this,
},
channels: {
cache: [],
create: sinon.stub().returns(
new Promise((res) => res({
id: 1,
send: sinon.stub(),
}))
),
}
}
} as unknown as Client;
export const fakeComposeClient = {} as ComposeClient;

export const fakeComposeQueryClient = () => ({
fetchCommentDetails: async (id: string) => sampleComment,
fetchThreadDetails: async (id: string) => sampleThread,
}) as any;

export const sendStub = sinon.stub().resolves({id: 1,});
export const channelStub = {
cache: {
get: sinon.stub().returnsThis(),
find: sinon.stub().returnsThis(),
threads: {
create: sinon.stub().returnsThis(),
send: sendStub,
},
send: sendStub,
},
};

export const fakeDiscordClient = {
guilds: {
cache: {
get: sinon.stub().returnsThis(),
channels: channelStub,
send: sendStub,
},
},
} as unknown as Client;
17 changes: 15 additions & 2 deletions apps/discord-bot/tests/unit/utils/data.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {SocialPlatform, Node} from "../../../src/core/types";
import {SocialPlatform, Node, SocialThreadId} from "../../../src/core/types";
import {expect} from "../../setup";
import {communityHasSocial, getSocialCommunityId} from "../../../src/core/utils/data";
import {communityHasSocial, getSocialCommunityId, getSocialThreadId} from "../../../src/core/utils/data";

describe('utils.data', () => {
const socialPlatforms: Node<SocialPlatform>[] = [
Expand All @@ -18,6 +18,11 @@ describe('utils.data', () => {
},
];

const socialThreadIds: SocialThreadId[] = [
{ threadId: "123", platformName: "discord" },
{ threadId: "456", platformName: "discourse" },
]

describe('communityHasSocial', () => {
it('returns true when the community has the specified social platform', () => {
const hasSocial = communityHasSocial(socialPlatforms, 'Discord');
Expand All @@ -40,4 +45,12 @@ describe('utils.data', () => {
expect(() => getSocialCommunityId(socialPlatforms, 'Instagram')).throw();
});
});

describe('getSocialThreadId', () => {
it("returns thread id for matching platform name", () => {
expect(getSocialThreadId(socialThreadIds, "discord")).to.eq("123");
expect(getSocialThreadId(socialThreadIds, "discourse")).to.eq("456");
expect(getSocialThreadId(socialThreadIds, "twitter")).to.eq("");
});
});
});
26 changes: 23 additions & 3 deletions apps/web/src/server/trpc/router/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import {definition, composeMutationHandler, composeQueryHandler } from "@devnode
import {ComposeClient} from "@composedb/client";
import {config} from "../../../config";
import {left, right} from "../../../utils/fp";
import {omit, get} from "lodash";
import {has, omit, get} from "lodash";
import {DIDSession} from "did-session";
import {SocialCommentId} from "../../types";

export const compose = new ComposeClient({
ceramic: config.ceramic.nodeUrl,
Expand Down Expand Up @@ -38,7 +39,15 @@ export const commentRouter = router({
const response = await handler.createComment(payload as any);
if(response.data) {
const commentId = get(response.data, "createComment.document.id");
handleWebToAggregator(commentId);
handleWebToAggregator(commentId)
.then((res) => res.json())
.then((data) => {
if (has(data, "data[0]")) {
updateComment(handler, commentId, data.data[0])
.then(console.log)
.catch(console.log);
}
});
}
return (response.errors && response.errors.length > 0)
? left(response.errors)
Expand All @@ -60,9 +69,20 @@ export const commentRouter = router({
}),
});

const updateComment = async (handler, streamId, social: SocialCommentId) => {
try {
const response = await handler.updateCommentWithSocialCommentId(streamId, social);
return response.errors && response.errors.length > 0
? left(response.errors)
: right(response.data);
} catch (e) {
return left(e);
}
}

const handleWebToAggregator = async (commentId: string) => {
const endpoint = `${config.aggregator.endpoint}/web-comment`;
await fetch(endpoint, {
return await fetch(endpoint, {
body: JSON.stringify({
commentId: commentId,
}),
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,8 @@ export interface SocialThreadId {
platformName: string;
threadId: string;
}

export interface SocialCommentId {
platformName: string;
commentId: string;
}
Loading