-module(sendr_scaleway). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/sendr_scaleway.gleam"). -export([config/3, request/2, response/1]). -export_type([scaleway_error/0, api_error_detail/0, scaleway_config/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " sendr_scaleway — Scaleway Transactional Email backend for sendr.\n" "\n" " Builds HTTP `Request` values for the Scaleway Transactional Email API and\n" " parses `Response` values returned by it.\n" ). -type scaleway_error() :: {invalid_uri, binary()} | {invalid_response, integer(), gleam@json:decode_error()} | {api_error, integer(), list(api_error_detail())}. -type api_error_detail() :: {api_error_detail, binary(), binary()}. -opaque scaleway_config() :: {scaleway_config, binary(), binary(), binary()}. -file("src/sendr_scaleway.gleam", 56). ?DOC( " Create a new `ScalewayConfig`.\n" "\n" " `api_key` is your Scaleway secret key (used for the `X-Auth-Token` header).\n" " `region` is the Scaleway region (e.g. `\"fr-par\"`).\n" " `project_id` is your Scaleway Project UUID.\n" ). -spec config(binary(), binary(), binary()) -> scaleway_config(). config(Api_key, Region, Project_id) -> {scaleway_config, Api_key, Region, Project_id}. -file("src/sendr_scaleway.gleam", 146). -spec uri(binary()) -> binary(). uri(Region) -> <<<<"https://api.scaleway.com/transactional-email/v1alpha1/regions/"/utf8, Region/binary>>/binary, "/emails"/utf8>>. -file("src/sendr_scaleway.gleam", 256). -spec attachment_to_json(sendr@message@attachment:attachment()) -> gleam@json:json(). attachment_to_json(Attachment) -> Filename@1 = case erlang:element(3, Attachment) of <<""/utf8>> -> erlang:element(4, Attachment); Filename -> Filename end, Content = gleam_stdlib:base64_encode(erlang:element(5, Attachment), true), Content_type = erlang:element(2, Attachment), gleam@json:object( [{<<"name"/utf8>>, gleam@json:string(Filename@1)}, {<<"type"/utf8>>, gleam@json:string(Content_type)}, {<<"content"/utf8>>, gleam@json:string(Content)}] ). -file("src/sendr_scaleway.gleam", 271). -spec add(list({binary(), gleam@json:json()}), binary(), gleam@json:json()) -> list({binary(), gleam@json:json()}). add(Entries, Key, Value) -> lists:append(Entries, [{Key, Value}]). -file("src/sendr_scaleway.gleam", 279). -spec add_if_some( list({binary(), gleam@json:json()}), binary(), gleam@option:option(gleam@json:json()) ) -> list({binary(), gleam@json:json()}). add_if_some(Entries, Key, Value) -> case Value of {some, Value@1} -> add(Entries, Key, Value@1); none -> Entries end. -file("src/sendr_scaleway.gleam", 246). -spec mailbox_to_json(sendr@message@mailbox:mailbox()) -> gleam@json:json(). mailbox_to_json(Mailbox) -> gleam@json:object( begin _pipe = [{<<"email"/utf8>>, gleam@json:string(erlang:element(3, Mailbox))}], add_if_some( _pipe, <<"name"/utf8>>, begin _pipe@1 = erlang:element(2, Mailbox), _pipe@2 = gleam@string:to_option(_pipe@1), gleam@option:map(_pipe@2, fun gleam@json:string/1) end ) end ). -file("src/sendr_scaleway.gleam", 223). -spec build_body(sendr@message:message(), scaleway_config()) -> gleam@json:json(). build_body(Message, Config) -> _pipe = [], _pipe@1 = add_if_some( _pipe, <<"from"/utf8>>, gleam@option:map(erlang:element(2, Message), fun mailbox_to_json/1) ), _pipe@2 = add_if_some( _pipe@1, <<"to"/utf8>>, gleam@option:map( erlang:element(4, Message), fun(_capture) -> gleam@json:array(_capture, fun mailbox_to_json/1) end ) ), _pipe@3 = add_if_some( _pipe@2, <<"cc"/utf8>>, gleam@option:map( erlang:element(5, Message), fun(_capture@1) -> gleam@json:array(_capture@1, fun mailbox_to_json/1) end ) ), _pipe@4 = add_if_some( _pipe@3, <<"bcc"/utf8>>, gleam@option:map( erlang:element(6, Message), fun(_capture@2) -> gleam@json:array(_capture@2, fun mailbox_to_json/1) end ) ), _pipe@5 = add_if_some( _pipe@4, <<"subject"/utf8>>, gleam@option:map(erlang:element(7, Message), fun gleam@json:string/1) ), _pipe@6 = add_if_some( _pipe@5, <<"text"/utf8>>, gleam@option:map( erlang:element(2, erlang:element(9, Message)), fun gleam@json:string/1 ) ), _pipe@7 = add_if_some( _pipe@6, <<"html"/utf8>>, gleam@option:map( erlang:element(3, erlang:element(9, Message)), fun gleam@json:string/1 ) ), _pipe@8 = add( _pipe@7, <<"project_id"/utf8>>, gleam@json:string(erlang:element(4, Config)) ), _pipe@9 = add( _pipe@8, <<"attachments"/utf8>>, gleam@json:array(erlang:element(8, Message), fun attachment_to_json/1) ), gleam@json:object(_pipe@9). -file("src/sendr_scaleway.gleam", 216). -spec validate_body(sendr@message:message()) -> {ok, nil} | {error, sendr:sendr_error(any())}. validate_body(Message) -> case {erlang:element(2, erlang:element(9, Message)), erlang:element(3, erlang:element(9, Message))} of {none, none} -> {error, {invalid_body, no_body}}; {_, _} -> {ok, nil} end. -file("src/sendr_scaleway.gleam", 202). -spec validate_attachments(sendr@message:message()) -> {ok, nil} | {error, sendr:sendr_error(any())}. validate_attachments(Message) -> gleam@list:try_each( erlang:element(8, Message), fun(Attachment) -> case {erlang:element(3, Attachment), erlang:element(4, Attachment)} of {<<""/utf8>>, <<""/utf8>>} -> {error, {invalid_attachment, required_filename_missing, Attachment}}; {_, _} -> case Attachment of {inlined_attachment, _, _, _, _, _} -> {error, {invalid_attachment, attachment_type_not_supported, Attachment}}; {attached_attachment, _, _, _, _} -> {ok, nil} end end end ). -file("src/sendr_scaleway.gleam", 195). -spec validate_subject(sendr@message:message()) -> {ok, nil} | {error, sendr:sendr_error(any())}. validate_subject(Message) -> case erlang:element(7, Message) of none -> {error, {required_field_missing, subject}}; {some, <<""/utf8>>} -> {error, {required_field_missing, subject}}; _ -> {ok, nil} end. -file("src/sendr_scaleway.gleam", 185). -spec validate_mailbox(sendr@message@mailbox:mailbox(), sendr:field()) -> {ok, nil} | {error, sendr:sendr_error(any())}. validate_mailbox(Mailbox, Field) -> case gleam@string:split_once(erlang:element(3, Mailbox), <<"@"/utf8>>) of {ok, {Local, Domain}} when (Local =/= <<""/utf8>>) andalso (Domain =/= <<""/utf8>>) -> {ok, nil}; _ -> {error, {invalid_mailbox, Field, Mailbox}} end. -file("src/sendr_scaleway.gleam", 164). -spec validate_recipients(sendr@message:message()) -> {ok, nil} | {error, sendr:sendr_error(any())}. validate_recipients(Message) -> Recipients = begin _pipe = [erlang:element(4, Message), erlang:element(5, Message), erlang:element(6, Message)], _pipe@1 = gleam@option:values(_pipe), lists:append(_pipe@1) end, Validate = fun(Mailboxes, Field) -> _pipe@2 = Mailboxes, _pipe@3 = gleam@option:unwrap(_pipe@2, []), gleam@list:try_each( _pipe@3, fun(_capture) -> validate_mailbox(_capture, Field) end ) end, case Recipients of [] -> {error, no_recipients}; _ -> _pipe@4 = Validate(erlang:element(6, Message), bcc), _pipe@5 = gleam@result:'or'( _pipe@4, Validate(erlang:element(5, Message), cc) ), gleam@result:'or'(_pipe@5, Validate(erlang:element(4, Message), to)) end. -file("src/sendr_scaleway.gleam", 157). -spec validate_reply_to(sendr@message:message()) -> {ok, nil} | {error, sendr:sendr_error(any())}. validate_reply_to(Message) -> case erlang:element(3, Message) of none -> {ok, nil}; {some, []} -> {ok, nil}; {some, _} -> {error, {too_many_entries, reply_to}} end. -file("src/sendr_scaleway.gleam", 150). -spec validate_from(sendr@message:message()) -> {ok, nil} | {error, sendr:sendr_error(any())}. validate_from(Message) -> case erlang:element(2, Message) of none -> {error, {required_field_missing, from}}; {some, Mailbox} -> validate_mailbox(Mailbox, from) end. -file("src/sendr_scaleway.gleam", 68). ?DOC( " Build an HTTP `Request` for the Scaleway send API from a sendr `Message`.\n" "\n" " Validates the message (from, reply-to, recipients, subject, attachments)\n" " and returns `Error(SendrError(ScalewayError))` on validation failure.\n" ). -spec request(sendr@message:message(), scaleway_config()) -> {ok, gleam@http@request:request(binary())} | {error, sendr:sendr_error(scaleway_error())}. request(Message, Config) -> gleam@result:'try'( validate_from(Message), fun(_) -> gleam@result:'try'( validate_reply_to(Message), fun(_) -> gleam@result:'try'( validate_recipients(Message), fun(_) -> gleam@result:'try'( validate_subject(Message), fun(_) -> gleam@result:'try'( validate_attachments(Message), fun(_) -> gleam@result:'try'( validate_body(Message), fun(_) -> Body = build_body( Message, Config ), Uri = uri( erlang:element( 3, Config ) ), _pipe = Uri, _pipe@1 = gleam_stdlib:uri_parse( _pipe ), _pipe@2 = gleam@result:'try'( _pipe@1, fun gleam@http@request:from_uri/1 ), _pipe@3 = gleam@result:replace_error( _pipe@2, {invalid_uri, Uri} ), _pipe@8 = gleam@result:map( _pipe@3, fun(Req) -> _pipe@4 = Req, _pipe@5 = gleam@http@request:set_method( _pipe@4, post ), _pipe@6 = gleam@http@request:set_header( _pipe@5, <<"content-type"/utf8>>, <<"application/json"/utf8>> ), _pipe@7 = gleam@http@request:set_header( _pipe@6, <<"X-Auth-Token"/utf8>>, erlang:element( 2, Config ) ), gleam@http@request:set_body( _pipe@7, gleam@json:to_string( Body ) ) end ), gleam@result:map_error( _pipe@8, fun(Field@0) -> {backend_error, Field@0} end ) end ) end ) end ) end ) end ) end ). -file("src/sendr_scaleway.gleam", 99). ?DOC( " Parse a `Response` from the Scaleway send API.\n" "\n" " On HTTP 200 the response body is decoded and the `message_id` of the first\n" " email is returned. All other statuses are treated as API errors.\n" ). -spec response(gleam@http@response:response(binary())) -> {ok, binary()} | {error, sendr:sendr_error(scaleway_error())}. response(Response) -> case erlang:element(2, Response) of 200 -> Email_decoder = begin gleam@dynamic@decode:field( <<"message_id"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Msg_id) -> gleam@dynamic@decode:success(Msg_id) end ) end, Success_decoder = begin gleam@dynamic@decode:field( <<"emails"/utf8>>, gleam@dynamic@decode:list(Email_decoder), fun(Emails) -> gleam@dynamic@decode:success(Emails) end ) end, case gleam@json:parse(erlang:element(4, Response), Success_decoder) of {ok, [Msg_id@1 | _]} -> {ok, Msg_id@1}; {ok, []} -> {error, {backend_error, {invalid_response, 200, {unable_to_decode, [{decode_error, <<"At least one email with message_id"/utf8>>, <<"List"/utf8>>, []}]}}}}; {error, Error} -> {error, {backend_error, {invalid_response, 200, Error}}} end; Status -> Error_decoder = begin gleam@dynamic@decode:field( <<"message"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_string/1}, fun(Msg) -> gleam@dynamic@decode:success( [{api_error_detail, <<"Failed invocation"/utf8>>, Msg}] ) end ) end, case gleam@json:parse(erlang:element(4, Response), Error_decoder) of {ok, Details} -> {error, {backend_error, {api_error, Status, Details}}}; {error, Error@1} -> {error, {backend_error, {invalid_response, Status, Error@1}}} end end.