A resend library for the Gleam programming language

feat: initial commit

+23
.github/workflows/test.yml
··· 1 + name: test 2 + 3 + on: 4 + push: 5 + branches: 6 + - master 7 + - main 8 + pull_request: 9 + 10 + jobs: 11 + test: 12 + runs-on: ubuntu-latest 13 + steps: 14 + - uses: actions/checkout@v4 15 + - uses: erlef/setup-beam@v1 16 + with: 17 + otp-version: "26.0.2" 18 + gleam-version: "1.3.2" 19 + rebar3-version: "3" 20 + # elixir-version: "1.15.4" 21 + - run: gleam deps download 22 + - run: gleam test 23 + - run: gleam format --check src test
+4
.gitignore
··· 1 + *.beam 2 + *.ez 3 + /build 4 + erl_crash.dump
+36
README.md
··· 1 + # resend 2 + 3 + [![Package Version](https://img.shields.io/hexpm/v/resend)](https://hex.pm/packages/resend) 4 + [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/resend/) 5 + 6 + ```sh 7 + gleam add resend@1 8 + ``` 9 + 10 + ```gleam 11 + import resend 12 + import resend/emails.{create_email, send_email, with_html} 13 + 14 + pub fn main() { 15 + let client = 16 + resend.Resend(api_key: "// Replace this with your resend api key") 17 + 18 + create_email( 19 + client:, 20 + from: "from@example.com", 21 + to: ["to@example.com"], 22 + subject: ":)", 23 + ) 24 + |> with_html("<p>Email sent</p>") 25 + |> send_email() 26 + } 27 + ``` 28 + 29 + Further documentation can be found at <https://hexdocs.pm/resend>. 30 + 31 + ## Development 32 + 33 + ```sh 34 + gleam run # Run the project 35 + gleam test # Run the tests 36 + ```
+13
gleam.toml
··· 1 + name = "resend" 2 + version = "1.0.0" 3 + licences = ["MIT"] 4 + repository = { type = "github", user = "dinkelspiel", repo = "resend" } 5 + 6 + [dependencies] 7 + gleam_stdlib = ">= 0.34.0 and < 2.0.0" 8 + gleam_http = ">= 3.6.0 and < 4.0.0" 9 + gleam_hackney = ">= 1.2.0 and < 2.0.0" 10 + gleam_json = ">= 1.0.1 and < 2.0.0" 11 + 12 + [dev-dependencies] 13 + gleeunit = ">= 1.0.0 and < 2.0.0"
+26
manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "certifi", version = "2.12.0", build_tools = ["rebar3"], requirements = [], otp_app = "certifi", source = "hex", outer_checksum = "EE68D85DF22E554040CDB4BE100F33873AC6051387BAF6A8F6CE82272340FF1C" }, 6 + { name = "gleam_hackney", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib", "hackney"], otp_app = "gleam_hackney", source = "hex", outer_checksum = "066B1A55D37DBD61CC72A1C4EDE43C6015B1797FAF3818C16FE476534C7B6505" }, 7 + { name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" }, 8 + { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, 9 + { name = "gleam_stdlib", version = "0.39.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "2D7DE885A6EA7F1D5015D1698920C9BAF7241102836CE0C3837A4F160128A9C4" }, 10 + { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, 11 + { name = "hackney", version = "1.20.1", build_tools = ["rebar3"], requirements = ["certifi", "idna", "metrics", "mimerl", "parse_trans", "ssl_verify_fun", "unicode_util_compat"], otp_app = "hackney", source = "hex", outer_checksum = "FE9094E5F1A2A2C0A7D10918FEE36BFEC0EC2A979994CFF8CFE8058CD9AF38E3" }, 12 + { name = "idna", version = "6.1.1", build_tools = ["rebar3"], requirements = ["unicode_util_compat"], otp_app = "idna", source = "hex", outer_checksum = "92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA" }, 13 + { name = "metrics", version = "1.0.1", build_tools = ["rebar3"], requirements = [], otp_app = "metrics", source = "hex", outer_checksum = "69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16" }, 14 + { name = "mimerl", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "mimerl", source = "hex", outer_checksum = "A1E15A50D1887217DE95F0B9B0793E32853F7C258A5CD227650889B38839FE9D" }, 15 + { name = "parse_trans", version = "3.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "parse_trans", source = "hex", outer_checksum = "620A406CE75DADA827B82E453C19CF06776BE266F5A67CFF34E1EF2CBB60E49A" }, 16 + { name = "ssl_verify_fun", version = "1.1.7", build_tools = ["mix", "rebar3", "make"], requirements = [], otp_app = "ssl_verify_fun", source = "hex", outer_checksum = "FE4C190E8F37401D30167C8C405EDA19469F34577987C76DDE613E838BBC67F8" }, 17 + { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, 18 + { name = "unicode_util_compat", version = "0.7.0", build_tools = ["rebar3"], requirements = [], otp_app = "unicode_util_compat", source = "hex", outer_checksum = "25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521" }, 19 + ] 20 + 21 + [requirements] 22 + gleam_hackney = { version = ">= 1.2.0 and < 2.0.0" } 23 + gleam_http = { version = ">= 3.6.0 and < 4.0.0" } 24 + gleam_json = { version = ">= 1.0.1 and < 2.0.0" } 25 + gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } 26 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
+3
src/resend.gleam
··· 1 + pub type Resend { 2 + Resend(api_key: String) 3 + }
+138
src/resend/emails.gleam
··· 1 + import gleam/dynamic 2 + import gleam/hackney 3 + import gleam/http.{Post} 4 + import gleam/http/request 5 + import gleam/json 6 + import gleam/list 7 + import gleam/option.{type Option, None, Some} 8 + import gleam/result.{try} 9 + import resend.{type Resend} 10 + 11 + pub type ResendEmail { 12 + ResendEmail( 13 + client: Resend, 14 + from: String, 15 + to: List(String), 16 + subject: String, 17 + bcc: Option(List(String)), 18 + cc: Option(List(String)), 19 + reply_to: Option(List(String)), 20 + html: Option(String), 21 + text: Option(String), 22 + ) 23 + } 24 + 25 + pub fn with_bcc(resend: ResendEmail, bcc: List(String)) { 26 + ResendEmail(..resend, bcc: Some(bcc)) 27 + } 28 + 29 + pub fn with_cc(resend: ResendEmail, cc: List(String)) { 30 + ResendEmail(..resend, cc: Some(cc)) 31 + } 32 + 33 + pub fn with_reply_to(resend: ResendEmail, reply_to: List(String)) { 34 + ResendEmail(..resend, reply_to: Some(reply_to)) 35 + } 36 + 37 + pub fn with_html(resend: ResendEmail, html: String) { 38 + ResendEmail(..resend, html: Some(html)) 39 + } 40 + 41 + pub fn with_text(resend: ResendEmail, text: String) { 42 + ResendEmail(..resend, text: Some(text)) 43 + } 44 + 45 + pub fn create_email( 46 + client client: Resend, 47 + from from: String, 48 + to to: List(String), 49 + subject subject: String, 50 + ) { 51 + ResendEmail( 52 + client:, 53 + from:, 54 + to:, 55 + subject:, 56 + bcc: None, 57 + cc: None, 58 + reply_to: None, 59 + html: None, 60 + text: None, 61 + ) 62 + } 63 + 64 + pub type SendEmailError { 65 + RequestError 66 + BadRequestError 67 + ParseResponseBodyError 68 + } 69 + 70 + pub type SuccessResponse { 71 + SuccessResponse(id: String) 72 + } 73 + 74 + pub fn send_email(email: ResendEmail) { 75 + let assert Ok(request) = request.to("https://api.resend.com/emails") 76 + 77 + let body = 78 + json.object( 79 + list.concat([ 80 + [ 81 + #("from", json.string(email.from)), 82 + #("to", json.array(email.to, fn(a) { json.string(a) })), 83 + #("subject", json.string(email.from)), 84 + ], 85 + case email.bcc { 86 + Some(bcc) -> [#("bcc", json.array(bcc, fn(a) { json.string(a) }))] 87 + None -> [] 88 + }, 89 + case email.cc { 90 + Some(cc) -> [#("cc", json.array(cc, fn(a) { json.string(a) }))] 91 + None -> [] 92 + }, 93 + case email.reply_to { 94 + Some(reply_to) -> [ 95 + #("reply_to", json.array(reply_to, fn(a) { json.string(a) })), 96 + ] 97 + None -> [] 98 + }, 99 + case email.html { 100 + Some(html) -> [#("html", json.string(html))] 101 + None -> [] 102 + }, 103 + case email.text { 104 + Some(text) -> [#("text", json.string(text))] 105 + None -> [] 106 + }, 107 + ]), 108 + ) 109 + 110 + use response <- try( 111 + request 112 + |> request.prepend_header("accept", "application/vnd.hmrc.1.0+json") 113 + |> request.prepend_header("Content-Type", "application/json") 114 + |> request.prepend_header( 115 + "Authorization", 116 + "Bearer " <> email.client.api_key, 117 + ) 118 + |> request.set_method(Post) 119 + |> request.set_body( 120 + body 121 + |> json.to_string, 122 + ) 123 + |> hackney.send 124 + |> result.replace_error(RequestError), 125 + ) 126 + 127 + let body_decoder = 128 + dynamic.decode1(SuccessResponse, dynamic.field("id", dynamic.string)) 129 + 130 + case response.status { 131 + 200 -> 132 + case json.decode(response.body, body_decoder) { 133 + Ok(body) -> Ok(body) 134 + Error(_) -> Error(ParseResponseBodyError) 135 + } 136 + _ -> Error(BadRequestError) 137 + } 138 + }
+22
test/resend_test.gleam
··· 1 + import gleeunit 2 + import gleeunit/should 3 + import resend 4 + import resend/emails.{create_email, send_email, with_html} 5 + 6 + pub fn main() { 7 + gleeunit.main() 8 + } 9 + 10 + pub fn resend_test() { 11 + let client = 12 + resend.Resend(api_key: "// Replace this with your resend api key") 13 + 14 + create_email( 15 + client:, 16 + from: "from@example.com", 17 + to: ["to@example.com"], 18 + subject: ":)", 19 + ) 20 + |> with_html("<p>Successful response</p>") 21 + |> send_email() 22 + }