Skip to content

Commit 9ea4dc4

Browse files
committed
feat: add fetchProblemSimplified method to LeetCode services and corresponding tests
1 parent 21a0bd7 commit 9ea4dc4

File tree

5 files changed

+203
-1
lines changed

5 files changed

+203
-1
lines changed

src/leetcode/leetcode-base-service.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ export interface LeetCodeBaseService {
109109
*/
110110
fetchDailyChallenge(): Promise<any>;
111111

112+
/**
113+
* Retrieves simplified information about a specific problem.
114+
* Returns only the most useful fields for the user.
115+
*
116+
* @param titleSlug - Problem identifier/slug as used in the LeetCode URL
117+
* @returns Promise resolving to the simplified problem details
118+
*/
119+
fetchProblemSimplified(titleSlug: string): Promise<any>;
120+
112121
/**
113122
* Retrieves detailed information about a specific problem.
114123
*

src/leetcode/leetcode-cn-service.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,57 @@ export class LeetCodeCNService implements LeetCodeBaseService {
206206
}
207207
}
208208

209+
async fetchProblemSimplified(titleSlug: string): Promise<any> {
210+
try {
211+
const problem = await this.fetchProblem(titleSlug);
212+
if (!problem) {
213+
throw new Error(`Problem ${titleSlug} not found`);
214+
}
215+
216+
const filteredTopicTags =
217+
problem.topicTags?.map((tag: any) => tag.slug) || [];
218+
219+
const filteredCodeSnippets =
220+
problem.codeSnippets?.filter((snippet: any) =>
221+
["cpp", "python3", "java"].includes(snippet.langSlug)
222+
) || [];
223+
224+
let parsedSimilarQuestions: any[] = [];
225+
if (problem.similarQuestions) {
226+
try {
227+
const allQuestions = JSON.parse(problem.similarQuestions);
228+
parsedSimilarQuestions = allQuestions
229+
.slice(0, 3)
230+
.map((q: any) => ({
231+
titleSlug: q.titleSlug,
232+
difficulty: q.difficulty
233+
}));
234+
} catch (e) {
235+
console.error("Error parsing similarQuestions:", e);
236+
}
237+
}
238+
239+
return {
240+
titleSlug,
241+
questionId: problem.questionId,
242+
title: problem.title,
243+
content: problem.content,
244+
difficulty: problem.difficulty,
245+
topicTags: filteredTopicTags,
246+
codeSnippets: filteredCodeSnippets,
247+
exampleTestcases: problem.exampleTestcases,
248+
hints: problem.hints,
249+
similarQuestions: parsedSimilarQuestions
250+
};
251+
} catch (error) {
252+
console.error(
253+
`Error fetching simplified problem ${titleSlug}:`,
254+
error
255+
);
256+
throw error;
257+
}
258+
}
259+
209260
async searchProblems(
210261
category?: string,
211262
tags?: string[],

src/leetcode/leetcode-global-service.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,57 @@ export class LeetCodeGlobalService implements LeetCodeBaseService {
192192
}
193193
}
194194

195+
async fetchProblemSimplified(titleSlug: string): Promise<any> {
196+
try {
197+
const problem = await this.fetchProblem(titleSlug);
198+
if (!problem) {
199+
throw new Error(`Problem ${titleSlug} not found`);
200+
}
201+
202+
const filteredTopicTags =
203+
problem.topicTags?.map((tag: any) => tag.slug) || [];
204+
205+
const filteredCodeSnippets =
206+
problem.codeSnippets?.filter((snippet: any) =>
207+
["cpp", "python3", "java"].includes(snippet.langSlug)
208+
) || [];
209+
210+
let parsedSimilarQuestions: any[] = [];
211+
if (problem.similarQuestions) {
212+
try {
213+
const allQuestions = JSON.parse(problem.similarQuestions);
214+
parsedSimilarQuestions = allQuestions
215+
.slice(0, 3)
216+
.map((q: any) => ({
217+
titleSlug: q.titleSlug,
218+
difficulty: q.difficulty
219+
}));
220+
} catch (e) {
221+
console.error("Error parsing similarQuestions:", e);
222+
}
223+
}
224+
225+
return {
226+
titleSlug,
227+
questionId: problem.questionId,
228+
title: problem.title,
229+
content: problem.content,
230+
difficulty: problem.difficulty,
231+
topicTags: filteredTopicTags,
232+
codeSnippets: filteredCodeSnippets,
233+
exampleTestcases: problem.exampleTestcases,
234+
hints: problem.hints,
235+
similarQuestions: parsedSimilarQuestions
236+
};
237+
} catch (error) {
238+
console.error(
239+
`Error fetching simplified problem ${titleSlug}:`,
240+
error
241+
);
242+
throw error;
243+
}
244+
}
245+
195246
async searchProblems(
196247
category?: string,
197248
tags?: string[],

src/mcp/tools/problem-tools.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ export class ProblemToolRegistry extends ToolRegistry {
4343
)
4444
},
4545
async ({ titleSlug }) => {
46-
const data = await this.leetcodeService.fetchProblem(titleSlug);
46+
const data =
47+
await this.leetcodeService.fetchProblemSimplified(
48+
titleSlug
49+
);
4750
return {
4851
content: [
4952
{
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { Credential, LeetCode, LeetCodeCN } from "leetcode-query";
2+
import { describe, expect, it } from "vitest";
3+
import { LeetCodeCNService } from "../../src/leetcode/leetcode-cn-service.js";
4+
import { LeetCodeGlobalService } from "../../src/leetcode/leetcode-global-service.js";
5+
6+
describe("LeetCode Problem Services", () => {
7+
describe("LeetCodeGlobalService", () => {
8+
const credential = new Credential();
9+
const leetCodeApi = new LeetCode(credential);
10+
const service = new LeetCodeGlobalService(leetCodeApi, credential);
11+
12+
describe("fetchProblemSimplified", () => {
13+
it("should return simplified problem data", async () => {
14+
const titleSlug = "two-sum";
15+
const result = await service.fetchProblemSimplified(titleSlug);
16+
17+
expect(result).toBeDefined();
18+
expect(result.titleSlug).toBe(titleSlug);
19+
expect(result.title).toBe("Two Sum");
20+
expect(result.questionId).toBeDefined();
21+
expect(result.questionFrontendId).toBeDefined();
22+
expect(result.content).toBeDefined();
23+
expect(result.difficulty).toBeDefined();
24+
expect(Array.isArray(result.topicTags)).toBe(true);
25+
expect(Array.isArray(result.codeSnippets)).toBe(true);
26+
expect(result.hints).toBeDefined();
27+
28+
if (result.similarQuestions) {
29+
expect(Array.isArray(result.similarQuestions)).toBe(true);
30+
}
31+
}, 30000);
32+
33+
it("should handle invalid problems correctly", async () => {
34+
const invalidSlug = `invalid-problem-${Date.now()}`;
35+
36+
try {
37+
await service.fetchProblemSimplified(invalidSlug);
38+
expect(true).toBe(false);
39+
} catch (error) {
40+
expect(error).toBeDefined();
41+
}
42+
}, 30000);
43+
});
44+
});
45+
46+
describe("LeetCodeCNService", () => {
47+
const credential = new Credential();
48+
const leetCodeApi = new LeetCodeCN(credential);
49+
const service = new LeetCodeCNService(leetCodeApi, credential);
50+
51+
describe("fetchProblemSimplified", () => {
52+
it("should return simplified problem data", async () => {
53+
const titleSlug = "two-sum";
54+
const result = await service.fetchProblemSimplified(titleSlug);
55+
56+
expect(result).toBeDefined();
57+
expect(result.titleSlug).toBe(titleSlug);
58+
expect(result.questionId).toBeDefined();
59+
expect(result.questionFrontendId).toBeDefined();
60+
expect(result.title).toBeDefined();
61+
expect(result.content).toBeDefined();
62+
expect(result.difficulty).toBeDefined();
63+
expect(Array.isArray(result.topicTags)).toBe(true);
64+
expect(Array.isArray(result.codeSnippets)).toBe(true);
65+
expect(result.hints).toBeDefined();
66+
67+
if (result.similarQuestions) {
68+
expect(Array.isArray(result.similarQuestions)).toBe(true);
69+
}
70+
71+
console.log(
72+
`Successfully fetched simplified data for ${titleSlug}`
73+
);
74+
}, 30000);
75+
76+
it("should handle invalid problems correctly", async () => {
77+
const invalidSlug = `invalid-problem-${Date.now()}`;
78+
79+
try {
80+
await service.fetchProblemSimplified(invalidSlug);
81+
expect(true).toBe(false);
82+
} catch (error) {
83+
expect(error).toBeDefined();
84+
}
85+
}, 30000);
86+
});
87+
});
88+
});

0 commit comments

Comments
 (0)