Skip to content

Commit 9ab37c9

Browse files
committed
fix: replaced direct imports of githubDiscordMap.json with UserService
1 parent 9c5a858 commit 9ab37c9

7 files changed

Lines changed: 84 additions & 111 deletions

File tree

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,9 @@
1-
import githubDiscordMapJson from "../../../data/githubDiscordMap.json";
1+
import { UserService } from "@src/items/services/UserService";
22

3-
// New structure: map from GitHub username to object with discordId
4-
const githubDiscordMap: {
5-
[key: string]: {
6-
githubUsername: string;
7-
githubId: string;
8-
discordId: string;
9-
};
10-
} = githubDiscordMapJson;
11-
12-
// TODO: this any should be the generalized discord.js interaction type so that all interactions can leverage this method
13-
export const can = (interaction: any): boolean => {
3+
export const can = async (interaction: any): Promise<boolean> => {
144
const userId = interaction.user?.id;
5+
if (!userId) return false;
156

16-
const discordIds = Object.values(githubDiscordMap).map(
17-
(entry) => entry.discordId,
18-
);
19-
const isAuthorized = discordIds.includes(userId);
20-
21-
return isAuthorized;
7+
const userResult = await UserService.findUserByDiscordID(userId);
8+
return userResult.ok;
229
};

src/infrastructure/discord/commands/myIssues.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
11
import { SlashCommandBuilder, CommandInteraction } from "discord.js";
22
import { GithubAPI } from "@infrastructure/github";
33
import logger from "@config/logger";
4-
import githubDiscordMapJson from "../../../../data/githubDiscordMap.json";
4+
import { UserService } from "@src/items/services/UserService";
55
import { can } from "../authz";
66
import { buildIssueButtonRow } from "../builders";
77
import { formatDiscordDate } from "../webhookMessages";
88

9-
// Update type to reflect new structure
10-
const githubDiscordMap: {
11-
[githubUsername: string]: {
12-
githubUsername: string;
13-
githubId: string;
14-
discordId: string;
15-
};
16-
} = githubDiscordMapJson;
17-
189
export const data = new SlashCommandBuilder()
1910
.setName("my-issues")
2011
.setDescription("List all GitHub project issues assigned to you")
@@ -35,19 +26,19 @@ export async function execute(interaction: CommandInteraction) {
3526
}
3627

3728
const discordUserId = interaction.user.id;
38-
39-
const githubUsername = Object.values(githubDiscordMap).find(
40-
(entry) => entry.discordId === discordUserId,
41-
)?.githubUsername;
42-
43-
if (!githubUsername) {
29+
30+
const userResult = await UserService.findUserByDiscordID(discordUserId);
31+
if (userResult.err) {
4432
await interaction.reply({
4533
content: "❌ You don’t appear to be linked to a GitHub account.",
4634
ephemeral: true,
4735
});
4836
return;
4937
}
5038

39+
const githubUsername = userResult.val.githubUsername;
40+
41+
5142
await interaction.deferReply({ ephemeral: true });
5243

5344
const githubItemsResult = await GithubAPI.fetchProjectItems();

src/infrastructure/discord/interactions/assigneeSelectInteraction.ts

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
11
import { ItemService } from "@src/items/services";
22
import { UserSelectMenuInteraction } from "discord.js";
3-
import githubDiscordMapJson from "../../../../data/githubDiscordMap.json";
4-
5-
// Updated structure of the map
6-
const githubDiscordMap: {
7-
[githubUsername: string]: {
8-
githubUsername: string;
9-
githubId: string;
10-
discordId: string;
11-
};
12-
} = githubDiscordMapJson;
3+
import { UserService } from "@src/items/services/UserService";
134

145
export async function assigneeSelectInteraction(
156
interaction: UserSelectMenuInteraction,
@@ -22,19 +13,18 @@ export async function assigneeSelectInteraction(
2213
const githubIssueId = match[1];
2314
const selectedUserId = interaction.values[0];
2415

25-
// Find the GitHub ID using the selected Discord ID
26-
const githubId = Object.values(githubDiscordMap).find(
27-
(entry) => entry.discordId === selectedUserId,
28-
)?.githubId;
29-
30-
if (!githubId) {
16+
const userResult = await UserService.findUserByDiscordID(selectedUserId);
17+
if (userResult.err) {
3118
await interaction.reply({
32-
content: "❌ Unable to find linked GitHub account for selected user.",
19+
content: "❌ You don’t appear to be linked to a GitHub account.",
3320
ephemeral: true,
3421
});
3522
return;
3623
}
3724

25+
// Find the GitHub ID using the selected Discord ID
26+
const githubId = userResult.val.githubId
27+
3828
const result = await ItemService.updateAssignee({
3929
itemId: githubIssueId,
4030
assigneeId: githubId,

src/infrastructure/discord/tasks/urgentItemsDirectMessage.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,7 @@ import { GithubAPI } from "@infrastructure/github";
22
import logger from "@src/config/logger";
33
import { filterOutStatus, filterForUrgentItems } from "@src/items";
44
import { formatDiscordDate } from "../webhookMessages";
5-
6-
type GithubDiscordMapping = {
7-
[githubUsername: string]: {
8-
githubUsername: string;
9-
githubId: string;
10-
discordId: string;
11-
};
12-
};
13-
import githubDiscordMapJson from "../../../../data/githubDiscordMap.json";
14-
const githubDiscordMap = githubDiscordMapJson as GithubDiscordMapping;
5+
import { UserService } from "@src/items/services/UserService";
156

167
const urgencyEmojis = ["⏰", "🚨", "⚠️", "❗", "🧨", "💥"];
178

@@ -47,16 +38,16 @@ export const urgentItemsDirectMessage = async (client: any) => {
4738
const githubUsername = githubUrl.split("/").pop();
4839
if (!githubUsername) continue;
4940

50-
const mapping = githubDiscordMap[githubUsername];
51-
if (!mapping || !mapping.discordId) {
41+
const userResult = await UserService.findUserByGithubUsername(githubUsername);
42+
if (userResult.err || !userResult.val.discordId) {
5243
logger.warn({
5344
event: "dailyTasksReminder.missingMapping",
5445
body: `No Discord ID found for GitHub user: ${githubUsername}`,
5546
});
5647
continue;
5748
}
5849

59-
const discordId = mapping.discordId;
50+
const discordId = userResult.val.discordId;
6051
const list = groupedByDiscordId.get(discordId) || [];
6152
list.push({
6253
title: item.title,

src/infrastructure/discord/webhookMessages.ts

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import axios from "axios";
22
import { Err, Ok, Result } from "ts-results";
33
import dotenv from "dotenv";
4-
import githubDiscordMap from "../../../data/githubDiscordMap.json";
4+
import { UserService } from "@src/items/services/UserService";
55
import { Item } from "../../items";
66
import logger from "@config/logger";
77

@@ -57,15 +57,6 @@ export const sendDiscordItemMessage = async (
5757
}
5858
};
5959

60-
const githubUrlToDiscordId = (githubUrl: string) => {
61-
return githubDiscordMap[
62-
githubUrl.replace(
63-
"https://github.com/",
64-
"",
65-
) as keyof typeof githubDiscordMap
66-
].discordId;
67-
};
68-
6960
export const formatDiscordDate = (date: Date) => {
7061
return `<t:${Math.floor(date.getTime() / 1000)}:D>`;
7162
};
@@ -78,18 +69,17 @@ const formatMessageSectionTitle = (title: string) => {
7869
return `\n### ${title}: \n`;
7970
};
8071

81-
const formatDiscordAssignees = (assignees: string[]) => {
82-
// TODO: we should filter out the github url before getting to this stuff
83-
return assignees
84-
.map((assignee) => {
85-
const discordId = githubUrlToDiscordId(assignee);
86-
if (discordId) {
87-
return `<@${discordId}>`;
88-
} else {
89-
return assignee;
90-
}
91-
})
92-
.join(", ");
72+
const formatDiscordAssignees = async (assignees: string[]) => {
73+
const mentions = await Promise.all(
74+
assignees.map(async (githubUrl) => {
75+
const githubUsername = githubUrl.replace("https://github.com/", "");
76+
const userResult = await UserService.findUserByGithubUsername(githubUsername);
77+
78+
return userResult.ok ? `<@${userResult.val.discordId}>` : githubUsername;
79+
}),
80+
);
81+
82+
return mentions.join(", ");
9383
};
9484

9585
const formatItem = (item: Item) => {

test/infrastructure/discord/interactions/assigneeSelectInteraction.test.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { ItemService } from "@src/items/services";
2+
import { assigneeSelectInteraction } from "@infrastructure/discord/interactions/assigneeSelectInteraction";
3+
import { UserService } from "@src/items/services/UserService";
24

35
// Define IDs
46
const discordId = "147881865548791808";
@@ -12,20 +14,12 @@ jest.mock("@src/items/services", () => ({
1214
},
1315
}));
1416

15-
// Use dynamic mocking for githubDiscordMapJson
16-
beforeAll(() => {
17-
jest.doMock("../../../../data/githubDiscordMap.json", () => {
18-
return {
19-
[githubUsername]: {
20-
githubUsername,
21-
githubId,
22-
discordId,
23-
},
24-
};
25-
});
26-
});
27-
28-
import { assigneeSelectInteraction } from "@infrastructure/discord/interactions/assigneeSelectInteraction";
17+
// Manual mock of UserService
18+
jest.mock("@src/items/services/UserService", () => ({
19+
UserService: {
20+
findUserByDiscordID: jest.fn(),
21+
},
22+
}));
2923

3024
describe("assigneeSelectInteraction", () => {
3125
const mockReply = jest.fn();
@@ -58,6 +52,10 @@ describe("assigneeSelectInteraction", () => {
5852
"not-in-map",
5953
]);
6054

55+
(UserService.findUserByDiscordID as jest.Mock).mockResolvedValue({
56+
err: true,
57+
});
58+
6159
await assigneeSelectInteraction(interaction as any);
6260

6361
expect(mockReply).toHaveBeenCalledWith({
@@ -69,6 +67,15 @@ describe("assigneeSelectInteraction", () => {
6967
it("will show error if updateAssignee fails", async () => {
7068
const interaction = makeInteraction("issue:assignee:select:issue-001");
7169

70+
(UserService.findUserByDiscordID as jest.Mock).mockResolvedValue({
71+
ok: true,
72+
val: {
73+
githubUsername,
74+
githubId,
75+
discordId,
76+
},
77+
});
78+
7279
(ItemService.updateAssignee as jest.Mock).mockResolvedValue({ err: true });
7380

7481
await assigneeSelectInteraction(interaction as any);
@@ -88,6 +95,15 @@ describe("assigneeSelectInteraction", () => {
8895
it("will update message if assignee update succeeds", async () => {
8996
const interaction = makeInteraction("issue:assignee:select:issue-002");
9097

98+
(UserService.findUserByDiscordID as jest.Mock).mockResolvedValue({
99+
ok: true,
100+
val: {
101+
githubUsername,
102+
githubId,
103+
discordId,
104+
},
105+
});
106+
91107
(ItemService.updateAssignee as jest.Mock).mockResolvedValue({ err: false });
92108

93109
await assigneeSelectInteraction(interaction as any);

test/infrastructure/discord/tasks/urgentItemsDirecMessage.test.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { urgentItemsDirectMessage } from "@infrastructure/discord/tasks/urgentItemsDirectMessage";
22
import { GithubAPI } from "@infrastructure/github";
3+
import { UserService } from "@src/items/services/UserService";
34

45
jest.mock("@infrastructure/github", () => ({
56
GithubAPI: {
@@ -11,17 +12,11 @@ jest.mock("@infrastructure/discord/webhookMessages", () => ({
1112
formatDiscordDate: jest.fn((date) => date.toISOString().split("T")[0]),
1213
}));
1314

14-
jest.mock(
15-
"../../../../data/githubDiscordMap.json",
16-
() => ({
17-
MathyouMB: {
18-
githubUsername: "MathyouMB",
19-
githubId: "mock-github-id",
20-
discordId: "147881865548791808",
21-
},
22-
}),
23-
{ virtual: true },
24-
);
15+
jest.mock("@src/items/services/UserService", () => ({
16+
UserService: {
17+
findUserByGithubUsername: jest.fn(),
18+
},
19+
}));
2520

2621
describe("urgentItemsDirectMessage", () => {
2722
let mockSend: jest.Mock;
@@ -51,6 +46,15 @@ describe("urgentItemsDirectMessage", () => {
5146
],
5247
});
5348

49+
(UserService.findUserByGithubUsername as jest.Mock).mockResolvedValue({
50+
ok: true,
51+
val: {
52+
githubUsername: "MathyouMB",
53+
githubId: "mock-github-id",
54+
discordId: "147881865548791808",
55+
},
56+
});
57+
5458
await urgentItemsDirectMessage(mockClient);
5559

5660
expect(mockClient.users.fetch).toHaveBeenCalledWith("147881865548791808");
@@ -80,6 +84,10 @@ describe("urgentItemsDirectMessage", () => {
8084
],
8185
});
8286

87+
(UserService.findUserByGithubUsername as jest.Mock).mockResolvedValue({
88+
err: true,
89+
});
90+
8391
await urgentItemsDirectMessage(mockClient);
8492

8593
expect(mockSend).not.toHaveBeenCalled();

0 commit comments

Comments
 (0)