Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Supabase Edge Functions T-Shirt Contest

req--supabase_edge-functions_shirt.livemd

Supabase Edge Functions T-Shirt Contest

Mix.install([
  {:req, github: "wojtekmach/req"},
  {:floki, "~> 0.32"}
])

Summary

Supabase made the announcement on Twitter

> Supabase Edge Functions now support GET requests! > > To celebrate, we’re giving away some limited edition Function Shirts! > > To enter the draw, make a GET request with your email address, your Twitter handle, your t-shirt size, and the correct answer 👇

Michael Crumm mentioned this was a great excuse to use Req and it’s an even greater excuse to use Livebook :>. We also take this a step further using Livebook 0.7’s new secrets feature to keep from exposing our email address, twitter handle, and tshirt size. While my Twitter handle is public knowledge and I don’t mind sharing it, some people may not wish to tie their code to that identity.

Analysis

  1. Their link pointed to [https://obuldanrptloktxcffvn.functions.supabase.co/get-tshirt-competition?email=tshirt@supabase.io&twitter=supabase&size=2XL&answer=2]
  2. The error code returned is
       {
          "error": "Sorry, that's wrong, please try again! HINT: https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts"
       }
  3. Inspecting [https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts], we see the answer check is line 43:
       if (Number(answer) !== positionInAlphabet(email!))
          throw new Error(
             `Sorry, that's wrong, please try again! HINT: https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts`
          )
  4. Translate the positionInAlphabet() function to Elixir
       function positionInAlphabet(myChar: string): number {
          const DIFFERENCE_CHARCODE_AND_LETTERS = 96
          // Convert the character into lowercase
          const myCharLowercase = myChar.toLowerCase()
          // Find the position of the char in the alphabet
          const position = myCharLowercase.charCodeAt(0) - DIFFERENCE_CHARCODE_AND_LETTERS
          return position
       }
  5. I didn’t remember how charCodeAt() works but [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt] as a great primer. It even gives the example used in positionInAlphabet, 'ABC'.charCodeAt(0) // returns 65.
  6. Find a TypeScript playground to brute force an answer at [https://www.typescriptlang.org/play]. This will help us check our work.
       function positionInAlphabet(myChar: string): number {
          const DIFFERENCE_CHARCODE_AND_LETTERS = 96
          // Convert the character into lowercase
          const myCharLowercase = myChar.toLowerCase()
          console.log(`myCharLowercase: ${myCharLowercase}`)
          // Find the position of the char in the alphabet
          const position = myCharLowercase.charCodeAt(0) - DIFFERENCE_CHARCODE_AND_LETTERS
          console.log(`myCharLowercase.charCodeAt(0): ${myCharLowercase.charCodeAt(0)}`)
          console.log(`position: ${position}`)
          return position
       }
       let email = "";
       console.log(positionInAlphabet(email));
  7. This determined the answer for my email address was 10. I plugged that in to the Req parameters and got a successful result.
       body: "Thanks for playing!  has been added to the draw \\o/"
  8. The console output helped reverse engineer our positionInAlphabet anonymous function
       [LOG]: "myCharLowercase: " 
       [LOG]: "myCharLowercase.charCodeAt(0): 106" 
       [LOG]: "position: 10" 
       [LOG]: 10
  9. I used Livebook’s new SECRETS section to create secrets for EMAIL_ADDRESS, TWITTER_HANDLE, and TSHIRT_SIZE using [https://news.livebook.dev/whats-new-in-livebook-0.7-2wOPsY] as a guide.
  10. I reworked variable names and still downcased the whole string. In searching the String documentation for the function to return a single code point as an integer, I realized to_charlist/1 would return a list of those code points and I could simply return the first value. Hurray, the math worked and I got the same result.
  11. I realized later that other people may intuitively understand what a function positionInAlphabet returns, that A should be 1 and Z should be 26. With that knowledge you could manually determine that the letter J (what my email starts with) would be position 10. I absolutely needed the TypeScript example to verify my assumptions of how everything fit together.

Solution

url = "https://obuldanrptloktxcffvn.functions.supabase.co/get-tshirt-competition"

positionInAlphabet = fn input ->
  # Use the same DIFFERENCE_CHARCODE_AND_LETTERS, 96
  difference = 96
  # Convert the character into lowercase
  character_lowercase = String.downcase(input)
  # Find the position of the character in the alphabet
  #   to_charlist() converts the full email address to a charlist of code points
  #   and we get the first value in this list using hd()
  position = (character_lowercase |> String.to_charlist() |> hd()) - difference
  position
end

email = System.fetch_env!("LB_EMAIL_ADDRESS")
answer = positionInAlphabet.(email)

parameters = %{
  "email" => email,
  "twitter" => System.fetch_env!("LB_TWITTER_HANDLE"),
  "size" => System.fetch_env!("LB_TSHIRT_SIZE"),
  "answer" => answer
}

Req.get!(url, params: parameters)