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
- Their link pointed to [https://obuldanrptloktxcffvn.functions.supabase.co/get-tshirt-competition?email=tshirt@supabase.io&twitter=supabase&size=2XL&answer=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" }
-
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` )
-
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 }
-
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 inpositionInAlphabet
,'ABC'.charCodeAt(0) // returns 65
. -
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));
-
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/"
-
The console output helped reverse engineer our
positionInAlphabet
anonymous function[LOG]: "myCharLowercase: " [LOG]: "myCharLowercase.charCodeAt(0): 106" [LOG]: "position: 10" [LOG]: 10
-
I used Livebook’s new
SECRETS
section to create secrets forEMAIL_ADDRESS
,TWITTER_HANDLE
, andTSHIRT_SIZE
using [https://news.livebook.dev/whats-new-in-livebook-0.7-2wOPsY] as a guide. - 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.
-
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 position10
. 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)