Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
December 31, 2021 06:00 am GMT

How I Sync Daily LeetCoding Challenge to Todoist

If you have ever been on a job hunt for a software developer position, you would have come across the so-called LeetCode style interviews.

Despite the fact that most of us dont have to invert a Binary Tree at our actual job, that is how most coding/technical interviews are conducted at big tech companies like Google and Microsoft. Yes, even at the likes of unicorns (except for Stripe, because they are cool) and startups.

In this post, Ill be writing about the thought process of how I came about building and deploying a very simple JavaScript app for free with Cloudflare Worker. If you simply want to deploy and use the app on your own, check out the repository here.

TL;DR

  • How to get the Daily LeetCoding Challenge question using LeetCode API
  • Creating a Todoist task using Todoist API
  • Schedule our app to run daily using Cloudflare Worker ScheduledEvent API
  • How to test a Cloudflare Worker Cron trigger locally with Miniflare

Problem Statement

Since a year ago, I have been trying to make it a habit to solve the Daily LeetCoding Challenge (which Im still struggling with). As I am using Todoist as my main productivity tool of choice, I have a daily task that looks just like this:

My Todoist habitual tasks

My Todoist habitual tasks

As a lazy person, having to check leetcode.com every time I want to practice is too much of a hassle. So then I thought, why not just sync the Daily LeetCoding Challenge to my Todoist every day?

Requirements

Lets start by defining what I want the app to do:

  1. Get Daily LeetCoding Challenge question
  2. Ability to create a new task on my Todoist account
  3. Sync new Daily LeetCoding Challenge question on time
  4. The app has to sync on time for free every day

Lets start!

LeetCode API

Like any sane person would do, the first thing I did was to do some research. By research, I meant I started to Google for information.

The first thing I did was to immediately Google for leetcode API, looking for the official API documentation.

Official API documentation

To my surprise, there wasnt any official LeetCode API documentation available. While there is a couple of unofficial LeetCode API repositories on GitHub, I would rather not use any unofficial API due to reliability concerns (poorly maintained, outdated, etc.).

The Good Ol DevTool inspection

The second thing that immediately came to my mind was to inspect the network request being made while visiting the site https://leetcode.com/problemset/all/.

With this, I was able to figure out the exact API called to query for the Daily LeetCoding Challenge done.

Since LeetCode is using GraphQL, you would need to check out the Payload tab to see the GraphQL body

Since LeetCode is using GraphQL, you would need to check out the Payload tab to see the GraphQL body

Heres the GraphQL request body:

# HTTP POST to https://leetcode.com/graphqlquery questionOfToday {    activeDailyCodingChallengeQuestion {        date        userStatus        link        question {            acRate            difficulty            freqBar            frontendQuestionId: questionFrontendId            isFavor            paidOnly: isPaidOnly            status            title            titleSlug            hasVideoSolution            hasSolution            topicTags {                name                id                slug            }        }    }}

You can use the curl command below to try it out yourself:

curl --request POST \  --url https://leetcode.com/graphql \  --header 'Content-Type: application/json' \  --data '{"query":"query questionOfToday {
activeDailyCodingChallengeQuestion {
date
userStatus
link
question {
acRate
difficulty
freqBar
frontendQuestionId: questionFrontendId
isFavor
paidOnly: isPaidOnly
status
title
titleSlug
hasVideoSolution
hasSolution
topicTags {
name
id
slug
}
}
}
}
","operationName":"questionOfToday"}'

Code
Enough talking, lets start to write some code that does exactly what we went through:

// Just some constantsconst LEETCODE_API_ENDPOINT = 'https://leetcode.com/graphql'const DAILY_CODING_CHALLENGE_QUERY = `query questionOfToday {    activeDailyCodingChallengeQuestion {        date        userStatus        link        question {            acRate            difficulty            freqBar            frontendQuestionId: questionFrontendId            isFavor            paidOnly: isPaidOnly            status            title            titleSlug            hasVideoSolution            hasSolution            topicTags {                name                id                slug            }        }    }}`// We can pass the JSON response as an object to our createTodoistTask later.const fetchDailyCodingChallenge = async () => {    console.log(`Fetching daily coding challenge from LeetCode API.`)    const init = {        method: 'POST',        headers: { 'Content-Type': 'application/json' },        body: JSON.stringify({ query: DAILY_CODING_CHALLENGE_QUERY }),    }    const response = await fetch(LEETCODE_API_ENDPOINT, init)    return response.json()}

Task Get Daily LeetCoding Challenge question checked.

Todoist API

Like what we did in the previous step, I was able to find the official API documentation for Todoist. Typically, the first section that I always look for in API documentation is the Authorization section, especially when you want to perform create/update/delete operations on an app.

In short, authorization was pretty straightforward for Todoist:

  1. Get your API token
  2. Whenever you make a request, attach Authorization: Bearer xxx-your-todoist-api-token-xxx to your HTTP request header

Heres an example of what the curl command to create a new task on Todoist would look like:

curl --request POST \ --url 'https://api.todoist.com/rest/v1/tasks?=' \ --header 'Authorization: Bearer xxx-your-todoist-api-token-xxx' \ --header 'Content-Type: application/json' \ --data '{"content": "Buy a jar of peanut butter","due_string": "Today"}'

Code

Writing a function that does what we said is relatively easy, it looks something like this:

const TODOIST_API_ENDPOINT = "https://api.todoist.com/rest/v1";// Passing in the `question` object from fetchDailyCodingChallenge functionconst createTodoistTask = async (question) => {    const questionInfo = question.data.activeDailyCodingChallengeQuestion;    const questionTitle = questionInfo.question.title;    const questionDifficulty = questionInfo.question.difficulty;    const questionLink = `https://leetcode.com${questionInfo.link}`;    console.log(`Creating Todoist task with title ${questionTitle}.`);    const body = {        content: `[${questionTitle}](${questionLink})`,        description: `Difficulty: ${questionDifficulty}`,        due_string: "Today",        priority: 4,    };    const init = {        method: "POST",        body: JSON.stringify(body),        headers: {            "Content-Type": "application/json",            Authorization: `Bearer ${TODOIST_API_TOKEN}`, // Set at environment variable        },    };    const response = await fetch(`${TODOIST_API_ENDPOINT}/tasks`, init);    return response.json();};

Task Create a new task on my Todoist account done.

Cloudflare Worker

And were down to our one final task running/automating the 2 tasks above every day, for free.

The first thing that came to my mind was Cron job. So, I immediately started looking for free solutions on the Internet. After spending a couple of hours doing some homework, I came across Cloudflare Worker, and I figured to give them a try.

It runs on V8 JavaScript, not Node.js
This is probably one of the most common misconceptions about Cloudflare Worker. As the workers environment is not in Node.js, a lot of packages (e.g. npm install some-node-package) that are running on Node.js would simply not work.

Tip: Check out the supported packages and libraries here.

Lucky for us, we only need to use the JavaScript built-in fetch API.

More code

Starting a Cloudflare Worker project is dead simple (reference), basically:

  1. Install the Wrangler CLI using npm install -g @cloudflare/wrangler
  2. Run wrangler generate
  3. The entry point is addEventListener function. For our use case, we will be using the ScheduledEvent API where we simply have to change our event from "fetch" to "scheduled"

Lets stitch everything together:

// Move the constants to const.jsconst syncLeetCodeCodingChallenge = async (event) => {    const question = await fetchDailyCodingChallenge();    await createTodoistTask(question);};addEventListener("scheduled", (event) => {    // Change 'fetch' to 'scheduled'    event.waitUntil(syncLeetCodeCodingChallenge(event));});

Next, we would simply need to modify the wrangler.toml as below:

name = "<your-project-name>"type = "webpack"...[triggers]crons = ["1 0 * * *"]

With the setting above, the worker will run every day at 00:01 UTC and sync the Daily LeetCoding Challenge to your Todoist.

Thats all! Moving on to testing.

How to test Cloudflare workers locally

In order to try out the Cron triggers locally, we would need to install the Miniflare CLI. After installation, you may run your worker using:

# At terminal 1miniflare# At terminal 2curl "http://localhost:8787/.mf/scheduled"

If you see a new task is created on your Todoist, you have made it!

Deployment

No side project is ever done without hosting it.

To deploy the app on your own immediately, check out the project repository and use the Deploy with Workers button. If youre interested in the know-how:

  1. Create a Cloudflare account.
  2. Add TODOIST_API_TOKEN using wrangler secret put TODOIST_API_TOKEN. You may find the newly added secret under 'Cloudflare Worker' 'Settings' 'Variables'. You can get your Todoist API token here.
  3. Optional: This is only required for Wrangler actions. Add CF_API_TOKEN into your GitHub repository secrets. You can create your API token from https://dash.cloudflare.com/profile/api-tokens using the Edit Cloudflare Workers template.
  4. Finally, to publish any new changes to your Cloudflare Worker, run wrangler publish

And we are finally done!

Closing Thoughts

Finally, theres a lot more that we could have done, e.g.:

  • Handling unanswered questions from previous days
  • Making the app configurable/customizable for users
  • Add tags to our task
  • Allowing users to create a random LeetCode question as a task based on question tag

Im going to leave these features out for another day.

While there is a lot of hate on coding interviews as such, I personally look at it this way by learning some brain teasers, you probably get paid a lot more, so why not? It is really a matter of perspective. If you happen to enjoy doing them, that is even better.

Personally, I dont find as much joy doing LeetCode questions. Rather, I work on LeetCode problems as if I am lifting weights at the gym. While I dont enjoy lifting weights, I do like reaping the benefits of it.

Thats all for today. Let me know what are you building with Cloudflare Worker!


Original Link: https://dev.to/jerrynsh/how-i-sync-daily-leetcoding-challenge-to-todoist-4d9a

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To