Current section

Files

Jump to
ex_urlbox lib ex_urlbox.ex
Raw

lib/ex_urlbox.ex

defmodule ExUrlbox do
@moduledoc """
A light wrapper for the Urlbox API.
**Compatible with Urlbox API Version:** `v1`
A list of options that you can pass into `ExUrlbox.get/3`, `ExUrlbox.post/3`, `ExUrlbox.head/3`, and `ExUrlbox.delete/3` can be found here:
https://urlbox.io/docs/options
"""
require Logger
alias ExUrlbox.Config
alias ExUrlbox.Utils
@default_options [format: "png"]
@default_client_timeout 30_000
@doc """
Send a request to Urlbox for a screenshot.
Refer to the official documentation for all the available options: https://urlbox.io/docs/options
**This action is `synchronous`. **
If you want to use an `async` flow that uses webhooks, use `ExUrlbox.post/3`.
**Function signature is: `url, [options], timeout`**
- Example request:
```
ExUrlbox.get("https://anthonymineo.com")
```
- Example with options:
```
ExUrlbox.get("https://anthonymineo.com", [format: "png", full_page: true])
```
- Example with timeout (5s):
```
ExUrlbox.get("https://anthonymineo.com", [], 5_000)
```
"""
@spec get(String.t(), list(), integer()) :: {:error, any()} | {:ok, Tesla.Env.t()}
def get(url, opts \\ @default_options, timeout \\ @default_client_timeout) when is_binary(url) and url != nil do
[{:url, url} | opts]
|> Utils.build_url()
|> client_get(timeout)
end
@doc false
def get, do: raise("No URL was passed in to screenshot!")
@doc """
Post a request to Urlbox for a screenshot.
You should provide a `webhook_url` option to receive a postback to once the screenshot render is complete.
**This action is `async`. **
- Example request with options containing `webhook_url` for a postback:
```
ExUrlbox.post("https://anthonymineo.com", [webhook_url: "https://app-waiting-for-incoming-post.com/"])
```
- Initial response from a post:
```
{
"renderId": "ebcbce5q-9657-435f-aeb6-5db207ee87b5",
"status": "created",
"statusUrl": "https://api.urlbox.io/render/ebcbce5q-9657-435f-aeb6-5db207ee87b5"
}
```
- If you dont provide a `webhook_url` you will need to poll the `statusUrl` for the status of your request:
```
{
"renderId": "ebcbce5q-9657-435f-aeb6-5db207ee87b5",
"status": "succeeded",
"renderUrl": "https://renders.urlbox.io/urlbox1/renders/a1d7g7d45f7am5a0a69cd3de/2022/5/7/ebcbce5q-9657-435f-aeb6-5db207ee87b5.png",
"size": 34748
}
```
- Example of a postback to `webhook_url`:
```
{
"event": "render.succeeded",
"renderId": "ebcbce5q-9657-435f-aeb6-5db207ee87b5",
"result": {
"renderUrl": "https://renders.urlbox.io/urlbox1/renders/a1d7g7d45f7am5a0a69cd3de/2022/5/7/ebcbce5q-9657-435f-aeb6-5db207ee87b5.png",
"size": 34748
},
"meta": {
"startTime":"2022-05-07T20:25:28.879Z",
"endTime":"2022-05-07T20:25:31.646Z"
}
}
```
"""
@spec post(String.t(), list(), integer()) :: {:error, any()} | {:ok, Tesla.Env.t()}
def post(url, opts \\ @default_options, timeout \\ @default_client_timeout) when is_binary(url) and url != nil do
if !Keyword.get(opts, :webhook_url) do
Logger.warning("webhook_url option was not found! You will need to poll the statusUrl from the post response for your results")
end
[{:url, url} | opts]
|> Enum.into(%{})
|> client_post(timeout)
end
@doc false
def post, do: raise("No URL was passed in to screenshot!")
@doc """
Urlbox will cache previously created screenshots for some time. Sending a delete request removes a previously created screenshot from the cache.
- Example request:
```
ExUrlbox.delete("https://anthonymineo.com")
```
"""
@spec delete(String.t(), list(), integer()) :: {:error, any()} | {:ok, Tesla.Env.t()}
def delete(url, opts \\ @default_options, timeout \\ @default_client_timeout) when is_binary(url) and url != nil do
[{:url, url} | opts]
|> Utils.build_url()
|> client_delete(timeout)
end
@doc false
def delete, do: raise("No URL was passed in to screenshot!")
@doc """
If you just want to get the response status/headers without pulling down the full response body.
- Example request:
```
ExUrlbox.head("https://anthonymineo.com")
```
"""
@spec head(String.t(), list(), integer()) :: {:error, any()} | {:ok, Tesla.Env.t()}
def head(url, opts \\ @default_options, timeout \\ @default_client_timeout) when is_binary(url) and url != nil do
[{:url, url} | opts]
|> Utils.build_url()
|> client_head(timeout)
end
@doc false
def head, do: raise("No URL was passed in to screenshot!")
# Setup Tesla client
@doc false
@spec client(integer(), bool()) :: Tesla.Client.t()
def client(timeout, is_post? \\ false) do
middleware = [
{Tesla.Middleware.Timeout, timeout: timeout},
Tesla.Middleware.JSON
]
|> determine_endpoint(is_post?)
# Pass timeout to Hackney adapter
adapter_opts = [recv_timeout: timeout]
# Use Config module to determine the appropriate adapter
adapter = ExUrlbox.Config.get_client_adapter(adapter_opts)
Tesla.client(middleware, adapter)
end
@doc false
@spec determine_endpoint(list(), boolean()) :: list()
defp determine_endpoint(middleware, is_post?) do
case is_post? do
true -> [{Tesla.Middleware.BaseUrl, Config.base_endpoint} | middleware] |> include_bearer_token()
false -> [{Tesla.Middleware.BaseUrl, Config.api_key_endpoint} | middleware]
end
end
@doc false
@spec include_bearer_token(list()) :: list()
defp include_bearer_token(middleware) do
[{Tesla.Middleware.BearerAuth, token: Config.api_secret} | middleware]
end
# Handle the HTTP GET
@doc false
@spec client_get(String.t(), integer()) :: {:error, any()} | {:ok, Tesla.Env.t()}
def client_get(opts, timeout) do
client(timeout)
|> Tesla.get(opts)
end
# Handle the HTTP DELETE
@doc false
@spec client_delete(String.t(), integer()) :: {:error, any()} | {:ok, Tesla.Env.t()}
def client_delete(opts, timeout) do
client(timeout)
|> Tesla.delete(opts)
end
# Handle the HTTP HEAD
@doc false
@spec client_head(String.t(), integer()) :: {:error, any()} | {:ok, Tesla.Env.t()}
def client_head(opts, timeout) do
client(timeout)
|> Tesla.head(opts)
end
# Handle the HTTP POST
@doc false
@spec client_post(String.t(), integer()) :: {:error, any()} | {:ok, Tesla.Env.t()}
def client_post(post_body, timeout) do
is_post? = true
client(timeout, is_post?)
|> Tesla.post(Config.api_post_endpoint, post_body)
end
end