├── .dockerignore ├── .gitignore ├── .prettierrc ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── bun.lockb ├── docker-compose.yml ├── jest.config.ts ├── package-lock.json ├── package.json ├── public └── demo │ ├── contribute.png │ ├── demo1.png │ ├── demo10.png │ ├── demo11.png │ ├── demo12.png │ ├── demo13.png │ ├── demo14.png │ ├── demo15.png │ ├── demo16.png │ ├── demo17.png │ ├── demo2.png │ ├── demo3.png │ ├── demo4.png │ ├── demo5.png │ ├── demo6.png │ ├── demo7.png │ ├── demo8.png │ └── demo9.png ├── setupTests.ts ├── src ├── .DS_Store ├── Controllers │ ├── fetchDataRawFormat.ts │ ├── fetchDiscussion.ts │ ├── fetchProblems.ts │ ├── fetchSingleProblem.ts │ ├── fetchUserDetails.ts │ └── index.ts ├── FormatUtils │ ├── index.ts │ ├── problemData.ts │ ├── trendingTopicData.ts │ └── userData.ts ├── GQLQueries │ ├── contest.ts │ ├── dailyProblem.ts │ ├── index.ts │ ├── languageStats.ts │ ├── newQueries.ts │ ├── problemList.ts │ ├── recentAcSubmit.ts │ ├── recentSubmit.ts │ ├── selectProblem.ts │ ├── trendingDiscuss.ts │ └── userProfile.ts ├── __tests__ │ ├── msw │ │ ├── handlers.ts │ │ ├── index.ts │ │ └── mockData │ │ │ ├── dailyProblem.json │ │ │ ├── index.ts │ │ │ ├── problems.json │ │ │ ├── recentAcSubmissionList.json │ │ │ ├── recentSubmissions.json │ │ │ ├── selectProblem.json │ │ │ ├── singleUser.json │ │ │ └── singleUserContests.json │ ├── problemData.spec.ts │ └── userData.spec.ts ├── app.ts ├── config.ts ├── index.ts ├── leetCode.ts └── types.ts ├── tsconfig.json └── vercel.json /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | leetCode-api.txt 4 | 5 | dist 6 | 7 | .DS_Store -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Wanna Contribute 🤔?? 2 | 3 | ## With Docker 🐳 4 | 5 | - #### 🍴 Create a Fork of this Repo to Your GitHub Account 6 | 7 | ![Alt text](./public/demo/contribute.png) 8 | 9 | - #### 🧑🏽‍💻 Clone Your Repo Locally 10 | 11 | first open your CLI and clone the repo locally. But don't forget to replace **username** with your GitHub username 12 | 13 | ```bash 14 | git clone https://github.com/username/alfa-leetcode-api.git 15 | ``` 16 | 17 | - #### 👋🏽 Go to the Project Folder 18 | 19 | ```bash 20 | cd alfa-leetcode-api 21 | ``` 22 | 23 | - #### 🚀 Start the Project with Docker 24 | ```bash 25 | docker compose up 26 | ``` 27 | 28 | ## Without Docker 🐳❌ 29 | 30 | - #### 🍴 Create a Fork of this repo to Your GitHub Account 31 | 32 | ![Alt text](./public/demo/contribute.png) 33 | 34 | - #### 🧑🏽‍💻 Clone Your Repo Locally 35 | 36 | first open your CLI and clone the repo locally. But don't forget to replace **username** with your GitHub username 37 | 38 | ```bash 39 | git clone https://github.com/username/alfa-leetcode-api.git 40 | ``` 41 | 42 | - #### 👋🏽 Go to the Project Folder 43 | 44 | ```bash 45 | cd alfa-leetcode-api 46 | ``` 47 | 48 | - #### 🚀 Install Required Modules 49 | 50 | ```bash 51 | npm install 52 | ``` 53 | 54 | - #### 🏃🏽‍♂️Run the Project Locally 55 | 56 | ```bash 57 | npm run dev 58 | ``` 59 | 60 | ## 61 | 62 | - #### 💡 Make Changes & Create Pull Requests 63 | 64 | Make changes as needed and push them to your GitHub repository with proper **commit message**. From there, create a **PR (Pull Request)** and submit it. 65 | 66 | - #### 📌 Additional Tip 67 | 68 | Try to create a new branch relevant to your work. For example - `fix:xyz-issue` 69 | 70 | 📝Note - It's not Necessary for contribution but it will help me to merge it. 71 | 72 | # 73 | 74 | #### 📌 Special Thanks to [@sazsu](https://github.com/sazsu) for improving the documentation 75 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-alpine 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package*.json ./ 6 | 7 | RUN npm install 8 | 9 | COPY . . 10 | 11 | # need to remove when we use dev command 12 | # RUN npm run build 13 | 14 | EXPOSE 3000 15 | 16 | # CMD ["node", "dist/index.js"] 17 | CMD ["npm", "run", "dev"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Arghya Das 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

alfa-leetcode-api

2 |
3 | 4 | ### The API for retrieving your LeetCode profile & Problems statistics 5 | 6 | 7 | 8 | 9 | ![TypeScript](https://img.shields.io/badge/typetscript-%2320232a.svg?style=for-the-badge&logo=typescript&logoColor=%fff) 10 | ![Node.js](https://img.shields.io/badge/Node.js-%2320232a?style=for-the-badge&logo=node.js&logoColor=43853D) 11 | ![Express.js](https://img.shields.io/badge/express-%2320232a.svg?style=for-the-badge&logo=express&logoColor=%23F7DF1E) 12 | ![REST API](https://img.shields.io/badge/RestApi-%2320232a.svg?style=for-the-badge&logo=restAPI&logoColor=%23F7DF1E) 13 | 14 |
15 | 16 | ## About ✨ 17 | 18 | At First, I struggled to find proper documentation for the `leetcode.com/graphql`. After scouring various articles about the LeetCode public API, I still couldn't find the comprehensive documentation I was looking for. Fueled by the desire to fill this gap I created **alfa-leetcode-api**. 19 | 20 | **alfa-leetcode-api** is a custom solution born out of the need for a well-documented and detailed LeetCode API. This project is designed to provide developers with endpoints that offer insights into a user's profile, badges, solved questions, contest details, contest history, submissions, and also daily questions, selected problem, list of problems. 21 | 22 | ## API URL 🌐 23 | 24 | ``` 25 | https://alfa-leetcode-api.onrender.com/ 26 | ``` 27 | 28 | ## Run with docker 🐳 29 | 30 | ``` 31 | docker run -p 3000:3000 alfaarghya/alfa-leetcode-api:2.0.1 32 | ``` 33 | 34 | ## Wanna Contribute 🤔?? 35 | 36 | follow this documentation => CONTRIBUTING.md 37 | 38 | ### 💡 Rate Limit 39 | 40 | I've implemented a rate limit to prevent any potential server overload issues. 41 | 42 | ### ‼️ Note 43 | 44 | During development, it's recommended to utilize the API locally. To do so, follow this documentation => Local Deploy 45 | 46 | ## Endpoints 🚀 47 | 48 | ### 👤User Details 49 | 50 | | Details | Endpoint | Description | Demo | 51 | | :---------------------------- | :----------------------------------- | :------------------------------------------------------------------- | ----------------------------------------------------------------- | 52 | | _Profile_ | `/:username` | Get details about a user's profile. | click here | 53 | | _Badges_ | `/:username/badges` | Get the badges earned by the user. | click here | 54 | | _Solved_ | `/:username/solved` | Get the total number of questions solved by the user. | click here | 55 | | _Contest_ | `/:username/contest` | Get details about the user's contest participation. | click here | 56 | | _Contest History_ | `/:username/contest/history` | Get contest history. | click here | 57 | | _Submission_ | `/:username/submission` | Get the last 20 submissions of the user. | click here | 58 | | _Limited Submission_ | `/:username/submission?limit=number` | Get a specified **_number_** of the user's last submissions. | click here | 59 | | _Accepted Submission_ | `/:username/acSubmission` | Get the last 20 accepted submission of the user. | click here | 60 | | _Limited Accepted Submission_ | `/:username/acSubmission?limit=7` | Get a specified **_number_** of the user's last accepted submission. | click here | 61 | | _Calendar_ | `/:username/calendar` | Get the user's submission calendar. | click here | 62 | 63 | ### 😀 New Endpoints 🎉 64 | 65 | | Details | Endpoint | Description | 66 | | :--------------------- | :------------------------------------------------- | :----------------------------------- | 67 | | _Full Profile_ | `/userProfile/:username` | get full profile details in one call | 68 | | _Year Calender_ | `/userProfileCalendar?username=yourname&year=2024` | get your calendar details with year | 69 | | _Lang Stats_ | `/languageStats?username=yourname` | get the language stats of a user | 70 | | _Question Progress_ | `/userProfileUserQuestionProgressV2/:userSlug` | get your question progress | 71 | | _Skill Stats_ | `/skillStats/:username` | get your skill stats | 72 | | _User Contest Ranking_ | `/userContestRankingInfo/:username` | get contest ranking | 73 | | _Trending Discussion_ | `/trendingDiscuss?first=20` | get top 20 trending discussions | 74 | | _Discussion Topic_ | `/discussTopic/:topicId` | get discussion topic | 75 | | _Discussion Comment_ | `/discussComments/:topicId` | get discussion comments | 76 | | _Raw Daily Problem_ | `/dailyQuestion` | get raw daily question | 77 | 78 | ### ❓Questions Details 79 | 80 | | Details | Endpoint | Description | Demo | 81 | | :--------------------------------- | :------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | 82 | | _Daily Problem_ | `/daily` | Get the daily question. | click here | 83 | | _Selected Problem_ | `/select?titleSlug=selected-question` | Get details about a **_selected-question_**. | click here | 84 | | _Problems_ | `/problems` | Get a list of 20 problems. | click here | 85 | | _Limited Problems_ | `/problems?limit=number` | Get a list of a specified **_number_** of problems. | click here | 86 | | _Filter Problems_ | `/problems?tags=tag1+tag2` | Get a list of problems based on selected **_tags_**. | click here | 87 | | _Skip Problems_ | `/problems?skip=number` | Get a list of 20 problems, skipping a specified **_number_** of problems. | TODO | 88 | | _Difficulty_ | `/problems?difficulty=EASY` | Get a list of difficulty based problems, use **_MEDIUM_** to get medium level, **_HARD_** to get Hard level . | TODO | 89 | | _Filter & Limited Problems_ | `/problems?tags=tag1+tag2+tag3&limit=number` | Get a list of a specified **_number_** of problems based on selected **_tags_**. | click here | 90 | | _Skip & Limited Problems_ | `/problems?limit=number&skip=number` | Get a list of a specified **_number_** of problems skipping a specified **number** of problems. | TODO | 91 | | _Skip & Filter & Limited Problems_ | `/problems?tags=tag1+tag2+tag3&limit=number&skip=number` | Get a list of a specified **_number_** of problems based on selected **_tags_** skipping a specified **number** of problems. | TODO | 92 | 93 | ## Author ✒️ 94 | 95 | - [@alfaarghya](https://www.github.com/alfaarghya) 96 | 97 | ## Contributor ✏️ 98 | 99 | | Contributor | Contribution | 100 | | :------------------------------------------------- | :------------------------------------------------------------------------------- | 101 | | [@aryanpingle](https://www.github.com/aryanpingle) | AC submission | 102 | | [@jamesh48](https://www.github.com/jamesh48) | TypeScript Refactoring | 103 | | [@kvqn](https://www.github.com/kvqn) | PORT environment variable | 104 | | [@changchunlei](https://github.com/changchunlei) | New Endpoints - language stats, integrated user profile, contest and discussions | 105 | | [@merakesh99](https://github.com/merakesh99) | Hot reload issue solved | 106 | | [@ajchili](https://github.com/ajchili) | Skip param to fetch problems | 107 | | [@theinit01](https://github.com/theinit01) | Temp fix for skip | 108 | | [@123xylem](https://github.com/123xylem) | Add Descriptions and Methods to API route documentation. | 109 | | [@P-M-Manmohan](https://github.com/P-M-Manmohan) | Added filtering based on difficulty | 110 | 111 | ## Connect with me 📲 112 | 113 | [![LinkedIn](https://img.shields.io/badge/linkedin-%2320232a.svg?style=normal&logo=linkedIn&logoColor=%230077B5)](https://linkedin.com/in/alfaarghya) 114 | [![Twitter](https://img.shields.io/badge/twitter-%2320232a.svg?style=normal&logo=twitter&logoColor=%230077B5)](https://twitter.com/alfaarghya) 115 | [![Instagram](https://img.shields.io/badge/Instagram-%2320232a.svg?style=normal&logo=instagram&logoColor=white)](https://www.instagram.com/alfaarghya) 116 | [![LeetCode](https://img.shields.io/badge/LeetCode-%2320232a.svg?style=normal&logo=LeetCode&logoColor=%FFA116)](https://leetcode.com/alfaarghya/) 117 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/bun.lockb -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | app: 5 | build: . 6 | container_name: alfa-leetcode-api-docker 7 | ports: 8 | - '3000:3000' 9 | restart: always 10 | environment: 11 | - WDS_SOCKET_HOST=127.0.0.1 12 | - CHOKIDAR_USEPOLLING=true 13 | - WATCHPACK_POLLING=true 14 | volumes: 15 | - .:/usr/src/app 16 | - /usr/src/app/node_modules 17 | command: npm run dev 18 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from '@jest/types'; 2 | 3 | const customJestConfig: Config.InitialOptions = { 4 | preset: 'ts-jest', 5 | transform: { 6 | '^.+\\.(ts|tsx)?$': 'ts-jest', 7 | }, 8 | moduleDirectories: ['node_modules', '/'], 9 | setupFilesAfterEnv: ['/setupTests.ts'], 10 | testMatch: ['/src/__tests__/**/*.spec.ts'], 11 | testPathIgnorePatterns: ['/iac'], 12 | }; 13 | 14 | module.exports = async function () { 15 | return customJestConfig; 16 | }; 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alfa-leetcode-api", 3 | "version": "2.0.1", 4 | "description": "It's a leetcode custom api. This API provides endpoints to retrieve details about a user's profile, badges, solved questions, contest details, contest history, submissions, calendar and and also daily questions, selected problem, list of problems.", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "dev": "nodemon --exec ts-node src/index.ts", 9 | "start": "node dist/index.js", 10 | "prestart": "npm run build", 11 | "test": "jest" 12 | }, 13 | "keywords": [], 14 | "author": "alfaarghya", 15 | "license": "ISC", 16 | "dependencies": { 17 | "apicache": "^1.6.3", 18 | "axios": "^1.7.2", 19 | "cors": "^2.8.5", 20 | "express": "^4.18.2", 21 | "express-rate-limit": "^7.1.5" 22 | }, 23 | "devDependencies": { 24 | "@types/apicache": "^1.6.6", 25 | "@types/cors": "^2.8.17", 26 | "@types/express": "^4.17.21", 27 | "@types/jest": "^29.5.12", 28 | "@types/node": "^20.11.27", 29 | "@types/supertest": "^6.0.2", 30 | "jest": "^29.7.0", 31 | "msw": "^2.2.3", 32 | "nodemon": "^3.1.0", 33 | "supertest": "^6.3.4", 34 | "ts-jest": "^29.1.2", 35 | "ts-node": "^10.9.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /public/demo/contribute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/contribute.png -------------------------------------------------------------------------------- /public/demo/demo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo1.png -------------------------------------------------------------------------------- /public/demo/demo10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo10.png -------------------------------------------------------------------------------- /public/demo/demo11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo11.png -------------------------------------------------------------------------------- /public/demo/demo12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo12.png -------------------------------------------------------------------------------- /public/demo/demo13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo13.png -------------------------------------------------------------------------------- /public/demo/demo14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo14.png -------------------------------------------------------------------------------- /public/demo/demo15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo15.png -------------------------------------------------------------------------------- /public/demo/demo16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo16.png -------------------------------------------------------------------------------- /public/demo/demo17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo17.png -------------------------------------------------------------------------------- /public/demo/demo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo2.png -------------------------------------------------------------------------------- /public/demo/demo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo3.png -------------------------------------------------------------------------------- /public/demo/demo4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo4.png -------------------------------------------------------------------------------- /public/demo/demo5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo5.png -------------------------------------------------------------------------------- /public/demo/demo6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo6.png -------------------------------------------------------------------------------- /public/demo/demo7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo7.png -------------------------------------------------------------------------------- /public/demo/demo8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo8.png -------------------------------------------------------------------------------- /public/demo/demo9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/public/demo/demo9.png -------------------------------------------------------------------------------- /setupTests.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment node 3 | */ 4 | 5 | import { server } from './src/__tests__/msw'; 6 | 7 | beforeAll(() => { 8 | server.listen(); 9 | }); 10 | 11 | afterAll(() => server.close()); 12 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfaarghya/alfa-leetcode-api/d362520c8c2feb41a5dcfc8445ce5db20a4662cb/src/.DS_Store -------------------------------------------------------------------------------- /src/Controllers/fetchDataRawFormat.ts: -------------------------------------------------------------------------------- 1 | import { Response } from 'express'; 2 | 3 | const fetchDataRawFormat = async ( 4 | options: { username: string }, 5 | res: Response, 6 | query: string 7 | ) => { 8 | try { 9 | 10 | const response = await fetch('https://leetcode.com/graphql', { 11 | method: 'POST', 12 | headers: { 13 | 'Content-Type': 'application/json', 14 | Referer: 'https://leetcode.com', 15 | }, 16 | body: JSON.stringify({ 17 | query: query, 18 | variables: { 19 | username: options.username, 20 | }, 21 | }), 22 | }); 23 | 24 | const result = await response.json(); 25 | if (!response.ok) { 26 | console.error(`HTTP error! status: ${response.status}`); 27 | } 28 | if (result.errors) { 29 | return res.send(result); 30 | } 31 | 32 | return res.json(result.data); 33 | } catch (err) { 34 | console.error('Error: ', err); 35 | return res.send(err); 36 | } 37 | }; 38 | 39 | export default fetchDataRawFormat; 40 | -------------------------------------------------------------------------------- /src/Controllers/fetchDiscussion.ts: -------------------------------------------------------------------------------- 1 | import { Response } from 'express'; 2 | import { TrendingDiscussionObject } from '../types'; 3 | 4 | const fetchDiscussion = async ( 5 | options: { first: number }, 6 | res: Response, 7 | formatData: (data: TrendingDiscussionObject) => {}, 8 | query: string 9 | ) => { 10 | try { 11 | const response = await fetch('https://leetcode.com/graphql', { 12 | method: 'POST', 13 | headers: { 14 | 'Content-Type': 'application/json', 15 | Referer: 'https://leetcode.com', 16 | }, 17 | body: JSON.stringify({ 18 | query: query, 19 | variables: { 20 | first: options.first || 20, //by default get 20 question 21 | }, 22 | }), 23 | }); 24 | 25 | const result = await response.json(); 26 | 27 | if (result.errors) { 28 | return res.send(result); 29 | } 30 | 31 | return res.json(formatData(result.data)); 32 | } catch (err) { 33 | console.error('Error: ', err); 34 | return res.send(err); 35 | } 36 | }; 37 | 38 | export default fetchDiscussion; 39 | -------------------------------------------------------------------------------- /src/Controllers/fetchProblems.ts: -------------------------------------------------------------------------------- 1 | import { Response } from 'express'; 2 | import { ProblemSetQuestionListData } from '../types'; 3 | 4 | const fetchProblems = async ( 5 | options: { limit?: number; skip?: number; tags?: string; difficulty?: string}, // Mark parameters as optional 6 | res: Response, 7 | formatData: (data: ProblemSetQuestionListData) => {}, 8 | query: string 9 | ) => { 10 | try { 11 | // Set default limit to 1 if only skip is provided 12 | const limit = options.skip !== undefined && options.limit === undefined ? 1 : options.limit || 20; 13 | const skip = options.skip || 0; // Default to 0 if not provided 14 | const tags = options.tags ? options.tags.split(' ') : []; // Split tags or default to empty array 15 | const difficulty = options.difficulty || undefined; // difficulty has to be 'EASY', 'MEDIUM' or 'HARD' 16 | 17 | const response = await fetch('https://leetcode.com/graphql', { 18 | method: 'POST', 19 | headers: { 20 | 'Content-Type': 'application/json', 21 | Referer: 'https://leetcode.com', 22 | }, 23 | body: JSON.stringify({ 24 | query: query, 25 | variables: { 26 | categorySlug: '', 27 | skip, 28 | limit, 29 | filters: { tags, 30 | difficulty 31 | }, 32 | }, 33 | }), 34 | }); 35 | console.log(response) 36 | 37 | const result = await response.json(); 38 | 39 | if (result.errors) { 40 | return res.status(400).json(result.errors); // Return errors with a 400 status code 41 | } 42 | return res.json(formatData(result.data)); 43 | } catch (err) { 44 | console.error('Error: ', err); 45 | return res.status(500).json({ error: 'Internal server error' }); // Return a 500 status code for server errors 46 | } 47 | }; 48 | 49 | export default fetchProblems; -------------------------------------------------------------------------------- /src/Controllers/fetchSingleProblem.ts: -------------------------------------------------------------------------------- 1 | import { Response } from 'express'; 2 | import { DailyProblemData, SelectProblemData } from '../types'; 3 | 4 | const fetchSingleProblem = async ( 5 | res: Response, 6 | formatData: (data: DailyProblemData & SelectProblemData) => void, 7 | query: string, 8 | titleSlug: string | null 9 | ) => { 10 | try { 11 | const response = await fetch('https://leetcode.com/graphql', { 12 | method: 'POST', 13 | headers: { 14 | 'Content-Type': 'application/json', 15 | Referer: 'https://leetcode.com', 16 | }, 17 | body: JSON.stringify({ 18 | query: query, 19 | variables: { 20 | titleSlug, //search question using titleSlug 21 | }, 22 | }), 23 | }); 24 | 25 | const result = await response.json(); 26 | 27 | if (result.errors) { 28 | return res.send(result); 29 | } 30 | 31 | return res.json(formatData(result.data)); 32 | } catch (err) { 33 | console.error('Error: ', err); 34 | return res.send(err); 35 | } 36 | }; 37 | 38 | export default fetchSingleProblem; 39 | -------------------------------------------------------------------------------- /src/Controllers/fetchUserDetails.ts: -------------------------------------------------------------------------------- 1 | import { Response } from 'express'; 2 | import { UserData } from '../types'; 3 | 4 | const fetchUserDetails = async ( 5 | options: { username: string; limit: number }, 6 | res: Response, 7 | formatData: (data: UserData) => {}, 8 | query: string 9 | ) => { 10 | try { 11 | const response = await fetch('https://leetcode.com/graphql', { 12 | method: 'POST', 13 | headers: { 14 | 'Content-Type': 'application/json', 15 | Referer: 'https://leetcode.com', 16 | }, 17 | body: JSON.stringify({ 18 | query: query, 19 | variables: { 20 | username: options.username, //username required 21 | limit: options.limit, //only for submission 22 | }, 23 | }), 24 | }); 25 | 26 | const result = await response.json(); 27 | 28 | if (result.errors) { 29 | return res.send(result); 30 | } 31 | 32 | return res.json(formatData(result.data)); 33 | } catch (err) { 34 | console.error('Error: ', err); 35 | return res.send(err); 36 | } 37 | }; 38 | 39 | export default fetchUserDetails; 40 | -------------------------------------------------------------------------------- /src/Controllers/index.ts: -------------------------------------------------------------------------------- 1 | export { default as fetchSingleProblem } from './fetchSingleProblem'; 2 | export { default as fetchProblems } from './fetchProblems'; 3 | export { default as fetchUserDetails } from './fetchUserDetails'; 4 | export { default as fetchTrendingTopics } from './fetchDiscussion'; 5 | export { default as fetchDataRawFormat } from './fetchDataRawFormat'; -------------------------------------------------------------------------------- /src/FormatUtils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './userData'; 2 | export * from './problemData'; 3 | export * from './trendingTopicData'; -------------------------------------------------------------------------------- /src/FormatUtils/problemData.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DailyProblemData, 3 | ProblemSetQuestionListData, 4 | SelectProblemData, 5 | } from '../types'; 6 | 7 | export const formatDailyData = (data: DailyProblemData) => ({ 8 | questionLink: 9 | `https://leetcode.com` + data.activeDailyCodingChallengeQuestion.link, 10 | date: data.activeDailyCodingChallengeQuestion.date, 11 | questionId: data.activeDailyCodingChallengeQuestion.question.questionId, 12 | questionFrontendId: 13 | data.activeDailyCodingChallengeQuestion.question.questionFrontendId, 14 | questionTitle: data.activeDailyCodingChallengeQuestion.question.title, 15 | titleSlug: data.activeDailyCodingChallengeQuestion.question.titleSlug, 16 | difficulty: data.activeDailyCodingChallengeQuestion.question.difficulty, 17 | isPaidOnly: data.activeDailyCodingChallengeQuestion.question.isPaidOnly, 18 | question: data.activeDailyCodingChallengeQuestion.question.content, 19 | exampleTestcases: 20 | data.activeDailyCodingChallengeQuestion.question.exampleTestcases, 21 | topicTags: data.activeDailyCodingChallengeQuestion.question.topicTags, 22 | hints: data.activeDailyCodingChallengeQuestion.question.hints, 23 | solution: data.activeDailyCodingChallengeQuestion.question.solution, 24 | companyTagStats: 25 | data.activeDailyCodingChallengeQuestion.question.companyTagStats, 26 | likes: data.activeDailyCodingChallengeQuestion.question.likes, 27 | dislikes: data.activeDailyCodingChallengeQuestion.question.dislikes, 28 | similarQuestions: 29 | data.activeDailyCodingChallengeQuestion.question.similarQuestions, 30 | }); 31 | 32 | export const formatQuestionData = (data: SelectProblemData) => ({ 33 | link: `https://leetcode.com/problems/` + data.question.titleSlug, 34 | questionId: data.question.questionId, 35 | questionFrontendId: data.question.questionFrontendId, 36 | questionTitle: data.question.title, 37 | titleSlug: data.question.titleSlug, 38 | difficulty: data.question.difficulty, 39 | isPaidOnly: data.question.isPaidOnly, 40 | question: data.question.content, 41 | exampleTestcases: data.question.exampleTestcases, 42 | topicTags: data.question.topicTags, 43 | hints: data.question.hints, 44 | solution: data.question.solution, 45 | companyTagStats: data.question.companyTagStats, 46 | likes: data.question.likes, 47 | dislikes: data.question.dislikes, 48 | similarQuestions: data.question.similarQuestions, 49 | }); 50 | 51 | export const formatProblemsData = (data: ProblemSetQuestionListData) => ({ 52 | totalQuestions: data.problemsetQuestionList.total, 53 | count: data.problemsetQuestionList.questions.length, 54 | problemsetQuestionList: data.problemsetQuestionList.questions, 55 | }); 56 | 57 | -------------------------------------------------------------------------------- /src/FormatUtils/trendingTopicData.ts: -------------------------------------------------------------------------------- 1 | import { TrendingDiscussionObject } from "../types"; 2 | 3 | export const formatTrendingCategoryTopicData = (data: TrendingDiscussionObject) => { 4 | // Not suggest reformat this data. as the orginial format are orginised in a way that is easy to understand 5 | return data 6 | }; -------------------------------------------------------------------------------- /src/FormatUtils/userData.ts: -------------------------------------------------------------------------------- 1 | import { UserData } from '../types'; 2 | 3 | export const formatUserData = (data: UserData) => ({ 4 | username: data.matchedUser.username, 5 | name: data.matchedUser.profile.realName, 6 | birthday: data.matchedUser.profile.birthday, 7 | avatar: data.matchedUser.profile.userAvatar, 8 | ranking: data.matchedUser.profile.ranking, 9 | reputation: data.matchedUser.profile.reputation, 10 | gitHub: data.matchedUser.githubUrl, 11 | twitter: data.matchedUser.twitterUrl, 12 | linkedIN: data.matchedUser.linkedinUrl, 13 | website: data.matchedUser.profile.websites, 14 | country: data.matchedUser.profile.countryName, 15 | company: data.matchedUser.profile.company, 16 | school: data.matchedUser.profile.school, 17 | skillTags: data.matchedUser.profile.skillTags, 18 | about: data.matchedUser.profile.aboutMe, 19 | }); 20 | 21 | export const formatBadgesData = (data: UserData) => ({ 22 | badgesCount: data.matchedUser.badges.length, 23 | badges: data.matchedUser.badges, 24 | upcomingBadges: data.matchedUser.upcomingBadges, 25 | activeBadge: data.matchedUser.activeBadge, 26 | }); 27 | 28 | export const formatContestData = (data: UserData) => ({ 29 | contestAttend: data.userContestRanking?.attendedContestsCount, 30 | contestRating: data.userContestRanking?.rating, 31 | contestGlobalRanking: data.userContestRanking?.globalRanking, 32 | totalParticipants: data.userContestRanking?.totalParticipants, 33 | contestTopPercentage: data.userContestRanking?.topPercentage, 34 | contestBadges: data.userContestRanking?.badge, 35 | contestParticipation: data.userContestRankingHistory.filter( 36 | (obj) => obj.attended === true 37 | ), 38 | }); 39 | 40 | export const formatContestHistoryData = (data: UserData) => ({ 41 | count: data.userContestRankingHistory.length, 42 | contestHistory: data.userContestRankingHistory, 43 | }); 44 | 45 | export const formatSolvedProblemsData = (data: UserData) => ({ 46 | solvedProblem: data.matchedUser.submitStats.acSubmissionNum[0].count, 47 | easySolved: data.matchedUser.submitStats.acSubmissionNum[1].count, 48 | mediumSolved: data.matchedUser.submitStats.acSubmissionNum[2].count, 49 | hardSolved: data.matchedUser.submitStats.acSubmissionNum[3].count, 50 | totalSubmissionNum: data.matchedUser.submitStats.totalSubmissionNum, 51 | acSubmissionNum: data.matchedUser.submitStats.acSubmissionNum, 52 | }); 53 | 54 | export const formatSubmissionData = (data: UserData) => ({ 55 | count: data.recentSubmissionList.length, 56 | submission: data.recentSubmissionList, 57 | }); 58 | 59 | export const formatAcSubmissionData = (data: UserData) => ({ 60 | count: data.recentAcSubmissionList.length, 61 | submission: data.recentAcSubmissionList, 62 | }); 63 | 64 | export const formatSubmissionCalendarData = (data: UserData) => ({ 65 | submissionCalendar: data.matchedUser.submissionCalendar, 66 | }); 67 | -------------------------------------------------------------------------------- /src/GQLQueries/contest.ts: -------------------------------------------------------------------------------- 1 | const query = `#graphql 2 | query getUserContestRanking ($username: String!) { 3 | userContestRanking(username: $username) { 4 | attendedContestsCount 5 | rating 6 | globalRanking 7 | totalParticipants 8 | topPercentage 9 | badge { 10 | name 11 | } 12 | } 13 | userContestRankingHistory(username: $username) { 14 | attended 15 | rating 16 | ranking 17 | trendDirection 18 | problemsSolved 19 | totalProblems 20 | finishTimeInSeconds 21 | contest { 22 | title 23 | startTime 24 | } 25 | } 26 | }`; 27 | 28 | export default query; 29 | -------------------------------------------------------------------------------- /src/GQLQueries/dailyProblem.ts: -------------------------------------------------------------------------------- 1 | const query = `#graphql 2 | query getDailyProblem { 3 | activeDailyCodingChallengeQuestion { 4 | date 5 | link 6 | question { 7 | questionId 8 | questionFrontendId 9 | boundTopicId 10 | title 11 | titleSlug 12 | content 13 | translatedTitle 14 | translatedContent 15 | isPaidOnly 16 | difficulty 17 | likes 18 | dislikes 19 | isLiked 20 | similarQuestions 21 | exampleTestcases 22 | contributors { 23 | username 24 | profileUrl 25 | avatarUrl 26 | } 27 | topicTags { 28 | name 29 | slug 30 | translatedName 31 | } 32 | companyTagStats 33 | codeSnippets { 34 | lang 35 | langSlug 36 | code 37 | } 38 | stats 39 | hints 40 | solution { 41 | id 42 | canSeeDetail 43 | paidOnly 44 | hasVideoSolution 45 | paidOnlyVideo 46 | } 47 | status 48 | sampleTestCase 49 | metaData 50 | judgerAvailable 51 | judgeType 52 | mysqlSchemas 53 | enableRunCode 54 | enableTestMode 55 | enableDebugger 56 | envInfo 57 | libraryUrl 58 | adminUrl 59 | challengeQuestion { 60 | id 61 | date 62 | incompleteChallengeCount 63 | streakCount 64 | type 65 | } 66 | note 67 | } 68 | } 69 | }`; 70 | 71 | export default query; 72 | -------------------------------------------------------------------------------- /src/GQLQueries/index.ts: -------------------------------------------------------------------------------- 1 | // import exp from 'constants'; 2 | 3 | export { default as AcSubmissionQuery } from './recentAcSubmit'; 4 | export { default as contestQuery } from './contest'; 5 | export { default as dailyProblemQuery } from './dailyProblem'; 6 | export { default as problemListQuery } from './problemList'; 7 | export { default as userProfileQuery } from './userProfile'; 8 | export { default as selectProblemQuery } from './selectProblem'; 9 | export { default as submissionQuery } from './recentSubmit'; 10 | export { default as trendingDiscussQuery } from './trendingDiscuss'; 11 | export { default as languageStatsQuery } from './languageStats'; -------------------------------------------------------------------------------- /src/GQLQueries/languageStats.ts: -------------------------------------------------------------------------------- 1 | const query = ` 2 | query languageStats($username: String!) { 3 | matchedUser(username: $username) { 4 | languageProblemCount { 5 | languageName 6 | problemsSolved 7 | } 8 | } 9 | } 10 | `; 11 | 12 | 13 | export default query; 14 | -------------------------------------------------------------------------------- /src/GQLQueries/newQueries.ts: -------------------------------------------------------------------------------- 1 | 2 | export const selectQuestion = ` 3 | query selectProblem($titleSlug: String!) { 4 | question(titleSlug: $titleSlug) { 5 | questionId 6 | questionFrontendId 7 | boundTopicId 8 | title 9 | titleSlug 10 | content 11 | translatedTitle 12 | translatedContent 13 | isPaidOnly 14 | difficulty 15 | likes 16 | dislikes 17 | isLiked 18 | similarQuestions 19 | exampleTestcases 20 | contributors { 21 | username 22 | profileUrl 23 | avatarUrl 24 | } 25 | topicTags { 26 | name 27 | slug 28 | translatedName 29 | } 30 | companyTagStats 31 | codeSnippets { 32 | lang 33 | langSlug 34 | code 35 | } 36 | stats 37 | hints 38 | solution { 39 | id 40 | canSeeDetail 41 | paidOnly 42 | hasVideoSolution 43 | paidOnlyVideo 44 | } 45 | status 46 | sampleTestCase 47 | metaData 48 | judgerAvailable 49 | judgeType 50 | mysqlSchemas 51 | enableRunCode 52 | enableTestMode 53 | enableDebugger 54 | envInfo 55 | libraryUrl 56 | adminUrl 57 | challengeQuestion { 58 | id 59 | date 60 | incompleteChallengeCount 61 | streakCount 62 | type 63 | } 64 | note 65 | } 66 | }` 67 | 68 | // queries.ts 69 | export const dailyQeustion = ` 70 | query getDailyProblem { 71 | activeDailyCodingChallengeQuestion { 72 | date 73 | link 74 | question { 75 | questionId 76 | questionFrontendId 77 | boundTopicId 78 | title 79 | titleSlug 80 | content 81 | translatedTitle 82 | translatedContent 83 | isPaidOnly 84 | difficulty 85 | likes 86 | dislikes 87 | isLiked 88 | similarQuestions 89 | exampleTestcases 90 | contributors { 91 | username 92 | profileUrl 93 | avatarUrl 94 | } 95 | topicTags { 96 | name 97 | slug 98 | translatedName 99 | } 100 | companyTagStats 101 | codeSnippets { 102 | lang 103 | langSlug 104 | code 105 | } 106 | stats 107 | hints 108 | solution { 109 | id 110 | canSeeDetail 111 | paidOnly 112 | hasVideoSolution 113 | paidOnlyVideo 114 | } 115 | status 116 | sampleTestCase 117 | metaData 118 | judgerAvailable 119 | judgeType 120 | mysqlSchemas 121 | enableRunCode 122 | enableTestMode 123 | enableDebugger 124 | envInfo 125 | libraryUrl 126 | adminUrl 127 | challengeQuestion { 128 | id 129 | date 130 | incompleteChallengeCount 131 | streakCount 132 | type 133 | } 134 | note 135 | } 136 | } 137 | } 138 | `; 139 | 140 | export const questionOfTodayQuery = ` 141 | query questionOfToday { 142 | activeDailyCodingChallengeQuestion { 143 | date 144 | userStatus 145 | link 146 | question { 147 | acRate 148 | difficulty 149 | freqBar 150 | frontendQuestionId: questionFrontendId 151 | isFavor 152 | paidOnly: isPaidOnly 153 | status 154 | title 155 | titleSlug 156 | hasVideoSolution 157 | hasSolution 158 | topicTags { 159 | name 160 | id 161 | slug 162 | } 163 | } 164 | } 165 | } 166 | `; 167 | 168 | 169 | export const skillStatsQuery = ` 170 | query skillStats($username: String!) { 171 | matchedUser(username: $username) { 172 | tagProblemCounts { 173 | advanced { 174 | tagName 175 | tagSlug 176 | problemsSolved 177 | } 178 | intermediate { 179 | tagName 180 | tagSlug 181 | problemsSolved 182 | } 183 | fundamental { 184 | tagName 185 | tagSlug 186 | problemsSolved 187 | } 188 | } 189 | } 190 | } 191 | `; 192 | 193 | 194 | // queries.ts 195 | export const getUserProfileQuery = ` 196 | query getUserProfile($username: String!) { 197 | allQuestionsCount { 198 | difficulty 199 | count 200 | } 201 | matchedUser(username: $username) { 202 | contributions { 203 | points 204 | } 205 | profile { 206 | reputation 207 | ranking 208 | } 209 | submissionCalendar 210 | submitStats { 211 | acSubmissionNum { 212 | difficulty 213 | count 214 | submissions 215 | } 216 | totalSubmissionNum { 217 | difficulty 218 | count 219 | submissions 220 | } 221 | } 222 | } 223 | recentSubmissionList(username: $username) { 224 | title 225 | titleSlug 226 | timestamp 227 | statusDisplay 228 | lang 229 | __typename 230 | } 231 | matchedUserStats: matchedUser(username: $username) { 232 | submitStats: submitStatsGlobal { 233 | acSubmissionNum { 234 | difficulty 235 | count 236 | submissions 237 | __typename 238 | } 239 | totalSubmissionNum { 240 | difficulty 241 | count 242 | submissions 243 | __typename 244 | } 245 | __typename 246 | } 247 | } 248 | } 249 | `; 250 | 251 | // queries.ts 252 | export const officialSolutionQuery = ` 253 | query OfficialSolution($titleSlug: String!) { 254 | question(titleSlug: $titleSlug) { 255 | solution { 256 | id 257 | title 258 | content 259 | contentTypeId 260 | paidOnly 261 | hasVideoSolution 262 | paidOnlyVideo 263 | canSeeDetail 264 | rating { 265 | count 266 | average 267 | userRating { 268 | score 269 | } 270 | } 271 | topic { 272 | id 273 | commentCount 274 | topLevelCommentCount 275 | viewCount 276 | subscribed 277 | solutionTags { 278 | name 279 | slug 280 | } 281 | post { 282 | id 283 | status 284 | creationDate 285 | author { 286 | username 287 | isActive 288 | profile { 289 | userAvatar 290 | reputation 291 | } 292 | } 293 | } 294 | } 295 | } 296 | } 297 | } 298 | `; 299 | 300 | 301 | 302 | // queries.ts 303 | export const userProfileCalendarQuery = ` 304 | query UserProfileCalendar($username: String!, $year: Int!) { 305 | matchedUser(username: $username) { 306 | userCalendar(year: $year) { 307 | activeYears 308 | streak 309 | totalActiveDays 310 | dccBadges { 311 | timestamp 312 | badge { 313 | name 314 | icon 315 | } 316 | } 317 | submissionCalendar 318 | } 319 | } 320 | } 321 | `; 322 | 323 | // queries.ts 324 | export const userProfileUserQuestionProgressV2Query = ` 325 | query userProfileUserQuestionProgressV2($userSlug: String!) { 326 | userProfileUserQuestionProgressV2(userSlug: $userSlug) { 327 | numAcceptedQuestions { 328 | count 329 | difficulty 330 | } 331 | numFailedQuestions { 332 | count 333 | difficulty 334 | } 335 | numUntouchedQuestions { 336 | count 337 | difficulty 338 | } 339 | userSessionBeatsPercentage { 340 | difficulty 341 | percentage 342 | } 343 | } 344 | } 345 | `; 346 | 347 | 348 | export const userContestRankingInfoQuery = ` 349 | query userContestRankingInfo($username: String!) { 350 | userContestRanking(username: $username) { 351 | attendedContestsCount 352 | rating 353 | globalRanking 354 | totalParticipants 355 | topPercentage 356 | badge { 357 | name 358 | } 359 | } 360 | userContestRankingHistory(username: $username) { 361 | attended 362 | trendDirection 363 | problemsSolved 364 | totalProblems 365 | finishTimeInSeconds 366 | rating 367 | ranking 368 | contest { 369 | title 370 | startTime 371 | } 372 | } 373 | } 374 | `; 375 | 376 | export const discussCommentsQuery = ` 377 | query discussComments($topicId: Int!, $orderBy: String = "newest_to_oldest", $pageNo: Int = 1, $numPerPage: Int = 10) { 378 | topicComments(topicId: $topicId, orderBy: $orderBy, pageNo: $pageNo, numPerPage: $numPerPage) { 379 | data { 380 | id 381 | pinned 382 | pinnedBy { 383 | username 384 | } 385 | post { 386 | ...DiscussPost 387 | } 388 | numChildren 389 | } 390 | } 391 | } 392 | 393 | fragment DiscussPost on PostNode { 394 | id 395 | voteCount 396 | voteStatus 397 | content 398 | updationDate 399 | creationDate 400 | status 401 | isHidden 402 | coinRewards { 403 | ...CoinReward 404 | } 405 | author { 406 | isDiscussAdmin 407 | isDiscussStaff 408 | username 409 | nameColor 410 | activeBadge { 411 | displayName 412 | icon 413 | } 414 | profile { 415 | userAvatar 416 | reputation 417 | } 418 | isActive 419 | } 420 | authorIsModerator 421 | isOwnPost 422 | } 423 | 424 | fragment CoinReward on ScoreNode { 425 | id 426 | score 427 | description 428 | date 429 | } 430 | `; 431 | 432 | export const discussTopicQuery = ` 433 | query DiscussTopic($topicId: Int!) { 434 | topic(id: $topicId) { 435 | id 436 | viewCount 437 | topLevelCommentCount 438 | subscribed 439 | title 440 | pinned 441 | tags 442 | hideFromTrending 443 | post { 444 | ...DiscussPost 445 | } 446 | } 447 | } 448 | 449 | fragment DiscussPost on PostNode { 450 | id 451 | voteCount 452 | voteStatus 453 | content 454 | updationDate 455 | creationDate 456 | status 457 | isHidden 458 | coinRewards { 459 | ...CoinReward 460 | } 461 | author { 462 | isDiscussAdmin 463 | isDiscussStaff 464 | username 465 | nameColor 466 | activeBadge { 467 | displayName 468 | icon 469 | } 470 | profile { 471 | userAvatar 472 | reputation 473 | } 474 | isActive 475 | } 476 | authorIsModerator 477 | isOwnPost 478 | } 479 | 480 | fragment CoinReward on ScoreNode { 481 | id 482 | score 483 | description 484 | date 485 | } 486 | `; 487 | -------------------------------------------------------------------------------- /src/GQLQueries/problemList.ts: -------------------------------------------------------------------------------- 1 | const query = `#graphql 2 | query getProblems($categorySlug: String, $limit: Int, $skip: Int, $filters: QuestionListFilterInput) { 3 | problemsetQuestionList: questionList( 4 | categorySlug: $categorySlug 5 | limit: $limit 6 | skip: $skip 7 | filters: $filters 8 | ) { 9 | total: totalNum 10 | questions: data { 11 | acRate 12 | difficulty 13 | freqBar 14 | questionFrontendId 15 | isFavor 16 | isPaidOnly 17 | status 18 | title 19 | titleSlug 20 | topicTags { 21 | name 22 | id 23 | slug 24 | } 25 | hasSolution 26 | hasVideoSolution 27 | } 28 | } 29 | }`; 30 | 31 | export default query; 32 | -------------------------------------------------------------------------------- /src/GQLQueries/recentAcSubmit.ts: -------------------------------------------------------------------------------- 1 | const query = `#graphql 2 | query getACSubmissions ($username: String!, $limit: Int) { 3 | recentAcSubmissionList(username: $username, limit: $limit) { 4 | title 5 | titleSlug 6 | timestamp 7 | statusDisplay 8 | lang 9 | } 10 | }`; 11 | 12 | export default query; 13 | -------------------------------------------------------------------------------- /src/GQLQueries/recentSubmit.ts: -------------------------------------------------------------------------------- 1 | const query = `#graphql 2 | query getRecentSubmissions($username: String!, $limit: Int) { 3 | recentSubmissionList(username: $username, limit: $limit) { 4 | title 5 | titleSlug 6 | timestamp 7 | statusDisplay 8 | lang 9 | } 10 | }`; 11 | 12 | export default query; 13 | -------------------------------------------------------------------------------- /src/GQLQueries/selectProblem.ts: -------------------------------------------------------------------------------- 1 | const query = `#graphql 2 | query selectProblem($titleSlug: String!) { 3 | question(titleSlug: $titleSlug) { 4 | questionId 5 | questionFrontendId 6 | boundTopicId 7 | title 8 | titleSlug 9 | content 10 | translatedTitle 11 | translatedContent 12 | isPaidOnly 13 | difficulty 14 | likes 15 | dislikes 16 | isLiked 17 | similarQuestions 18 | exampleTestcases 19 | contributors { 20 | username 21 | profileUrl 22 | avatarUrl 23 | } 24 | topicTags { 25 | name 26 | slug 27 | translatedName 28 | } 29 | companyTagStats 30 | codeSnippets { 31 | lang 32 | langSlug 33 | code 34 | } 35 | stats 36 | hints 37 | solution { 38 | id 39 | canSeeDetail 40 | paidOnly 41 | hasVideoSolution 42 | paidOnlyVideo 43 | } 44 | status 45 | sampleTestCase 46 | metaData 47 | judgerAvailable 48 | judgeType 49 | mysqlSchemas 50 | enableRunCode 51 | enableTestMode 52 | enableDebugger 53 | envInfo 54 | libraryUrl 55 | adminUrl 56 | challengeQuestion { 57 | id 58 | date 59 | incompleteChallengeCount 60 | streakCount 61 | type 62 | } 63 | note 64 | } 65 | }`; 66 | 67 | export default query; 68 | -------------------------------------------------------------------------------- /src/GQLQueries/trendingDiscuss.ts: -------------------------------------------------------------------------------- 1 | const query = ` 2 | query trendingDiscuss($first: Int!) { 3 | cachedTrendingCategoryTopics(first: $first) { 4 | id 5 | title 6 | post { 7 | id 8 | creationDate 9 | contentPreview 10 | author { 11 | username 12 | isActive 13 | profile { 14 | userAvatar 15 | } 16 | } 17 | } 18 | } 19 | } 20 | `; 21 | 22 | export default query; 23 | -------------------------------------------------------------------------------- /src/GQLQueries/userProfile.ts: -------------------------------------------------------------------------------- 1 | const query = `#graphql 2 | query getUserProfile($username: String!) { 3 | allQuestionsCount { 4 | difficulty 5 | count 6 | } 7 | matchedUser(username: $username) { 8 | username 9 | githubUrl 10 | twitterUrl 11 | linkedinUrl 12 | contributions { 13 | points 14 | questionCount 15 | testcaseCount 16 | } 17 | profile { 18 | realName 19 | userAvatar 20 | birthday 21 | ranking 22 | reputation 23 | websites 24 | countryName 25 | company 26 | school 27 | skillTags 28 | aboutMe 29 | starRating 30 | } 31 | badges { 32 | id 33 | displayName 34 | icon 35 | creationDate 36 | } 37 | upcomingBadges { 38 | name 39 | icon 40 | } 41 | activeBadge { 42 | id 43 | displayName 44 | icon 45 | creationDate 46 | } 47 | submitStats { 48 | totalSubmissionNum { 49 | difficulty 50 | count 51 | submissions 52 | } 53 | acSubmissionNum { 54 | difficulty 55 | count 56 | submissions 57 | } 58 | } 59 | submissionCalendar 60 | } 61 | recentSubmissionList(username: $username, limit: 20) { 62 | title 63 | titleSlug 64 | timestamp 65 | statusDisplay 66 | lang 67 | } 68 | }`; 69 | 70 | export default query; 71 | -------------------------------------------------------------------------------- /src/__tests__/msw/handlers.ts: -------------------------------------------------------------------------------- 1 | import * as msw from 'msw'; 2 | import { 3 | singleUser, 4 | singleUserContests, 5 | recentSubmissions, 6 | recentACSubmissions, 7 | dailyProblem, 8 | problems, 9 | selectProblem, 10 | } from './mockData'; 11 | 12 | export const handlers = [ 13 | msw.http.post('https://leetcode.com/graphql', async (ctx) => { 14 | const test = await ctx.request.json(); 15 | const typed = test as { query: string }; 16 | if (typed.query.indexOf('getUserProfile') !== -1) { 17 | return msw.HttpResponse.json(singleUser); 18 | } 19 | 20 | if (typed.query.indexOf('getUserContestRanking') !== -1) { 21 | return msw.HttpResponse.json(singleUserContests); 22 | } 23 | 24 | if (typed.query.indexOf('getRecentSubmissions') !== -1) { 25 | return msw.HttpResponse.json(recentSubmissions); 26 | } 27 | 28 | if (typed.query.indexOf('getACSubmissions') !== -1) { 29 | return msw.HttpResponse.json(recentACSubmissions); 30 | } 31 | 32 | if (typed.query.indexOf('getDailyProblem') !== -1) { 33 | return msw.HttpResponse.json(dailyProblem); 34 | } 35 | 36 | if (typed.query.indexOf('getProblems') !== -1) { 37 | return msw.HttpResponse.json(problems); 38 | } 39 | 40 | if (typed.query.indexOf('selectProblem') !== -1) { 41 | return msw.HttpResponse.json(selectProblem); 42 | } 43 | 44 | return msw.HttpResponse.json({}); 45 | }), 46 | ]; 47 | -------------------------------------------------------------------------------- /src/__tests__/msw/index.ts: -------------------------------------------------------------------------------- 1 | import { setupServer } from 'msw/node'; 2 | import { handlers } from './handlers'; 3 | 4 | export const server = setupServer(...handlers); 5 | -------------------------------------------------------------------------------- /src/__tests__/msw/mockData/dailyProblem.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "activeDailyCodingChallengeQuestion": { 4 | "date": "2024-03-14", 5 | "link": "/problems/binary-subarrays-with-sum/", 6 | "question": { 7 | "questionId": "966", 8 | "questionFrontendId": "930", 9 | "boundTopicId": null, 10 | "title": "Binary Subarrays With Sum", 11 | "titleSlug": "binary-subarrays-with-sum", 12 | "content": "

Given a binary array nums and an integer goal, return the number of non-empty subarrays with a sum goal.

\r\n\r\n

A subarray is a contiguous part of the array.

\r\n\r\n

 

\r\n

Example 1:

\r\n\r\n
\r\nInput: nums = [1,0,1,0,1], goal = 2\r\nOutput: 4\r\nExplanation: The 4 subarrays are bolded and underlined below:\r\n[1,0,1,0,1]\r\n[1,0,1,0,1]\r\n[1,0,1,0,1]\r\n[1,0,1,0,1]\r\n
\r\n\r\n

Example 2:

\r\n\r\n
\r\nInput: nums = [0,0,0,0,0], goal = 0\r\nOutput: 15\r\n
\r\n\r\n

 

\r\n

Constraints:

\r\n\r\n
    \r\n\t
  • 1 <= nums.length <= 3 * 104
  • \r\n\t
  • nums[i] is either 0 or 1.
  • \r\n\t
  • 0 <= goal <= nums.length
  • \r\n
", 13 | "translatedTitle": null, 14 | "translatedContent": null, 15 | "isPaidOnly": false, 16 | "difficulty": "Medium", 17 | "likes": 3592, 18 | "dislikes": 120, 19 | "isLiked": null, 20 | "similarQuestions": "[{\"title\": \"Count Subarrays With Score Less Than K\", \"titleSlug\": \"count-subarrays-with-score-less-than-k\", \"difficulty\": \"Hard\", \"translatedTitle\": null}, {\"title\": \"Ways to Split Array Into Good Subarrays\", \"titleSlug\": \"ways-to-split-array-into-good-subarrays\", \"difficulty\": \"Medium\", \"translatedTitle\": null}]", 21 | "exampleTestcases": "[1,0,1,0,1]\n2\n[0,0,0,0,0]\n0", 22 | "contributors": [], 23 | "topicTags": [ 24 | { "name": "Array", "slug": "array", "translatedName": null }, 25 | { 26 | "name": "Hash Table", 27 | "slug": "hash-table", 28 | "translatedName": null 29 | }, 30 | { 31 | "name": "Sliding Window", 32 | "slug": "sliding-window", 33 | "translatedName": null 34 | }, 35 | { "name": "Prefix Sum", "slug": "prefix-sum", "translatedName": null } 36 | ], 37 | "companyTagStats": null, 38 | "codeSnippets": [ 39 | { 40 | "lang": "C++", 41 | "langSlug": "cpp", 42 | "code": "class Solution {\r\npublic:\r\n int numSubarraysWithSum(vector& nums, int goal) {\r\n \r\n }\r\n};" 43 | }, 44 | { 45 | "lang": "Java", 46 | "langSlug": "java", 47 | "code": "class Solution {\r\n public int numSubarraysWithSum(int[] nums, int goal) {\r\n \r\n }\r\n}" 48 | }, 49 | { 50 | "lang": "Python", 51 | "langSlug": "python", 52 | "code": "class Solution(object):\r\n def numSubarraysWithSum(self, nums, goal):\r\n \"\"\"\r\n :type nums: List[int]\r\n :type goal: int\r\n :rtype: int\r\n \"\"\"" 53 | }, 54 | { 55 | "lang": "Python3", 56 | "langSlug": "python3", 57 | "code": "class Solution:\r\n def numSubarraysWithSum(self, nums: List[int], goal: int) -> int:" 58 | }, 59 | { 60 | "lang": "C", 61 | "langSlug": "c", 62 | "code": "int numSubarraysWithSum(int* nums, int numsSize, int goal){\r\n\r\n}" 63 | }, 64 | { 65 | "lang": "C#", 66 | "langSlug": "csharp", 67 | "code": "public class Solution {\r\n public int NumSubarraysWithSum(int[] nums, int goal) {\r\n \r\n }\r\n}" 68 | }, 69 | { 70 | "lang": "JavaScript", 71 | "langSlug": "javascript", 72 | "code": "/**\r\n * @param {number[]} nums\r\n * @param {number} goal\r\n * @return {number}\r\n */\r\nvar numSubarraysWithSum = function(nums, goal) {\r\n \r\n};" 73 | }, 74 | { 75 | "lang": "TypeScript", 76 | "langSlug": "typescript", 77 | "code": "function numSubarraysWithSum(nums: number[], goal: number): number {\r\n\r\n};" 78 | }, 79 | { 80 | "lang": "PHP", 81 | "langSlug": "php", 82 | "code": "class Solution {\r\n\r\n /**\r\n * @param Integer[] $nums\r\n * @param Integer $goal\r\n * @return Integer\r\n */\r\n function numSubarraysWithSum($nums, $goal) {\r\n \r\n }\r\n}" 83 | }, 84 | { 85 | "lang": "Swift", 86 | "langSlug": "swift", 87 | "code": "class Solution {\r\n func numSubarraysWithSum(_ nums: [Int], _ goal: Int) -> Int {\r\n \r\n }\r\n}" 88 | }, 89 | { 90 | "lang": "Kotlin", 91 | "langSlug": "kotlin", 92 | "code": "class Solution {\r\n fun numSubarraysWithSum(nums: IntArray, goal: Int): Int {\r\n \r\n }\r\n}" 93 | }, 94 | { 95 | "lang": "Go", 96 | "langSlug": "golang", 97 | "code": "func numSubarraysWithSum(nums []int, goal int) int {\r\n \r\n}" 98 | }, 99 | { 100 | "lang": "Ruby", 101 | "langSlug": "ruby", 102 | "code": "# @param {Integer[]} nums\r\n# @param {Integer} goal\r\n# @return {Integer}\r\ndef num_subarrays_with_sum(nums, goal)\r\n \r\nend" 103 | }, 104 | { 105 | "lang": "Scala", 106 | "langSlug": "scala", 107 | "code": "object Solution {\r\n def numSubarraysWithSum(nums: Array[Int], goal: Int): Int = {\r\n \r\n }\r\n}" 108 | }, 109 | { 110 | "lang": "Rust", 111 | "langSlug": "rust", 112 | "code": "impl Solution {\r\n pub fn num_subarrays_with_sum(nums: Vec, goal: i32) -> i32 {\r\n \r\n }\r\n}" 113 | }, 114 | { 115 | "lang": "Racket", 116 | "langSlug": "racket", 117 | "code": "(define/contract (num-subarrays-with-sum nums goal)\r\n (-> (listof exact-integer?) exact-integer? exact-integer?)\r\n\r\n )" 118 | } 119 | ], 120 | "stats": "{\"totalAccepted\": \"203.9K\", \"totalSubmission\": \"330.4K\", \"totalAcceptedRaw\": 203936, \"totalSubmissionRaw\": 330374, \"acRate\": \"61.7%\"}", 121 | "hints": [], 122 | "solution": { 123 | "id": "2266", 124 | "canSeeDetail": true, 125 | "paidOnly": false, 126 | "hasVideoSolution": false, 127 | "paidOnlyVideo": true 128 | }, 129 | "status": null, 130 | "sampleTestCase": "[1,0,1,0,1]\n2", 131 | "metaData": "{\n \"name\": \"numSubarraysWithSum\",\n \"params\": [\n {\n \"name\": \"nums\",\n \"type\": \"integer[]\"\n },\n {\n \"name\": \"goal\",\n \"type\": \"integer\"\n }\n ],\n \"return\": {\n \"type\": \"integer\"\n }\n}", 132 | "judgerAvailable": true, 133 | "judgeType": "large", 134 | "mysqlSchemas": [], 135 | "enableRunCode": true, 136 | "enableTestMode": false, 137 | "enableDebugger": true, 138 | "envInfo": "{\"cpp\": [\"C++\", \"

Compiled with clang 17 using the latest C++ 20 standard, and libstdc++ provided by GCC 11.

\\r\\n\\r\\n

Your code is compiled with level two optimization (-O2). AddressSanitizer is also enabled to help detect out-of-bounds and use-after-free bugs.

\\r\\n\\r\\n

Most standard library headers are already included automatically for your convenience.

\"], \"java\": [\"Java\", \"

OpenJDK 21. Using compile arguments: --enable-preview --release 21

\\r\\n\\r\\n

Most standard library headers are already included automatically for your convenience.

\\r\\n

Includes Pair class from https://docs.oracle.com/javase/8/javafx/api/javafx/util/Pair.html.

\"], \"python\": [\"Python\", \"

Python 2.7.12.

\\r\\n\\r\\n

Most libraries are already imported automatically for your convenience, such as array, bisect, collections. If you need more libraries, you can import it yourself.

\\r\\n\\r\\n

For Map/TreeMap data structure, you may use sortedcontainers library.

\\r\\n\\r\\n

Note that Python 2.7 will not be maintained past 2020. For the latest Python, please choose Python3 instead.

\"], \"c\": [\"C\", \"

Compiled with gcc 11 using the gnu11 standard.

\\r\\n\\r\\n

Your code is compiled with level one optimization (-O2). AddressSanitizer is also enabled to help detect out-of-bounds and use-after-free bugs.

\\r\\n\\r\\n

Most standard library headers are already included automatically for your convenience.

\\r\\n\\r\\n

For hash table operations, you may use uthash. \\\"uthash.h\\\" is included by default. Below are some examples:

\\r\\n\\r\\n

1. Adding an item to a hash.\\r\\n

\\r\\nstruct hash_entry {\\r\\n    int id;            /* we'll use this field as the key */\\r\\n    char name[10];\\r\\n    UT_hash_handle hh; /* makes this structure hashable */\\r\\n};\\r\\n\\r\\nstruct hash_entry *users = NULL;\\r\\n\\r\\nvoid add_user(struct hash_entry *s) {\\r\\n    HASH_ADD_INT(users, id, s);\\r\\n}\\r\\n
\\r\\n

\\r\\n\\r\\n

2. Looking up an item in a hash:\\r\\n

\\r\\nstruct hash_entry *find_user(int user_id) {\\r\\n    struct hash_entry *s;\\r\\n    HASH_FIND_INT(users, &user_id, s);\\r\\n    return s;\\r\\n}\\r\\n
\\r\\n

\\r\\n\\r\\n

3. Deleting an item in a hash:\\r\\n

\\r\\nvoid delete_user(struct hash_entry *user) {\\r\\n    HASH_DEL(users, user);  \\r\\n}\\r\\n
\\r\\n

\"], \"csharp\": [\"C#\", \"

C# 12 with .NET 8 runtime

\"], \"javascript\": [\"JavaScript\", \"

Node.js 20.10.0.

\\r\\n\\r\\n

Your code is run with --harmony flag, enabling new ES6 features.

\\r\\n\\r\\n

lodash.js library is included by default.

\\r\\n\\r\\n

For Priority Queue / Queue data structures, you may use 5.4.0 version of datastructures-js/priority-queue and 4.2.3 version of datastructures-js/queue.

\"], \"ruby\": [\"Ruby\", \"

Ruby 3.2

\\r\\n\\r\\n

Some common data structure implementations are provided in the Algorithms module: https://www.rubydoc.info/github/kanwei/algorithms/Algorithms

\"], \"swift\": [\"Swift\", \"

Swift 5.9.

\\r\\n\\r\\n

You may use swift-algorithms 1.2.0 and swift-collections 1.0.6.

\"], \"golang\": [\"Go\", \"

Go 1.21

\\r\\n

Support https://godoc.org/github.com/emirpasic/gods@v1.18.1 library.

\"], \"python3\": [\"Python3\", \"

Python 3.11.

\\r\\n\\r\\n

Most libraries are already imported automatically for your convenience, such as array, bisect, collections. If you need more libraries, you can import it yourself.

\\r\\n\\r\\n

For Map/TreeMap data structure, you may use sortedcontainers library.

\"], \"scala\": [\"Scala\", \"

Scala 3.3.1.

\"], \"kotlin\": [\"Kotlin\", \"

Kotlin 1.9.0.

\\r\\n\\r\\n

We are using an experimental compiler provided by JetBrains.

\"], \"rust\": [\"Rust\", \"

Rust 1.74.1

\\r\\n\\r\\n

Supports rand v0.6\\u00a0from crates.io

\"], \"php\": [\"PHP\", \"

PHP 8.2.

\\r\\n

With bcmath module

\"], \"typescript\": [\"Typescript\", \"

TypeScript 5.1.6, Node.js 20.10.0.

\\r\\n\\r\\n

Compile Options: --alwaysStrict --strictBindCallApply --strictFunctionTypes --target ES2022

\\r\\n\\r\\n

Your code is run with --harmony flag, enabling new ES2022 features.

\\r\\n\\r\\n

lodash.js library is included by default.

\"], \"racket\": [\"Racket\", \"

Racket CS v8.11

\\r\\n\\r\\n

Using #lang racket

\\r\\n\\r\\n

Required data/gvector data/queue data/order data/heap automatically for your convenience

\"]}", 139 | "libraryUrl": null, 140 | "adminUrl": null, 141 | "challengeQuestion": { 142 | "id": "1692", 143 | "date": "2024-03-14", 144 | "incompleteChallengeCount": 0, 145 | "streakCount": 0, 146 | "type": "DAILY" 147 | }, 148 | "note": null 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/__tests__/msw/mockData/index.ts: -------------------------------------------------------------------------------- 1 | export { default as singleUser } from './singleUser.json'; 2 | export { default as recentSubmissions } from './recentSubmissions.json'; 3 | export { default as recentACSubmissions } from './recentAcSubmissionList.json'; 4 | export { default as singleUserContests } from './singleUserContests.json'; 5 | export { default as dailyProblem } from './dailyProblem.json'; 6 | export { default as problems } from './problems.json'; 7 | export { default as selectProblem } from './selectProblem.json'; 8 | -------------------------------------------------------------------------------- /src/__tests__/msw/mockData/problems.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "problemsetQuestionList": { 4 | "total": 3078, 5 | "questions": [ 6 | { 7 | "acRate": 52.09288098160903, 8 | "difficulty": "Easy", 9 | "freqBar": null, 10 | "questionFrontendId": "1", 11 | "isFavor": false, 12 | "isPaidOnly": false, 13 | "status": null, 14 | "title": "Two Sum", 15 | "titleSlug": "two-sum", 16 | "topicTags": [ 17 | { "name": "Array", "id": "VG9waWNUYWdOb2RlOjU=", "slug": "array" }, 18 | { 19 | "name": "Hash Table", 20 | "id": "VG9waWNUYWdOb2RlOjY=", 21 | "slug": "hash-table" 22 | } 23 | ], 24 | "hasSolution": true, 25 | "hasVideoSolution": true 26 | } 27 | ] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/__tests__/msw/mockData/recentAcSubmissionList.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "recentAcSubmissionList": [ 4 | { 5 | "title": "Generate a String With Characters That Have Odd Counts", 6 | "titleSlug": "generate-a-string-with-characters-that-have-odd-counts", 7 | "timestamp": "1705161771", 8 | "statusDisplay": "Accepted", 9 | "lang": "javascript" 10 | }, 11 | { 12 | "title": "Count Negative Numbers in a Sorted Matrix", 13 | "titleSlug": "count-negative-numbers-in-a-sorted-matrix", 14 | "timestamp": "1705161438", 15 | "statusDisplay": "Accepted", 16 | "lang": "javascript" 17 | }, 18 | { 19 | "title": "Check If N and Its Double Exist", 20 | "titleSlug": "check-if-n-and-its-double-exist", 21 | "timestamp": "1705161205", 22 | "statusDisplay": "Accepted", 23 | "lang": "javascript" 24 | }, 25 | { 26 | "title": "Determine if String Halves Are Alike", 27 | "titleSlug": "determine-if-string-halves-are-alike", 28 | "timestamp": "1705032551", 29 | "statusDisplay": "Accepted", 30 | "lang": "javascript" 31 | }, 32 | { 33 | "title": "Minimum Number of Operations to Make Array Empty", 34 | "titleSlug": "minimum-number-of-operations-to-make-array-empty", 35 | "timestamp": "1704343666", 36 | "statusDisplay": "Accepted", 37 | "lang": "javascript" 38 | }, 39 | { 40 | "title": "Sort Array By Parity", 41 | "titleSlug": "sort-array-by-parity", 42 | "timestamp": "1704316936", 43 | "statusDisplay": "Accepted", 44 | "lang": "javascript" 45 | }, 46 | { 47 | "title": "Convert an Array Into a 2D Array With Conditions", 48 | "titleSlug": "convert-an-array-into-a-2d-array-with-conditions", 49 | "timestamp": "1704261617", 50 | "statusDisplay": "Accepted", 51 | "lang": "javascript" 52 | }, 53 | { 54 | "title": "Average Value of Even Numbers That Are Divisible by Three", 55 | "titleSlug": "average-value-of-even-numbers-that-are-divisible-by-three", 56 | "timestamp": "1704259967", 57 | "statusDisplay": "Accepted", 58 | "lang": "javascript" 59 | }, 60 | { 61 | "title": "Number of Laser Beams in a Bank", 62 | "titleSlug": "number-of-laser-beams-in-a-bank", 63 | "timestamp": "1704256409", 64 | "statusDisplay": "Accepted", 65 | "lang": "javascript" 66 | }, 67 | { 68 | "title": "Take Gifts From the Richest Pile", 69 | "titleSlug": "take-gifts-from-the-richest-pile", 70 | "timestamp": "1704252901", 71 | "statusDisplay": "Accepted", 72 | "lang": "javascript" 73 | }, 74 | { 75 | "title": "Decompress Run-Length Encoded List", 76 | "titleSlug": "decompress-run-length-encoded-list", 77 | "timestamp": "1704236706", 78 | "statusDisplay": "Accepted", 79 | "lang": "javascript" 80 | }, 81 | { 82 | "title": "Make Array Zero by Subtracting Equal Amounts", 83 | "titleSlug": "make-array-zero-by-subtracting-equal-amounts", 84 | "timestamp": "1704234483", 85 | "statusDisplay": "Accepted", 86 | "lang": "javascript" 87 | }, 88 | { 89 | "title": "Remove One Element to Make the Array Strictly Increasing", 90 | "titleSlug": "remove-one-element-to-make-the-array-strictly-increasing", 91 | "timestamp": "1704228445", 92 | "statusDisplay": "Accepted", 93 | "lang": "javascript" 94 | }, 95 | { 96 | "title": "Check if All A's Appears Before All B's", 97 | "titleSlug": "check-if-all-as-appears-before-all-bs", 98 | "timestamp": "1704223378", 99 | "statusDisplay": "Accepted", 100 | "lang": "javascript" 101 | }, 102 | { 103 | "title": "Contains Duplicate II", 104 | "titleSlug": "contains-duplicate-ii", 105 | "timestamp": "1704222499", 106 | "statusDisplay": "Accepted", 107 | "lang": "javascript" 108 | }, 109 | { 110 | "title": "Pascal's Triangle", 111 | "titleSlug": "pascals-triangle", 112 | "timestamp": "1704219156", 113 | "statusDisplay": "Accepted", 114 | "lang": "javascript" 115 | }, 116 | { 117 | "title": "Make Array Zero by Subtracting Equal Amounts", 118 | "titleSlug": "make-array-zero-by-subtracting-equal-amounts", 119 | "timestamp": "1704216984", 120 | "statusDisplay": "Accepted", 121 | "lang": "javascript" 122 | }, 123 | { 124 | "title": "Apply Transform Over Each Element in Array", 125 | "titleSlug": "apply-transform-over-each-element-in-array", 126 | "timestamp": "1704215794", 127 | "statusDisplay": "Accepted", 128 | "lang": "javascript" 129 | }, 130 | { 131 | "title": "Find the Difference", 132 | "titleSlug": "find-the-difference", 133 | "timestamp": "1702492842", 134 | "statusDisplay": "Accepted", 135 | "lang": "javascript" 136 | }, 137 | { 138 | "title": "Monotonic Array", 139 | "titleSlug": "monotonic-array", 140 | "timestamp": "1702417451", 141 | "statusDisplay": "Accepted", 142 | "lang": "javascript" 143 | } 144 | ] 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/__tests__/msw/mockData/recentSubmissions.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "recentSubmissionList": [ 4 | { 5 | "title": "Generate a String With Characters That Have Odd Counts", 6 | "titleSlug": "generate-a-string-with-characters-that-have-odd-counts", 7 | "timestamp": "1705161771", 8 | "statusDisplay": "Accepted", 9 | "lang": "javascript" 10 | }, 11 | { 12 | "title": "Count Negative Numbers in a Sorted Matrix", 13 | "titleSlug": "count-negative-numbers-in-a-sorted-matrix", 14 | "timestamp": "1705161438", 15 | "statusDisplay": "Accepted", 16 | "lang": "javascript" 17 | }, 18 | { 19 | "title": "Count Negative Numbers in a Sorted Matrix", 20 | "titleSlug": "count-negative-numbers-in-a-sorted-matrix", 21 | "timestamp": "1705161424", 22 | "statusDisplay": "Runtime Error", 23 | "lang": "javascript" 24 | }, 25 | { 26 | "title": "Check If N and Its Double Exist", 27 | "titleSlug": "check-if-n-and-its-double-exist", 28 | "timestamp": "1705161205", 29 | "statusDisplay": "Accepted", 30 | "lang": "javascript" 31 | }, 32 | { 33 | "title": "Check If N and Its Double Exist", 34 | "titleSlug": "check-if-n-and-its-double-exist", 35 | "timestamp": "1705161123", 36 | "statusDisplay": "Wrong Answer", 37 | "lang": "javascript" 38 | }, 39 | { 40 | "title": "Minimum Number of Steps to Make Two Strings Anagram", 41 | "titleSlug": "minimum-number-of-steps-to-make-two-strings-anagram", 42 | "timestamp": "1705160304", 43 | "statusDisplay": "Wrong Answer", 44 | "lang": "javascript" 45 | }, 46 | { 47 | "title": "Determine if String Halves Are Alike", 48 | "titleSlug": "determine-if-string-halves-are-alike", 49 | "timestamp": "1705032551", 50 | "statusDisplay": "Accepted", 51 | "lang": "javascript" 52 | }, 53 | { 54 | "title": "Longest Increasing Subsequence", 55 | "titleSlug": "longest-increasing-subsequence", 56 | "timestamp": "1704442672", 57 | "statusDisplay": "Wrong Answer", 58 | "lang": "javascript" 59 | }, 60 | { 61 | "title": "Longest Increasing Subsequence", 62 | "titleSlug": "longest-increasing-subsequence", 63 | "timestamp": "1704440871", 64 | "statusDisplay": "Wrong Answer", 65 | "lang": "javascript" 66 | }, 67 | { 68 | "title": "Longest Increasing Subsequence", 69 | "titleSlug": "longest-increasing-subsequence", 70 | "timestamp": "1704439843", 71 | "statusDisplay": "Wrong Answer", 72 | "lang": "javascript" 73 | }, 74 | { 75 | "title": "Minimum Number of Operations to Make Array Empty", 76 | "titleSlug": "minimum-number-of-operations-to-make-array-empty", 77 | "timestamp": "1704343666", 78 | "statusDisplay": "Accepted", 79 | "lang": "javascript" 80 | }, 81 | { 82 | "title": "Minimum Number of Operations to Make Array Empty", 83 | "titleSlug": "minimum-number-of-operations-to-make-array-empty", 84 | "timestamp": "1704343585", 85 | "statusDisplay": "Wrong Answer", 86 | "lang": "javascript" 87 | }, 88 | { 89 | "title": "Minimum Number of Operations to Make Array Empty", 90 | "titleSlug": "minimum-number-of-operations-to-make-array-empty", 91 | "timestamp": "1704343426", 92 | "statusDisplay": "Wrong Answer", 93 | "lang": "javascript" 94 | }, 95 | { 96 | "title": "Minimum Number of Operations to Make Array Empty", 97 | "titleSlug": "minimum-number-of-operations-to-make-array-empty", 98 | "timestamp": "1704343413", 99 | "statusDisplay": "Runtime Error", 100 | "lang": "javascript" 101 | }, 102 | { 103 | "title": "Minimum Number of Operations to Make Array Empty", 104 | "titleSlug": "minimum-number-of-operations-to-make-array-empty", 105 | "timestamp": "1704343387", 106 | "statusDisplay": "Runtime Error", 107 | "lang": "javascript" 108 | }, 109 | { 110 | "title": "Sort Array By Parity", 111 | "titleSlug": "sort-array-by-parity", 112 | "timestamp": "1704316936", 113 | "statusDisplay": "Accepted", 114 | "lang": "javascript" 115 | }, 116 | { 117 | "title": "Convert an Array Into a 2D Array With Conditions", 118 | "titleSlug": "convert-an-array-into-a-2d-array-with-conditions", 119 | "timestamp": "1704261617", 120 | "statusDisplay": "Accepted", 121 | "lang": "javascript" 122 | }, 123 | { 124 | "title": "Convert an Array Into a 2D Array With Conditions", 125 | "titleSlug": "convert-an-array-into-a-2d-array-with-conditions", 126 | "timestamp": "1704261566", 127 | "statusDisplay": "Accepted", 128 | "lang": "javascript" 129 | }, 130 | { 131 | "title": "Convert an Array Into a 2D Array With Conditions", 132 | "titleSlug": "convert-an-array-into-a-2d-array-with-conditions", 133 | "timestamp": "1704261530", 134 | "statusDisplay": "Accepted", 135 | "lang": "javascript" 136 | }, 137 | { 138 | "title": "Average Value of Even Numbers That Are Divisible by Three", 139 | "titleSlug": "average-value-of-even-numbers-that-are-divisible-by-three", 140 | "timestamp": "1704259967", 141 | "statusDisplay": "Accepted", 142 | "lang": "javascript" 143 | } 144 | ] 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/__tests__/msw/mockData/selectProblem.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "question": { 4 | "questionId": "1", 5 | "questionFrontendId": "1", 6 | "boundTopicId": null, 7 | "title": "Two Sum", 8 | "titleSlug": "two-sum", 9 | "content": "

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.

\n\n

You may assume that each input would have exactly one solution, and you may not use the same element twice.

\n\n

You can return the answer in any order.

\n\n

 

\n

Example 1:

\n\n
\nInput: nums = [2,7,11,15], target = 9\nOutput: [0,1]\nExplanation: Because nums[0] + nums[1] == 9, we return [0, 1].\n
\n\n

Example 2:

\n\n
\nInput: nums = [3,2,4], target = 6\nOutput: [1,2]\n
\n\n

Example 3:

\n\n
\nInput: nums = [3,3], target = 6\nOutput: [0,1]\n
\n\n

 

\n

Constraints:

\n\n
    \n\t
  • 2 <= nums.length <= 104
  • \n\t
  • -109 <= nums[i] <= 109
  • \n\t
  • -109 <= target <= 109
  • \n\t
  • Only one valid answer exists.
  • \n
\n\n

 

\nFollow-up: Can you come up with an algorithm that is less than O(n2) time complexity?", 10 | "translatedTitle": null, 11 | "translatedContent": null, 12 | "isPaidOnly": false, 13 | "difficulty": "Easy", 14 | "likes": 55371, 15 | "dislikes": 1901, 16 | "isLiked": null, 17 | "similarQuestions": "[{\"title\": \"3Sum\", \"titleSlug\": \"3sum\", \"difficulty\": \"Medium\", \"translatedTitle\": null}, {\"title\": \"4Sum\", \"titleSlug\": \"4sum\", \"difficulty\": \"Medium\", \"translatedTitle\": null}, {\"title\": \"Two Sum II - Input Array Is Sorted\", \"titleSlug\": \"two-sum-ii-input-array-is-sorted\", \"difficulty\": \"Medium\", \"translatedTitle\": null}, {\"title\": \"Two Sum III - Data structure design\", \"titleSlug\": \"two-sum-iii-data-structure-design\", \"difficulty\": \"Easy\", \"translatedTitle\": null}, {\"title\": \"Subarray Sum Equals K\", \"titleSlug\": \"subarray-sum-equals-k\", \"difficulty\": \"Medium\", \"translatedTitle\": null}, {\"title\": \"Two Sum IV - Input is a BST\", \"titleSlug\": \"two-sum-iv-input-is-a-bst\", \"difficulty\": \"Easy\", \"translatedTitle\": null}, {\"title\": \"Two Sum Less Than K\", \"titleSlug\": \"two-sum-less-than-k\", \"difficulty\": \"Easy\", \"translatedTitle\": null}, {\"title\": \"Max Number of K-Sum Pairs\", \"titleSlug\": \"max-number-of-k-sum-pairs\", \"difficulty\": \"Medium\", \"translatedTitle\": null}, {\"title\": \"Count Good Meals\", \"titleSlug\": \"count-good-meals\", \"difficulty\": \"Medium\", \"translatedTitle\": null}, {\"title\": \"Count Number of Pairs With Absolute Difference K\", \"titleSlug\": \"count-number-of-pairs-with-absolute-difference-k\", \"difficulty\": \"Easy\", \"translatedTitle\": null}, {\"title\": \"Number of Pairs of Strings With Concatenation Equal to Target\", \"titleSlug\": \"number-of-pairs-of-strings-with-concatenation-equal-to-target\", \"difficulty\": \"Medium\", \"translatedTitle\": null}, {\"title\": \"Find All K-Distant Indices in an Array\", \"titleSlug\": \"find-all-k-distant-indices-in-an-array\", \"difficulty\": \"Easy\", \"translatedTitle\": null}, {\"title\": \"First Letter to Appear Twice\", \"titleSlug\": \"first-letter-to-appear-twice\", \"difficulty\": \"Easy\", \"translatedTitle\": null}, {\"title\": \"Number of Excellent Pairs\", \"titleSlug\": \"number-of-excellent-pairs\", \"difficulty\": \"Hard\", \"translatedTitle\": null}, {\"title\": \"Number of Arithmetic Triplets\", \"titleSlug\": \"number-of-arithmetic-triplets\", \"difficulty\": \"Easy\", \"translatedTitle\": null}, {\"title\": \"Node With Highest Edge Score\", \"titleSlug\": \"node-with-highest-edge-score\", \"difficulty\": \"Medium\", \"translatedTitle\": null}, {\"title\": \"Check Distances Between Same Letters\", \"titleSlug\": \"check-distances-between-same-letters\", \"difficulty\": \"Easy\", \"translatedTitle\": null}, {\"title\": \"Find Subarrays With Equal Sum\", \"titleSlug\": \"find-subarrays-with-equal-sum\", \"difficulty\": \"Easy\", \"translatedTitle\": null}, {\"title\": \"Largest Positive Integer That Exists With Its Negative\", \"titleSlug\": \"largest-positive-integer-that-exists-with-its-negative\", \"difficulty\": \"Easy\", \"translatedTitle\": null}, {\"title\": \"Number of Distinct Averages\", \"titleSlug\": \"number-of-distinct-averages\", \"difficulty\": \"Easy\", \"translatedTitle\": null}, {\"title\": \"Count Pairs Whose Sum is Less than Target\", \"titleSlug\": \"count-pairs-whose-sum-is-less-than-target\", \"difficulty\": \"Easy\", \"translatedTitle\": null}]", 18 | "exampleTestcases": "[2,7,11,15]\n9\n[3,2,4]\n6\n[3,3]\n6", 19 | "contributors": [], 20 | "topicTags": [ 21 | { "name": "Array", "slug": "array", "translatedName": null }, 22 | { "name": "Hash Table", "slug": "hash-table", "translatedName": null } 23 | ], 24 | "companyTagStats": null, 25 | "codeSnippets": [ 26 | { 27 | "lang": "C++", 28 | "langSlug": "cpp", 29 | "code": "class Solution {\npublic:\n vector twoSum(vector& nums, int target) {\n \n }\n};" 30 | }, 31 | { 32 | "lang": "Java", 33 | "langSlug": "java", 34 | "code": "class Solution {\n public int[] twoSum(int[] nums, int target) {\n \n }\n}" 35 | }, 36 | { 37 | "lang": "Python", 38 | "langSlug": "python", 39 | "code": "class Solution(object):\n def twoSum(self, nums, target):\n \"\"\"\n :type nums: List[int]\n :type target: int\n :rtype: List[int]\n \"\"\"\n " 40 | }, 41 | { 42 | "lang": "Python3", 43 | "langSlug": "python3", 44 | "code": "class Solution:\n def twoSum(self, nums: List[int], target: int) -> List[int]:\n " 45 | }, 46 | { 47 | "lang": "C", 48 | "langSlug": "c", 49 | "code": "/**\n * Note: The returned array must be malloced, assume caller calls free().\n */\nint* twoSum(int* nums, int numsSize, int target, int* returnSize) {\n \n}" 50 | }, 51 | { 52 | "lang": "C#", 53 | "langSlug": "csharp", 54 | "code": "public class Solution {\n public int[] TwoSum(int[] nums, int target) {\n \n }\n}" 55 | }, 56 | { 57 | "lang": "JavaScript", 58 | "langSlug": "javascript", 59 | "code": "/**\n * @param {number[]} nums\n * @param {number} target\n * @return {number[]}\n */\nvar twoSum = function(nums, target) {\n \n};" 60 | }, 61 | { 62 | "lang": "TypeScript", 63 | "langSlug": "typescript", 64 | "code": "function twoSum(nums: number[], target: number): number[] {\n \n};" 65 | }, 66 | { 67 | "lang": "PHP", 68 | "langSlug": "php", 69 | "code": "class Solution {\n\n /**\n * @param Integer[] $nums\n * @param Integer $target\n * @return Integer[]\n */\n function twoSum($nums, $target) {\n \n }\n}" 70 | }, 71 | { 72 | "lang": "Swift", 73 | "langSlug": "swift", 74 | "code": "class Solution {\n func twoSum(_ nums: [Int], _ target: Int) -> [Int] {\n \n }\n}" 75 | }, 76 | { 77 | "lang": "Kotlin", 78 | "langSlug": "kotlin", 79 | "code": "class Solution {\n fun twoSum(nums: IntArray, target: Int): IntArray {\n \n }\n}" 80 | }, 81 | { 82 | "lang": "Dart", 83 | "langSlug": "dart", 84 | "code": "class Solution {\n List twoSum(List nums, int target) {\n \n }\n}" 85 | }, 86 | { 87 | "lang": "Go", 88 | "langSlug": "golang", 89 | "code": "func twoSum(nums []int, target int) []int {\n \n}" 90 | }, 91 | { 92 | "lang": "Ruby", 93 | "langSlug": "ruby", 94 | "code": "# @param {Integer[]} nums\n# @param {Integer} target\n# @return {Integer[]}\ndef two_sum(nums, target)\n \nend" 95 | }, 96 | { 97 | "lang": "Scala", 98 | "langSlug": "scala", 99 | "code": "object Solution {\n def twoSum(nums: Array[Int], target: Int): Array[Int] = {\n \n }\n}" 100 | }, 101 | { 102 | "lang": "Rust", 103 | "langSlug": "rust", 104 | "code": "impl Solution {\n pub fn two_sum(nums: Vec, target: i32) -> Vec {\n \n }\n}" 105 | }, 106 | { 107 | "lang": "Racket", 108 | "langSlug": "racket", 109 | "code": "(define/contract (two-sum nums target)\n (-> (listof exact-integer?) exact-integer? (listof exact-integer?))\n )" 110 | }, 111 | { 112 | "lang": "Erlang", 113 | "langSlug": "erlang", 114 | "code": "-spec two_sum(Nums :: [integer()], Target :: integer()) -> [integer()].\ntwo_sum(Nums, Target) ->\n ." 115 | }, 116 | { 117 | "lang": "Elixir", 118 | "langSlug": "elixir", 119 | "code": "defmodule Solution do\n @spec two_sum(nums :: [integer], target :: integer) :: [integer]\n def two_sum(nums, target) do\n \n end\nend" 120 | } 121 | ], 122 | "stats": "{\"totalAccepted\": \"12.6M\", \"totalSubmission\": \"24.1M\", \"totalAcceptedRaw\": 12566379, \"totalSubmissionRaw\": 24123031, \"acRate\": \"52.1%\"}", 123 | "hints": [ 124 | "A really brute force way would be to search for all possible pairs of numbers but that would be too slow. Again, it's best to try out brute force solutions for just for completeness. It is from these brute force solutions that you can come up with optimizations.", 125 | "So, if we fix one of the numbers, say x, we have to scan the entire array to find the next number y which is value - x where value is the input parameter. Can we change our array somehow so that this search becomes faster?", 126 | "The second train of thought is, without changing the array, can we use additional space somehow? Like maybe a hash map to speed up the search?" 127 | ], 128 | "solution": { 129 | "id": "7", 130 | "canSeeDetail": true, 131 | "paidOnly": false, 132 | "hasVideoSolution": true, 133 | "paidOnlyVideo": false 134 | }, 135 | "status": null, 136 | "sampleTestCase": "[2,7,11,15]\n9", 137 | "metaData": "{\n \"name\": \"twoSum\",\n \"params\": [\n {\n \"name\": \"nums\",\n \"type\": \"integer[]\"\n },\n {\n \"name\": \"target\",\n \"type\": \"integer\"\n }\n ],\n \"return\": {\n \"type\": \"integer[]\",\n \"size\": 2\n },\n \"manual\": false\n}", 138 | "judgerAvailable": true, 139 | "judgeType": "small", 140 | "mysqlSchemas": [], 141 | "enableRunCode": true, 142 | "enableTestMode": false, 143 | "enableDebugger": true, 144 | "envInfo": "{\"cpp\": [\"C++\", \"

Compiled with clang 17 using the latest C++ 20 standard, and libstdc++ provided by GCC 11.

\\r\\n\\r\\n

Your code is compiled with level two optimization (-O2). AddressSanitizer is also enabled to help detect out-of-bounds and use-after-free bugs.

\\r\\n\\r\\n

Most standard library headers are already included automatically for your convenience.

\"], \"java\": [\"Java\", \"

OpenJDK 21. Using compile arguments: --enable-preview --release 21

\\r\\n\\r\\n

Most standard library headers are already included automatically for your convenience.

\\r\\n

Includes Pair class from https://docs.oracle.com/javase/8/javafx/api/javafx/util/Pair.html.

\"], \"python\": [\"Python\", \"

Python 2.7.12.

\\r\\n\\r\\n

Most libraries are already imported automatically for your convenience, such as array, bisect, collections. If you need more libraries, you can import it yourself.

\\r\\n\\r\\n

For Map/TreeMap data structure, you may use sortedcontainers library.

\\r\\n\\r\\n

Note that Python 2.7 will not be maintained past 2020. For the latest Python, please choose Python3 instead.

\"], \"c\": [\"C\", \"

Compiled with gcc 11 using the gnu11 standard.

\\r\\n\\r\\n

Your code is compiled with level one optimization (-O2). AddressSanitizer is also enabled to help detect out-of-bounds and use-after-free bugs.

\\r\\n\\r\\n

Most standard library headers are already included automatically for your convenience.

\\r\\n\\r\\n

For hash table operations, you may use uthash. \\\"uthash.h\\\" is included by default. Below are some examples:

\\r\\n\\r\\n

1. Adding an item to a hash.\\r\\n

\\r\\nstruct hash_entry {\\r\\n    int id;            /* we'll use this field as the key */\\r\\n    char name[10];\\r\\n    UT_hash_handle hh; /* makes this structure hashable */\\r\\n};\\r\\n\\r\\nstruct hash_entry *users = NULL;\\r\\n\\r\\nvoid add_user(struct hash_entry *s) {\\r\\n    HASH_ADD_INT(users, id, s);\\r\\n}\\r\\n
\\r\\n

\\r\\n\\r\\n

2. Looking up an item in a hash:\\r\\n

\\r\\nstruct hash_entry *find_user(int user_id) {\\r\\n    struct hash_entry *s;\\r\\n    HASH_FIND_INT(users, &user_id, s);\\r\\n    return s;\\r\\n}\\r\\n
\\r\\n

\\r\\n\\r\\n

3. Deleting an item in a hash:\\r\\n

\\r\\nvoid delete_user(struct hash_entry *user) {\\r\\n    HASH_DEL(users, user);  \\r\\n}\\r\\n
\\r\\n

\"], \"csharp\": [\"C#\", \"

C# 12 with .NET 8 runtime

\"], \"javascript\": [\"JavaScript\", \"

Node.js 20.10.0.

\\r\\n\\r\\n

Your code is run with --harmony flag, enabling new ES6 features.

\\r\\n\\r\\n

lodash.js library is included by default.

\\r\\n\\r\\n

For Priority Queue / Queue data structures, you may use 5.4.0 version of datastructures-js/priority-queue and 4.2.3 version of datastructures-js/queue.

\"], \"ruby\": [\"Ruby\", \"

Ruby 3.2

\\r\\n\\r\\n

Some common data structure implementations are provided in the Algorithms module: https://www.rubydoc.info/github/kanwei/algorithms/Algorithms

\"], \"swift\": [\"Swift\", \"

Swift 5.9.

\\r\\n\\r\\n

You may use swift-algorithms 1.2.0 and swift-collections 1.0.6.

\"], \"golang\": [\"Go\", \"

Go 1.21

\\r\\n

Support https://godoc.org/github.com/emirpasic/gods@v1.18.1 library.

\"], \"python3\": [\"Python3\", \"

Python 3.11.

\\r\\n\\r\\n

Most libraries are already imported automatically for your convenience, such as array, bisect, collections. If you need more libraries, you can import it yourself.

\\r\\n\\r\\n

For Map/TreeMap data structure, you may use sortedcontainers library.

\"], \"scala\": [\"Scala\", \"

Scala 3.3.1.

\"], \"kotlin\": [\"Kotlin\", \"

Kotlin 1.9.0.

\\r\\n\\r\\n

We are using an experimental compiler provided by JetBrains.

\"], \"rust\": [\"Rust\", \"

Rust 1.74.1

\\r\\n\\r\\n

Supports rand v0.6\\u00a0from crates.io

\"], \"php\": [\"PHP\", \"

PHP 8.2.

\\r\\n

With bcmath module

\"], \"typescript\": [\"Typescript\", \"

TypeScript 5.1.6, Node.js 20.10.0.

\\r\\n\\r\\n

Compile Options: --alwaysStrict --strictBindCallApply --strictFunctionTypes --target ES2022

\\r\\n\\r\\n

Your code is run with --harmony flag, enabling new ES2022 features.

\\r\\n\\r\\n

lodash.js library is included by default.

\"], \"racket\": [\"Racket\", \"

Racket CS v8.11

\\r\\n\\r\\n

Using #lang racket

\\r\\n\\r\\n

Required data/gvector data/queue data/order data/heap automatically for your convenience

\"], \"erlang\": [\"Erlang\", \"Erlang/OTP 26\"], \"elixir\": [\"Elixir\", \"Elixir 1.15 with Erlang/OTP 26\"], \"dart\": [\"Dart\", \"

Dart 3.2

\\r\\n\\r\\n

Your code will be run directly without compiling

\"]}", 145 | "libraryUrl": null, 146 | "adminUrl": null, 147 | "challengeQuestion": null, 148 | "note": null 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/__tests__/msw/mockData/singleUser.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "matchedUser": { 4 | "submissionCalendar": "{\"1704153600\": 24, \"1704240000\": 9, \"1704326400\": 5, \"1704412800\": 3, \"1705017600\": 1, \"1705104000\": 6, \"1699315200\": 46, \"1701302400\": 6, \"1701648000\": 24, \"1701734400\": 11, \"1702339200\": 29, \"1702425600\": 7}", 5 | "submitStats": { 6 | "totalSubmissionNum": [ 7 | { "difficulty": "All", "count": 158, "submissions": 463 }, 8 | { "difficulty": "Easy", "count": 131, "submissions": 388 }, 9 | { "difficulty": "Medium", "count": 27, "submissions": 75 }, 10 | { "difficulty": "Hard", "count": 0, "submissions": 0 } 11 | ], 12 | "acSubmissionNum": [ 13 | { "difficulty": "All", "count": 133, "submissions": 215 }, 14 | { "difficulty": "Easy", "count": 122, "submissions": 202 }, 15 | { "difficulty": "Medium", "count": 11, "submissions": 13 }, 16 | { "difficulty": "Hard", "count": 0, "submissions": 0 } 17 | ] 18 | }, 19 | "username": "jambobjones", 20 | "githubUrl": "https://github.com/jambobjones", 21 | "twitterUrl": null, 22 | "linkedinUrl": null, 23 | "contributions": { 24 | "points": 115, 25 | "questionCount": 0, 26 | "testcaseCount": 0 27 | }, 28 | "profile": { 29 | "realName": "Jambob Jones", 30 | "userAvatar": "https://assets.leetcode.com/users/jambobjones/avatar_1617850141.png", 31 | "birthday": null, 32 | "ranking": 630800, 33 | "reputation": 0, 34 | "websites": [], 35 | "countryName": null, 36 | "company": null, 37 | "school": null, 38 | "skillTags": [], 39 | "aboutMe": "", 40 | "starRating": 2 41 | }, 42 | "badges": [], 43 | "upcomingBadges": [ 44 | { 45 | "name": "Mar LeetCoding Challenge", 46 | "icon": "/static/images/badges/dcc-2024-3.png" 47 | } 48 | ], 49 | "activeBadge": null 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/__tests__/msw/mockData/singleUserContests.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "userContestRanking": null, 4 | "userContestRankingHistory": [ 5 | { 6 | "attended": false, 7 | "rating": 1500, 8 | "ranking": 0, 9 | "trendDirection": "NONE", 10 | "problemsSolved": 0, 11 | "totalProblems": 3, 12 | "finishTimeInSeconds": 0 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/__tests__/problemData.spec.ts: -------------------------------------------------------------------------------- 1 | import request from 'supertest'; 2 | import assert from 'assert'; 3 | import app from '../app'; 4 | 5 | describe('Problem Data Tests', () => { 6 | it('Should fetch the daily problem', async () => { 7 | const response = await request(app).get('/daily'); 8 | [ 9 | 'questionLink', 10 | 'date', 11 | 'questionId', 12 | 'questionFrontendId', 13 | 'questionTitle', 14 | 'titleSlug', 15 | 'difficulty', 16 | 'isPaidOnly', 17 | 'question', 18 | 'exampleTestcases', 19 | 'topicTags', 20 | 'hints', 21 | 'solution', 22 | 'companyTagStats', 23 | 'likes', 24 | 'dislikes', 25 | 'similarQuestions', 26 | ].forEach((key) => { 27 | assert(key in response.body); 28 | }); 29 | }); 30 | 31 | it('Should fetch a list of problems', async () => { 32 | const response = await request(app).get('/problems'); 33 | ['totalQuestions', 'count', 'problemsetQuestionList'].forEach((key) => { 34 | assert(key in response.body); 35 | }); 36 | }); 37 | 38 | it('Should Select a Problem', async () => { 39 | const response = await request(app).get('/select?titleSlug=two-sum'); 40 | [ 41 | 'link', 42 | 'questionId', 43 | 'questionFrontendId', 44 | 'questionTitle', 45 | 'titleSlug', 46 | 'difficulty', 47 | 'isPaidOnly', 48 | 'question', 49 | 'exampleTestcases', 50 | 'topicTags', 51 | 'hints', 52 | 'solution', 53 | 'companyTagStats', 54 | 'likes', 55 | 'dislikes', 56 | 'similarQuestions', 57 | ].forEach((key) => assert(key in response.body)); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /src/__tests__/userData.spec.ts: -------------------------------------------------------------------------------- 1 | import request from 'supertest'; 2 | import app from '../app'; 3 | import assert from 'assert'; 4 | 5 | describe('User Data Tests', () => { 6 | it('should fetch a single user', async () => { 7 | const response = await request(app).get('/jambobjones'); 8 | expect(response.body.username).toBe('jambobjones'); 9 | [ 10 | 'username', 11 | 'name', 12 | 'birthday', 13 | 'avatar', 14 | 'ranking', 15 | 'reputation', 16 | 'gitHub', 17 | 'twitter', 18 | 'linkedIN', 19 | 'website', 20 | 'country', 21 | 'company', 22 | 'school', 23 | 'skillTags', 24 | 'about', 25 | ].forEach((key) => { 26 | assert(key in response.body); 27 | }); 28 | }); 29 | 30 | it('should fetch user badges', async () => { 31 | const response = await request(app).get('/jambobjones/badges'); 32 | 33 | ['badgesCount', 'badges', 'upcomingBadges', 'activeBadge'].forEach( 34 | (key) => { 35 | assert(key in response.body); 36 | } 37 | ); 38 | }); 39 | 40 | it('Should fetch users solved problems', async () => { 41 | const response = await request(app).get('/jambobjones/solved'); 42 | [ 43 | 'solvedProblem', 44 | 'easySolved', 45 | 'mediumSolved', 46 | 'hardSolved', 47 | 'totalSubmissionNum', 48 | 'acSubmissionNum', 49 | ].forEach((key) => { 50 | assert(key in response.body); 51 | }); 52 | }); 53 | 54 | it('Should fetch users contests', async () => { 55 | const response = await request(app).get('/jambobjones/contest'); 56 | ['contestParticipation'].forEach((key) => { 57 | assert(key in response.body); 58 | }); 59 | }); 60 | 61 | it('Should fetch user contest history', async () => { 62 | const response = await request(app).get('/jambobjones/contest/history'); 63 | ['count', 'contestHistory'].forEach((key) => { 64 | assert(key in response.body); 65 | }); 66 | }); 67 | 68 | it('Should fetch users recent submissions returning 20 by default', async () => { 69 | const response = await request(app).get('/jambobjones/submission'); 70 | ['count', 'submission'].forEach((key) => { 71 | assert(key in response.body); 72 | }); 73 | 74 | expect(response.body.count).toBeLessThanOrEqual(20); 75 | }); 76 | // Todo: Submission test with Limit Parameter 77 | 78 | it('Should fetch AC Submissions', async () => { 79 | const response = await request(app).get('/jambobjones/acSubmission'); 80 | 81 | ['count', 'submission'].forEach((key) => { 82 | assert(key in response.body); 83 | }); 84 | expect(response.body.count).toBeLessThanOrEqual(20); 85 | }); 86 | 87 | it('Should fetch Users Submission Calendar', async () => { 88 | const response = await request(app).get('/jambobjones/calendar'); 89 | assert('submissionCalendar' in response.body); 90 | expect(typeof response.body.submissionCalendar).toBe('string'); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import express, { NextFunction, Response } from 'express'; 2 | import cors from 'cors'; 3 | import rateLimit from 'express-rate-limit'; 4 | import * as leetcode from './leetCode'; 5 | import { FetchUserDataRequest } from './types'; 6 | import apicache from 'apicache'; 7 | import axios from 'axios'; 8 | import { 9 | userContestRankingInfoQuery, 10 | discussCommentsQuery, 11 | discussTopicQuery, 12 | userProfileUserQuestionProgressV2Query, 13 | skillStatsQuery, 14 | getUserProfileQuery, 15 | userProfileCalendarQuery, 16 | officialSolutionQuery, 17 | dailyQeustion, 18 | } from './GQLQueries/newQueries'; 19 | 20 | const app = express(); 21 | let cache = apicache.middleware; 22 | const API_URL = process.env.LEETCODE_API_URL || 'https://leetcode.com/graphql'; 23 | 24 | const limiter = rateLimit({ 25 | windowMs: 60 * 60 * 1000, // 1 hour 26 | limit: 60, 27 | standardHeaders: 'draft-7', 28 | legacyHeaders: false, 29 | message: 'Too many request from this IP, try again in 1 hour', 30 | }); 31 | 32 | app.use(cache('5 minutes')); 33 | app.use(cors()); //enable all CORS request 34 | app.use(limiter); //limit to all API 35 | app.use((req: express.Request, _res: Response, next: NextFunction) => { 36 | console.log('Requested URL:', req.originalUrl); 37 | next(); 38 | }); 39 | 40 | async function queryLeetCodeAPI(query: string, variables: any) { 41 | try { 42 | const response = await axios.post(API_URL, { query, variables }); 43 | if (response.data.errors) { 44 | throw new Error(response.data.errors[0].message); 45 | } 46 | return response.data; 47 | } catch (error) { 48 | if (error.response) { 49 | throw new Error(`Error from LeetCode API: ${error.response.data}`); 50 | } else if (error.request) { 51 | throw new Error('No response received from LeetCode API'); 52 | } else { 53 | throw new Error(`Error in setting up the request: ${error.message}`); 54 | } 55 | } 56 | } 57 | 58 | app.get('/', (_req, res) => { 59 | res.json({ 60 | apiOverview: 61 | 'Welcome to the Alfa-Leetcode-API! Alfa-Leetcode-Api is a custom solution born out of the need for a well-documented and detailed LeetCode API. This project is designed to provide developers with endpoints that offer insights into a user"s profile, badges, solved questions, contest details, contest history, submissions, and also daily questions, selected problem, list of problems.', 62 | apiEndpointsLink: 63 | 'https://github.com/alfaarghya/alfa-leetcode-api?tab=readme-ov-file#endpoints-', 64 | routes: { 65 | userDetails: { 66 | description: 67 | 'Endpoints for retrieving detailed user profile information on Leetcode.', 68 | Method: 'GET', 69 | '/:username': 'Get your leetcodevis profile Details', 70 | '/:username/badges': 'Get your badges', 71 | '/:username/solved': 'Get total number of question you solved', 72 | '/:username/contest': 'Get your contest details', 73 | '/:username/contest/history': 'Get all contest history', 74 | '/:username/submission': 'Get your last 20 submission', 75 | '/:username/acSubmission': 'Get your last 20 accepted submission', 76 | '/:username/calendar': 'Get your submission calendar', 77 | '/userProfile/:username': 'Get full profile details in one call', 78 | '/userProfileCalendar?username=yourname&year=2024': 79 | 'Get your calendar details with year', 80 | '/languageStats?username=yourname': 'Get the language stats of a user', 81 | '/userProfileUserQuestionProgressV2/:userSlug': 82 | 'Get your question progress', 83 | '/skillStats/:username': 'Get your skill stats', 84 | }, 85 | contest: { 86 | description: 87 | 'Endpoints for retrieving contest ranking and performance data.', 88 | Method: 'GET', 89 | '/userContestRankingInfo/:username': 'Get user contest ranking info', 90 | }, 91 | discussion: { 92 | description: 'Endpoints for fetching discussion topics and comments.', 93 | Method: 'GET', 94 | '/trendingDiscuss?first=20': 'Get top 20 trending discussions', 95 | '/discussTopic/:topicId': 'Get discussion topic', 96 | '/discussComments/:topicId': 'Get discussion comments', 97 | }, 98 | problems: { 99 | description: 100 | 'Endpoints for fetching problem-related data, including lists, details, and solutions.', 101 | Method: 'GET', 102 | singleProblem: { 103 | '/select?titleSlug=two-sum': 'Get selected Problem', 104 | '/daily': 'Get daily Problem', 105 | '/dailyQuestion': 'Get raw daily question', 106 | }, 107 | problemList: { 108 | '/problems': 'Get list of 20 problems', 109 | '/problems?limit=50': 'Get list of some problems', 110 | '/problems?tags=array+math': 'Get list problems on selected topics', 111 | '/problems?tags=array+math+string&limit=5': 112 | 'Get list some problems on selected topics', 113 | '/officialSolution?titleSlug=two-sum': 114 | 'Get official solution of selected problem', 115 | }, 116 | }, 117 | }, 118 | }); 119 | }); 120 | 121 | app.get('/officialSolution', async (req, res) => { 122 | const { titleSlug } = req.query; 123 | 124 | if (!titleSlug) { 125 | return res.status(400).json({ error: 'Missing titleSlug query parameter' }); 126 | } 127 | try { 128 | const data = await queryLeetCodeAPI(officialSolutionQuery, { titleSlug }); 129 | return res.json(data); 130 | } catch (error) { 131 | return res.status(500).json({ error: error.message }); 132 | } 133 | }); 134 | 135 | app.get('/userProfileCalendar', async (req, res) => { 136 | const { username, year } = req.query; 137 | 138 | if (!username || !year || typeof year !== 'string') { 139 | return res 140 | .status(400) 141 | .json({ error: 'Missing or invalid username or year query parameter' }); 142 | } 143 | 144 | try { 145 | const data = await queryLeetCodeAPI(userProfileCalendarQuery, { 146 | username, 147 | year: parseInt(year), 148 | }); 149 | return res.json(data); 150 | } catch (error) { 151 | return res.status(500).json({ error: error.message }); 152 | } 153 | }); 154 | 155 | // Format data 156 | const formatData = (data: any) => { 157 | return { 158 | totalSolved: data.matchedUser.submitStats.acSubmissionNum[0].count, 159 | totalSubmissions: data.matchedUser.submitStats.totalSubmissionNum, 160 | totalQuestions: data.allQuestionsCount[0].count, 161 | easySolved: data.matchedUser.submitStats.acSubmissionNum[1].count, 162 | totalEasy: data.allQuestionsCount[1].count, 163 | mediumSolved: data.matchedUser.submitStats.acSubmissionNum[2].count, 164 | totalMedium: data.allQuestionsCount[2].count, 165 | hardSolved: data.matchedUser.submitStats.acSubmissionNum[3].count, 166 | totalHard: data.allQuestionsCount[3].count, 167 | ranking: data.matchedUser.profile.ranking, 168 | contributionPoint: data.matchedUser.contributions.points, 169 | reputation: data.matchedUser.profile.reputation, 170 | submissionCalendar: JSON.parse(data.matchedUser.submissionCalendar), 171 | recentSubmissions: data.recentSubmissionList, 172 | matchedUserStats: data.matchedUser.submitStats, 173 | }; 174 | }; 175 | 176 | app.get('/userProfile/:id', async (req, res) => { 177 | const user = req.params.id; 178 | 179 | try { 180 | const data = await queryLeetCodeAPI(getUserProfileQuery, { 181 | username: user, 182 | }); 183 | if (data.errors) { 184 | res.send(data); 185 | } else { 186 | res.send(formatData(data.data)); 187 | } 188 | } catch (error) { 189 | res.send(error); 190 | } 191 | }); 192 | 193 | const handleRequest = async (res: Response, query: string, params: any) => { 194 | try { 195 | const data = await queryLeetCodeAPI(query, params); 196 | res.json(data); 197 | } catch (error) { 198 | res.status(500).json({ error: error.message }); 199 | } 200 | }; 201 | app.get('/dailyQuestion', (_, res) => { 202 | handleRequest(res, dailyQeustion, {}); 203 | }); 204 | 205 | app.get('/skillStats/:username', (req, res) => { 206 | const { username } = req.params; 207 | handleRequest(res, skillStatsQuery, { username }); 208 | }); 209 | 210 | app.get('/userProfileUserQuestionProgressV2/:userSlug', (req, res) => { 211 | const { userSlug } = req.params; 212 | handleRequest(res, userProfileUserQuestionProgressV2Query, { userSlug }); 213 | }); 214 | 215 | app.get('/discussTopic/:topicId', (req, res) => { 216 | const topicId = parseInt(req.params.topicId); 217 | handleRequest(res, discussTopicQuery, { topicId }); 218 | }); 219 | 220 | app.get('/discussComments/:topicId', (req, res) => { 221 | const topicId = parseInt(req.params.topicId); 222 | const { 223 | orderBy = 'newest_to_oldest', 224 | pageNo = 1, 225 | numPerPage = 10, 226 | } = req.query; 227 | handleRequest(res, discussCommentsQuery, { 228 | topicId, 229 | orderBy, 230 | pageNo, 231 | numPerPage, 232 | }); 233 | }); 234 | 235 | app.get('/userContestRankingInfo/:username', (req, res) => { 236 | const { username } = req.params; 237 | handleRequest(res, userContestRankingInfoQuery, { username }); 238 | }); 239 | 240 | //get the daily leetCode problem 241 | app.get('/daily', leetcode.dailyProblem); 242 | 243 | //get the selected question 244 | app.get('/select', leetcode.selectProblem); 245 | 246 | //get list of problems 247 | app.get('/problems', leetcode.problems); 248 | 249 | //get 20 trending Discuss 250 | app.get('/trendingDiscuss', leetcode.trendingCategoryTopics); 251 | 252 | app.get('/languageStats', leetcode.languageStats); 253 | 254 | // Construct options object on all user routes. 255 | app.use( 256 | '/:username*', 257 | (req: FetchUserDataRequest, _res: Response, next: NextFunction) => { 258 | req.body = { 259 | username: req.params.username, 260 | limit: req.query.limit, 261 | }; 262 | next(); 263 | } 264 | ); 265 | 266 | //get user profile details 267 | app.get('/:username', leetcode.userData); 268 | app.get('/:username/badges', leetcode.userBadges); 269 | app.get('/:username/solved', leetcode.solvedProblem); 270 | app.get('/:username/contest', leetcode.userContest); 271 | app.get('/:username/contest/history', leetcode.userContestHistory); 272 | app.get('/:username/submission', leetcode.submission); 273 | app.get('/:username/acSubmission', leetcode.acSubmission); 274 | app.get('/:username/calendar', leetcode.calendar); 275 | 276 | export default app; 277 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | const config = { 2 | port: process.env.PORT || 3000, 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import app from './app'; 2 | import config from './config'; 3 | 4 | app.listen(config.port, () => { 5 | console.log(`Server is running at => http://localhost:${config.port} ⚙️`); 6 | }); 7 | -------------------------------------------------------------------------------- /src/leetCode.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from 'express'; 2 | import * as gqlQueries from './GQLQueries'; 3 | import * as formatUtils from './FormatUtils'; 4 | import * as controllers from './Controllers'; 5 | import { TransformedUserDataRequest } from './types'; 6 | 7 | export const userData = (req: TransformedUserDataRequest, res: Response) => { 8 | controllers.fetchUserDetails( 9 | req.body, 10 | res, 11 | formatUtils.formatUserData, 12 | gqlQueries.userProfileQuery 13 | ); 14 | }; 15 | 16 | export const userBadges = (req: TransformedUserDataRequest, res: Response) => { 17 | controllers.fetchUserDetails( 18 | req.body, 19 | res, 20 | formatUtils.formatBadgesData, 21 | gqlQueries.userProfileQuery 22 | ); 23 | }; 24 | 25 | export const userContest = (req: TransformedUserDataRequest, res: Response) => { 26 | controllers.fetchUserDetails( 27 | req.body, 28 | res, 29 | formatUtils.formatContestData, 30 | gqlQueries.contestQuery 31 | ); 32 | }; 33 | 34 | export const userContestHistory = ( 35 | req: TransformedUserDataRequest, 36 | res: Response 37 | ) => { 38 | controllers.fetchUserDetails( 39 | req.body, 40 | res, 41 | formatUtils.formatContestHistoryData, 42 | gqlQueries.contestQuery 43 | ); 44 | }; 45 | 46 | export const solvedProblem = ( 47 | req: TransformedUserDataRequest, 48 | res: Response 49 | ) => { 50 | controllers.fetchUserDetails( 51 | req.body, 52 | res, 53 | formatUtils.formatSolvedProblemsData, 54 | gqlQueries.userProfileQuery 55 | ); 56 | }; 57 | 58 | export const submission = (req: TransformedUserDataRequest, res: Response) => { 59 | controllers.fetchUserDetails( 60 | req.body, 61 | res, 62 | formatUtils.formatSubmissionData, 63 | gqlQueries.submissionQuery 64 | ); 65 | }; 66 | 67 | export const acSubmission = ( 68 | req: TransformedUserDataRequest, 69 | res: Response 70 | ) => { 71 | controllers.fetchUserDetails( 72 | req.body, 73 | res, 74 | formatUtils.formatAcSubmissionData, 75 | gqlQueries.AcSubmissionQuery 76 | ); 77 | }; 78 | 79 | export const calendar = (req: TransformedUserDataRequest, res: Response) => { 80 | controllers.fetchUserDetails( 81 | req.body, 82 | res, 83 | formatUtils.formatSubmissionCalendarData, 84 | gqlQueries.userProfileQuery 85 | ); 86 | }; 87 | 88 | //Problems Details 89 | export const dailyProblem = (_req: Request, res: Response) => { 90 | controllers.fetchSingleProblem( 91 | res, 92 | formatUtils.formatDailyData, 93 | gqlQueries.dailyProblemQuery, 94 | null 95 | ); 96 | }; 97 | 98 | export const selectProblem = (req: Request, res: Response) => { 99 | const title = req.query.titleSlug as string; 100 | if (title !== undefined) { 101 | controllers.fetchSingleProblem( 102 | res, 103 | formatUtils.formatQuestionData, 104 | gqlQueries.selectProblemQuery, 105 | title 106 | ); 107 | } else { 108 | res.status(400).json({ 109 | error: 'Missing or invalid query parameter: titleSlug', 110 | solution: 'put query after select', 111 | example: 'localhost:3000/select?titleSlug=two-sum', 112 | }); 113 | } 114 | }; 115 | 116 | export const problems = ( 117 | req: Request<{}, {}, {}, { limit: number; skip: number; tags: string; difficulty: string }>, 118 | res: Response 119 | ) => { 120 | const difficulty=req.query.difficulty; 121 | const limit = req.query.limit; 122 | const skip = req.query.skip; 123 | const tags = req.query.tags; 124 | 125 | controllers.fetchProblems( 126 | { limit, skip, tags, difficulty }, 127 | res, 128 | formatUtils.formatProblemsData, 129 | gqlQueries.problemListQuery 130 | ); 131 | }; 132 | 133 | 134 | export const trendingCategoryTopics = (_req: Request, res: Response) => { 135 | const first = parseInt(_req.query.first as string); 136 | if (!isNaN(first)) { 137 | controllers.fetchTrendingTopics( 138 | { first }, 139 | res, 140 | formatUtils.formatTrendingCategoryTopicData, 141 | gqlQueries.trendingDiscussQuery 142 | ); 143 | } 144 | else { 145 | res.status(400).json({ 146 | error: 'Missing or invalid query parameter: limit', 147 | solution: 'put query after discussion', 148 | example: 'localhost:3000/trendingDiscuss?first=20', 149 | }); 150 | } 151 | 152 | }; 153 | 154 | export const languageStats = (_req: Request, res: Response) => { 155 | const username = _req.query.username as string; 156 | if (username) { 157 | controllers.fetchDataRawFormat( 158 | { username }, 159 | res, 160 | gqlQueries.languageStatsQuery 161 | ); 162 | } 163 | else { 164 | res.status(400).json({ 165 | error: 'Missing or invalid query parameter: username', 166 | solution: 'put query after discussion', 167 | example: 'localhost:3000/languageStats?username=uwi', 168 | }); 169 | } 170 | 171 | }; -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { Request } from 'express'; 2 | // User Data 3 | interface UserDataProfile { 4 | aboutMe: string; 5 | company?: string; 6 | countryName?: string; 7 | realName: string; 8 | birthday?: string; 9 | userAvatar: string; 10 | ranking: number; 11 | reputation: number; 12 | school?: string; 13 | skillTags: string[]; 14 | websites: string[]; 15 | } 16 | 17 | interface MatchedUser { 18 | activeBadge: Badge; 19 | badges: Badge[]; 20 | githubUrl: string; 21 | linkedinUrl?: string; 22 | profile: UserDataProfile; 23 | upcomingBadges: Badge[]; 24 | username: string; 25 | twitterUrl?: string; 26 | submissionCalendar: string; 27 | submitStats: { 28 | totalSubmissionNum: { 29 | difficulty: Difficulty; 30 | count: number; 31 | submissions: number; 32 | }[]; 33 | acSubmissionNum: { 34 | difficulty: Difficulty; 35 | count: number; 36 | submissions: number; 37 | }[]; 38 | count: number; 39 | }; 40 | } 41 | 42 | export interface UserData { 43 | userContestRanking: null | { 44 | attendedContestsCount: number; 45 | badge: Badge; 46 | globalRanking: number; 47 | rating: number; 48 | totalParticipants: number; 49 | topPercentage: number; 50 | }; 51 | userContestRankingHistory: { 52 | attended: boolean; 53 | rating: number; 54 | ranking: number; 55 | trendDirection: string; 56 | problemsSolved: number; 57 | totalProblems: number; 58 | finishTimeInSeconds: number; 59 | contest: { 60 | title: string; 61 | startTime: string; 62 | }; 63 | }[]; 64 | matchedUser: MatchedUser; 65 | recentAcSubmissionList: {}[]; 66 | recentSubmissionList: Submission[]; 67 | } 68 | 69 | interface Badge { 70 | name: string; 71 | icon: string; 72 | } 73 | 74 | type Difficulty = 'All' | 'Easy' | 'Medium' | 'Hard'; 75 | //User Details 76 | export type FetchUserDataRequest = Request< 77 | { username: string }, 78 | {}, 79 | { username: string; limit: number }, 80 | { limit: number } 81 | >; 82 | 83 | export type TransformedUserDataRequest = Request< 84 | {}, 85 | {}, 86 | { username: string; limit: number } 87 | >; 88 | 89 | // ProblemData 90 | export interface ProblemSetQuestionListData { 91 | problemsetQuestionList: { 92 | total: number; 93 | questions: {}[]; 94 | }; 95 | } 96 | 97 | interface Submission { 98 | title: string; 99 | titleSlug: string; 100 | timestamp: string; 101 | statusDisplay: string; 102 | lang: string; 103 | } 104 | 105 | interface Question { 106 | content: string; 107 | companyTagStats: string[]; 108 | difficulty: Difficulty; 109 | dislikes: number; 110 | exampleTestcases: {}[]; 111 | hints: {}[]; 112 | isPaidOnly: boolean; 113 | likes: number; 114 | questionId: number; 115 | questionFrontendId: number; 116 | solution: string; 117 | similarQuestions: {}[]; 118 | title: string; 119 | titleSlug: string; 120 | topicTags: string[]; 121 | } 122 | 123 | export interface DailyProblemData { 124 | activeDailyCodingChallengeQuestion: { 125 | date: string; 126 | link: string; 127 | question: Question; 128 | }; 129 | } 130 | export interface SelectProblemData { 131 | question: Question; 132 | } 133 | 134 | export interface TrendingDiscussionObject { 135 | data: { 136 | cachedTrendingCategoryTopics: { 137 | id: number; 138 | title: string; 139 | post: { 140 | id: number; 141 | creationDate: number; 142 | contentPreview: string; 143 | author: { 144 | username: string; 145 | isActive: boolean; 146 | profile: { 147 | userAvatar: string; 148 | }; 149 | }; 150 | }; 151 | }[]; 152 | }; 153 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "lib": ["dom", "es6", "es2017", "esnext.asynciterable"], 6 | "skipLibCheck": true, 7 | "sourceMap": true, 8 | "outDir": "./dist", 9 | "moduleResolution": "node", 10 | "removeComments": true, 11 | "noImplicitAny": true, 12 | "strictNullChecks": true, 13 | "strictFunctionTypes": true, 14 | "noImplicitThis": true, 15 | "noUnusedLocals": true, 16 | "noUnusedParameters": true, 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | "allowSyntheticDefaultImports": true, 20 | "esModuleInterop": true, 21 | "emitDecoratorMetadata": true, 22 | "experimentalDecorators": true, 23 | "resolveJsonModule": true, 24 | "baseUrl": "." 25 | }, 26 | "exclude": ["node_modules", "src/__tests__"], 27 | "include": ["./src/**/*.ts"] 28 | } 29 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "./index.js", 6 | "use": "@vercel/node" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/(.*)", 12 | "dest": "/" 13 | } 14 | ] 15 | } 16 | --------------------------------------------------------------------------------