diff --git a/.github/actions/elixir-setup/action.yml b/.github/actions/elixir-setup/action.yml index 8e8ae9e..54add5c 100644 --- a/.github/actions/elixir-setup/action.yml +++ b/.github/actions/elixir-setup/action.yml @@ -3,11 +3,9 @@ description: Checks out the code, configures Elixir, fetches dependencies, and m inputs: elixir-version: required: true - type: string description: Elixir version to set up otp-version: required: true - type: string description: OTP version to set up ################################################################# # Everything below this line is optional. @@ -18,32 +16,26 @@ inputs: ################################################################# build-deps: required: false - type: boolean - default: true + default: 'true' description: True if we should compile dependencies build-app: required: false - type: boolean - default: true + default: 'true' description: True if we should compile the application itself build-flags: required: false - type: string default: '--all-warnings' description: Flags to pass to mix compile install-rebar: required: false - type: boolean - default: true + default: 'true' description: By default, we will install Rebar (mix local.rebar --force). install-hex: required: false - type: boolean - default: true + default: 'true' description: By default, we will install Hex (mix local.hex --force). cache-key: required: false - type: string default: 'v1' description: If you need to reset the cache for some reason, you can change this key. outputs: @@ -64,7 +56,7 @@ runs: otp-version: ${{ inputs.otp-version }} - name: Get deps cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: deps/ key: deps-${{ inputs.cache-key }}-${{ runner.os }}-${{ hashFiles('**/mix.lock') }} @@ -72,7 +64,7 @@ runs: deps-${{ inputs.cache-key }}-${{ runner.os }}- - name: Get build cache - uses: actions/cache@v2 + uses: actions/cache@v4 id: build-cache with: path: _build/${{env.MIX_ENV}}/ @@ -81,7 +73,7 @@ runs: build-${{ inputs.cache-key }}-${{ runner.os }}-${{ inputs.otp-version }}-${{ inputs.elixir-version }}-${{ env.MIX_ENV }}- - name: Get Hex cache - uses: actions/cache@v2 + uses: actions/cache@v4 id: hex-cache with: path: ~/.hex diff --git a/.github/workflows/elixir-build-and-test.yml b/.github/workflows/elixir-build-and-test.yml index 1717aa3..6ee9b79 100644 --- a/.github/workflows/elixir-build-and-test.yml +++ b/.github/workflows/elixir-build-and-test.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Elixir Project uses: ./.github/actions/elixir-setup diff --git a/.github/workflows/elixir-dialyzer.yml b/.github/workflows/elixir-dialyzer.yml index d7a4674..da2c13f 100644 --- a/.github/workflows/elixir-dialyzer.yml +++ b/.github/workflows/elixir-dialyzer.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Elixir Project uses: ./.github/actions/elixir-setup @@ -34,7 +34,7 @@ jobs: # Don't cache PLTs based on mix.lock hash, as Dialyzer can incrementally update even old ones # Cache key based on Elixir & Erlang version (also useful when running in matrix) - name: Restore PLT cache - uses: actions/cache@v3 + uses: actions/cache@v4 id: plt_cache with: key: plt-${{ runner.os }}-${{ steps.beam.outputs.otp-version }}-${{ steps.beam.outputs.elixir-version }}-${{ hashFiles('**/mix.lock') }}-${{ hashFiles('**/*.ex') }} diff --git a/.github/workflows/elixir-quality-checks.yml b/.github/workflows/elixir-quality-checks.yml index f446052..3fd3a43 100644 --- a/.github/workflows/elixir-quality-checks.yml +++ b/.github/workflows/elixir-quality-checks.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Elixir Project uses: ./.github/actions/elixir-setup diff --git a/.gitignore b/.gitignore index c92e289..386782e 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ priv/plts # dot examples are not commited until they're ready! # makes it nicer to live test some APIs when local scripts examples/.* +.DS_Store diff --git a/lib/nimrag/api.ex b/lib/nimrag/api.ex index 1d7c220..600841e 100644 --- a/lib/nimrag/api.ex +++ b/lib/nimrag/api.ex @@ -29,6 +29,30 @@ defmodule Nimrag.Api do end end + @spec put(Client.t(), Keyword.t()) :: + {:ok, Req.Response.t(), Client.t()} | {:error, Req.Response.t()} + def put(%Client{} = client, opts) do + client + |> req(opts) + |> Req.put() + |> case do + {:ok, resp} -> {:ok, resp, Req.Response.get_private(resp, :client)} + {:error, error} -> {:error, error} + end + end + + @spec post(Client.t(), Keyword.t()) :: + {:ok, Req.Response.t(), Client.t()} | {:error, Req.Response.t()} + def post(%Client{} = client, opts) do + client + |> req(opts) + |> Req.post() + |> case do + {:ok, resp} -> {:ok, resp, Req.Response.get_private(resp, :client)} + {:error, error} -> {:error, error} + end + end + @spec response_as_data({:ok, Req.Response.t(), Client.t()}, data_module :: atom()) :: {:ok, any(), Client.t()} | {:error, Req.Response.t()} @spec response_as_data({:error, any()}, data_module :: atom()) :: {:error, any()} diff --git a/lib/nimrag/auth.ex b/lib/nimrag/auth.ex index 1ce704b..de75661 100644 --- a/lib/nimrag/auth.ex +++ b/lib/nimrag/auth.ex @@ -80,7 +80,7 @@ defmodule Nimrag.Auth do now = DateTime.utc_now() - {:ok, response} = + result = client.connectapi |> Req.Request.put_header("Authorization", oauth) |> Req.get( @@ -89,18 +89,25 @@ defmodule Nimrag.Auth do user_agent: @mobile_user_agent ) - %{"oauth_token" => token, "oauth_token_secret" => secret} = - query = URI.decode_query(response.body) - - {:ok, - %OAuth1Token{ - oauth_token: token, - oauth_token_secret: secret, - domain: client.domain, - mfa_token: query["mfa_token"] || "", - # TODO: OAuth1Token, Is that 365 days true with MFA active? We'll wait and see! - expires_at: DateTime.add(now, 365, :day) - }} + with {:ok, %{status: 200} = response} <- result, + query = URI.decode_query(response.body), + {:ok, token} <- Map.fetch(query, "oauth_token"), + {:ok, secret} <- Map.fetch(query, "oauth_token_secret") do + {:ok, + %OAuth1Token{ + oauth_token: token, + oauth_token_secret: secret, + domain: client.domain, + mfa_token: query["mfa_token"] || "", + # TODO: OAuth1Token, Is that 365 days true with MFA active? We'll wait and see! + expires_at: DateTime.add(now, 365, :day) + }} + else + {:ok, %{status: 429} = response} -> {:error, {:rate_limited, response}} + {:ok, response} -> {:error, {:get_oauth1_token, response}} + {:error, _} = error -> error + :error -> {:error, :invalid_oauth1_response} + end end def maybe_refresh_oauth2_token(%Client{} = client, opts \\ []) do @@ -196,7 +203,10 @@ defmodule Nimrag.Auth do uri = response |> get_location() |> URI.parse() sso.client - |> Req.Request.put_header("cookie", Enum.uniq(cookie ++ get_cookie(response))) + |> Req.Request.put_header( + "cookie", + Enum.uniq(cookie ++ get_cookie(response)) |> Enum.join("; ") + ) |> Req.Request.put_header( "referer", "#{sso.url}/verifyMFA/loginEnterMfaCode" @@ -295,7 +305,7 @@ defmodule Nimrag.Auth do defp signin_req(sso, %Req.Response{} = prev_resp) do sso.client - |> Req.Request.put_header("cookie", get_cookie(prev_resp)) + |> Req.Request.put_header("cookie", get_cookie(prev_resp) |> Enum.join("; ")) |> Req.Request.put_header("referer", "#{sso.url}/embed") |> Req.get( url: "/signin", @@ -307,7 +317,7 @@ defmodule Nimrag.Auth do defp submit_signin_req(sso, %Req.Response{} = prev_resp, credentials) do with {:ok, csrf_token} <- get_csrf_token(prev_resp) do sso.client - |> Req.Request.put_header("cookie", get_cookie(prev_resp)) + |> Req.Request.put_header("cookie", get_cookie(prev_resp) |> Enum.join("; ")) |> Req.Request.put_header("referer", "#{sso.url}/signin") |> Req.post( url: "/signin",