diff --git a/mix.exs b/mix.exs index f5cfa3d..3168007 100644 --- a/mix.exs +++ b/mix.exs @@ -38,7 +38,16 @@ defmodule GeoTherminator.MixProject do def application do [ mod: {GeoTherminator.Application, []}, - extra_applications: [:logger, :runtime_tools, :logger, :ssl, :crypto, :sasl, :tools, :inets] + extra_applications: [ + :logger, + :runtime_tools, + :logger, + :ssl, + :crypto, + :sasl, + :tools, + :inets + ] ] end @@ -69,7 +78,6 @@ defmodule GeoTherminator.MixProject do {:cubdb, "~> 2.0"}, {:gleam_stdlib, "~> 0.25"}, {:gleam_http, "~> 3.1"}, - {:gleam_hackney, "~> 0.2.1"}, {:gleam_erlang, "~> 0.17.1"}, {:gleam_json, "~> 0.5.0"} ] diff --git a/mix.lock b/mix.lock index 2424284..14b71e6 100644 --- a/mix.lock +++ b/mix.lock @@ -18,7 +18,6 @@ "floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"}, "gettext": {:hex, :gettext, "0.21.0", "15bbceb20b317b706a8041061a08e858b5a189654128618b53746bf36c84352b", [:mix], [{:expo, "~> 0.1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "04a66db4103b6d1d18f92240bb2c73167b517229316b7bef84e4eebbfb2f14f6"}, "gleam_erlang": {:hex, :gleam_erlang, "0.17.1", "40fff501e8ca39fa166f4c12ed13bb57e94fc5bb59a93b4446687d82d4a12ff9", [:gleam], [{:gleam_stdlib, "~> 0.22", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}], "hexpm", "baaa84f5bcc4477e809ba3e03bb3009a3894a6544c1511626c44408e39db2ae6"}, - "gleam_hackney": {:hex, :gleam_hackney, "0.2.1", "ca3c5677b85f31885a4366c73a110803515d6d23a2e233e459dc164260315404", [:gleam], [{:gleam_http, "~> 3.0", [hex: :gleam_http, repo: "hexpm", optional: false]}, {:gleam_stdlib, "~> 0.18", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}, {:hackney, "~> 1.18", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "ccaca00027c827436d8eb945651392b6e5798cfc9e69907a28be61832b0c02a4"}, "gleam_http": {:hex, :gleam_http, "3.1.1", "609158240630e21fc70c69b21384e5ebbcd86f71bd378a6f7c2b87f910ab3561", [:gleam], [{:gleam_stdlib, "~> 0.18", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}], "hexpm", "b66b7a1539ccb577119e4dc80dd3484c1a652cb032967954498eedbae3355763"}, "gleam_json": {:hex, :gleam_json, "0.5.0", "aff4507ad7700ad794ada6671c6dfd0174696713659bd8782858135b19f41b58", [:gleam], [{:gleam_stdlib, "~> 0.19", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}, {:thoas, "~> 0.2", [hex: :thoas, repo: "hexpm", optional: false]}], "hexpm", "e42443c98aa66e30143c24818f2cea801491c10ce6b1a5eddf3fc4abdc7601cb"}, "gleam_stdlib": {:hex, :gleam_stdlib, "0.25.0", "656f39258dcc8772719e463bbe7d1d1c7800238a520b41558fad53ea206ee3ab", [:gleam], [], "hexpm", "ad0f89928e0b919c8f8edf640484633b28dbf88630a9e6ae504617a3e3e5b9a2"}, @@ -53,4 +52,5 @@ "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, "thoas": {:hex, :thoas, "0.4.0", "86a72ccdc5ec388a13f9f843bcd6c1076640233b95440e47ffb8e3c0dbdb5a17", [:rebar3], [], "hexpm", "442296847aca11db8d25180693d7ca3073d6d7179f66952f07b16415306513b6"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, + "wx": {:hex, :bridge, "1.0.10", "5051dfe881e498a0bc056603df97b734bfe5fdc084f5e56dc42fab8049247144", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "b30941e57a194e557a123c5246f123f8b13acdd5c580f75590a52e72bda15632"}, } diff --git a/src/azure/b2c.gleam b/src/azure/b2c.gleam index 5759148..0c8d858 100644 --- a/src/azure/b2c.gleam +++ b/src/azure/b2c.gleam @@ -5,7 +5,6 @@ import gleam/json import gleam/base -import gleam/hackney import gleam/uri import gleam/http import gleam/http/request @@ -16,6 +15,8 @@ import gleam/list import gleam/result import gleam/int import azure/utils +import helpers/config +import helpers/finch import helpers/crypto import helpers/uri as uri_helpers import helpers/parsing @@ -186,7 +187,8 @@ fn confirm( try resp = req - |> hackney.send() + |> finch.build() + |> finch.request(config.finch_server()) |> b2c_error("Confirm HTTP request failed.") try resp = case resp.status { @@ -286,7 +288,8 @@ fn run_req( ) -> Result(response.Response(String), B2CError) { try resp = req - |> hackney.send() + |> finch.build() + |> finch.request(config.finch_server()) |> b2c_error("HTTP request failed.") case resp.status { diff --git a/src/helpers/config.gleam b/src/helpers/config.gleam index c3caae7..ff7baf0 100644 --- a/src/helpers/config.gleam +++ b/src/helpers/config.gleam @@ -3,9 +3,8 @@ import gleam/dynamic import gleam/uri import helpers/application -fn app_name() -> atom.Atom { - assert Ok(name) = atom.from_string("geo_therminator") - name +pub fn finch_server() -> atom.Atom { + atom.create_from_string("Elixir.GeoTherminator.PumpAPI.HTTP") } pub fn api_timeout() -> Int { @@ -27,6 +26,11 @@ pub fn api_installations_url() -> uri.Uri { config_url("api_installations_url") } +fn app_name() -> atom.Atom { + assert Ok(name) = atom.from_string("geo_therminator") + name +} + fn config_url(config_key: String) -> uri.Uri { let url = application.fetch_env_angry(app_name(), atom.create_from_string(config_key)) diff --git a/src/helpers/finch.gleam b/src/helpers/finch.gleam new file mode 100644 index 0000000..9b40675 --- /dev/null +++ b/src/helpers/finch.gleam @@ -0,0 +1,120 @@ +import gleam/uri +import gleam/erlang/atom.{Atom} +import gleam/http +import gleam/http/request as gleam_request +import gleam/http/response as gleam_response +import gleam/dynamic.{Dynamic} +import helpers/keyword.{Keyword} +import helpers/config + +pub type Method { + Head + Get + Put + Post + Patch + Delete + Options +} + +pub type Headers = + List(#(String, String)) + +pub external type Request + +pub external type Response + +pub fn build(req: gleam_request.Request(String)) -> Request { + let opts = + keyword.init() + |> keyword.put_int(atom.create_from_string("timeout"), config.api_timeout()) + + let url_str = + req + |> gleam_request.to_uri() + |> uri.to_string() + + build_ext( + gleam_method_to_finch_method(req.method), + url_str, + req.headers, + req.body, + opts, + ) +} + +pub fn request( + req: Request, + server: Atom, +) -> Result(gleam_response.Response(String), Dynamic) { + try resp = do_request(req, server) + + Ok(gleam_response.Response( + body: response_body(resp), + headers: response_headers(resp), + status: response_status(resp), + )) +} + +pub external fn build_ext( + method: Method, + url: String, + headers: Headers, + body: String, + opts: Keyword, +) -> Request = + "Elixir.Finch" "build" + +external fn do_request(req: Request, server: Atom) -> Result(Response, Dynamic) = + "Elixir.Finch" "request" + +pub fn response_body(resp: Response) -> String { + get_response_body(resp, Body) +} + +pub fn response_headers(resp: Response) -> Headers { + get_response_headers(resp, Headers) +} + +pub fn response_status(resp: Response) -> Int { + get_response_status(resp, Status) +} + +fn gleam_method_to_finch_method(method: http.Method) -> Method { + case method { + http.Get -> Get + http.Head -> Head + http.Post -> Post + http.Put -> Put + http.Patch -> Patch + http.Delete -> Delete + http.Options -> Options + + // This should never happen + _other -> Get + } +} + +type ResponseBodyKey { + Body +} + +type ResponseHeadersKey { + Headers +} + +type ResponseStatusKey { + Status +} + +external fn get_response_body(resp: Response, key: ResponseBodyKey) -> String = + "Elixir.Map" "fetch!" + +external fn get_response_status(resp: Response, key: ResponseStatusKey) -> Int = + "Elixir.Map" "fetch!" + +external fn get_response_headers( + resp: Response, + key: ResponseHeadersKey, +) -> Headers = + "Elixir.Map" "fetch!" diff --git a/src/helpers/keyword.gleam b/src/helpers/keyword.gleam new file mode 100644 index 0000000..78ecc28 --- /dev/null +++ b/src/helpers/keyword.gleam @@ -0,0 +1,12 @@ +import gleam/erlang/atom.{Atom} + +pub external type Keyword + +pub external fn init() -> Keyword = + "Elixir.Keyword" "new" + +pub external fn put_int(data: Keyword, key: Atom, value: Int) -> Keyword = + "Elixir.Keyword" "put" + +pub external fn put_string(data: Keyword, key: Atom, value: String) -> Keyword = + "Elixir.Keyword" "put" diff --git a/src/pump_api/auth/api.gleam b/src/pump_api/auth/api.gleam index 310142d..59de141 100644 --- a/src/pump_api/auth/api.gleam +++ b/src/pump_api/auth/api.gleam @@ -1,6 +1,5 @@ import gleam/http/request import gleam/json -import gleam/hackney import gleam/result import gleam/dynamic import gleam/list @@ -12,6 +11,7 @@ import pump_api/http import helpers/config import helpers/date_time import helpers/parsing +import helpers/finch import azure/b2c.{B2CError} pub type ApiError { @@ -69,7 +69,8 @@ pub fn installation_info(user: User) -> Result(List(InstallationInfo), ApiError) fn run_req(req: request.Request(String)) { try resp = req - |> hackney.send() + |> finch.build() + |> finch.request(config.finch_server()) |> result.replace_error(ApiRequestFailed) try body = case resp.status {