From 80381896938e7cbee1487d96318535b069b191ce Mon Sep 17 00:00:00 2001 From: Eka Putra Date: Tue, 3 Dec 2024 22:44:08 +0800 Subject: [PATCH 1/2] started refactoring --- README.md | 12 +- lib/api2pdf.ex | 106 +++++++++++++----- lib/api2pdf/chrome.ex | 15 ++- lib/api2pdf/client.ex | 16 ++- lib/api2pdf/libre_office.ex | 20 +++- .../model/balance_check_success_response.ex | 18 --- lib/api2pdf/wkhtml.ex | 6 +- mix.exs | 2 +- test/api2pdf_test.exs | 85 +++++++++----- 9 files changed, 179 insertions(+), 101 deletions(-) delete mode 100644 lib/api2pdf/model/balance_check_success_response.ex diff --git a/README.md b/README.md index 10ea707..54b0a61 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Api2pdf -Unofficial API client for PDF generator/converter service https://www.api2pdf.com. +API client for PDF generator/converter service https://www.api2pdf.com. -At this point only features that are related to PDF generation and conversion are supported as those are the ones that I'm currently using personally. +At this point only features that are related to PDF generation and conversion that are supported as those are the ones that I'm currently using. -Contribution are welcomed to add missing features or simply open a ticket for feature requests. If you're looking for API clients in other languages please check their [official repository](https://github.com/Api2Pdf). +Contribution are welcomed to add/fix features or simply open a ticket for feature requests or bug report. If you're looking for API clients in other languages please check their [official repository](https://github.com/Api2Pdf). Below are lists of features that are supported and those that are not, grouped by their engine: @@ -37,9 +37,9 @@ Below are lists of features that are supported and those that are not, grouped b ### Utility Commands - [x] check_balance (`Api2pdf.check_balance/1`) -- [ ] delete_file +- [x] delete_file - [ ] zip_files -- [ ] api_status +- [x] api_status ## Installation @@ -48,7 +48,7 @@ Add `api2pdf` to your list of dependencies in `mix.exs`: ```elixir def deps do [ - {:api2pdf, "~> 0.2"} + {:api2pdf, "~> 0.3"} ] end ``` diff --git a/lib/api2pdf.ex b/lib/api2pdf.ex index 3894c0f..a925f34 100644 --- a/lib/api2pdf.ex +++ b/lib/api2pdf.ex @@ -1,23 +1,14 @@ defmodule Api2pdf do @moduledoc File.read!("README.md") - alias Api2pdf.Model.{ - ApiSuccessResponse, - BalanceCheckSuccessResponse - } - - @spec make_post_request(String.t(), struct, keyword) :: - {:error, any} | {:ok, ApiSuccessResponse.t()} | {:ok, BalanceCheckSuccessResponse.t()} - def make_post_request(endpoint, payload, options \\ []) do - client = Application.get_env(:api2pdf, :client, Api2pdf.Client) - client.post_request(endpoint, payload, options) |> handle_response() - end + alias Api2pdf.Model.ApiSuccessResponse - @spec make_get_request(String.t(), keyword) :: - {:error, any} | {:ok, ApiSuccessResponse.t()} | {:ok, BalanceCheckSuccessResponse.t()} - def make_get_request(endpoint, options \\ []) do - client = Application.get_env(:api2pdf, :client, Api2pdf.Client) - client.get_request(endpoint, options) |> handle_response() + @doc """ + Returns HTTP client. + """ + @spec http_client() :: any() + def http_client() do + Application.get_env(:api2pdf, :client, Api2pdf.Client) end @doc """ @@ -28,31 +19,86 @@ defmodule Api2pdf do ## Examples ```elixir # api_key in config.exs - Api2pdf.check_balance() + {:ok, 5.0} = Api2pdf.balance() + + # OR, api_key as option + {:ok, 5.0} = Api2pdf.balance(api_key: "YOUR-API-KEY") + ``` + """ + @spec balance(keyword) :: {:error, any} | {:ok, number()} + def balance(options \\ []) do + http_client().get_request("/balance", options) + |> handle_response() + end + + @doc """ + Api2pdf API health check. + + https://app.swaggerhub.com/apis-docs/api2pdf/api2pdf/2.0.0#/Utility%20Commands/statusCheck + + ## Examples + ```elixir + # api_key in config.exs + :ok = Api2pdf.status() + + # OR, api_key as option + :ok = Api2pdf.status(api_key: "YOUR-API-KEY") + ``` + """ + @spec status(keyword) :: :ok | :error + def status(options \\ []) do + http_client().get_request("/status", options) + |> handle_response() + |> case do + {:ok, _} -> :ok + _ -> :error + end + end + + @doc """ + Delete a file on command instead of waiting 24 hours for self-delete. + + https://app.swaggerhub.com/apis-docs/api2pdf/api2pdf/2.0.0#/Utility%20Commands/fileDELETE + + ## Examples + ```elixir + response_id = "857af41a-b382-4c61-ace4-95be78dcd605" + + # api_key in config.exs + {:ok, _} = Api2pdf.delete_file(response_id) # OR, api_key as option - Api2pdf.check_balance(api_key: "YOUR-API-KEY") + {:ok, _} = Api2pdf.delete_file(response_id, api_key: "YOUR-API-KEY") ``` """ - @spec check_balance(keyword) :: - {:error, any} | {:ok, BalanceCheckSuccessResponse.t()} - def check_balance(options \\ []), do: make_get_request("/balance", options) + @spec delete_file(String.t(), keyword) :: {:ok, ApiSuccessResponse.t()} | {:error, any()} + def delete_file(response_id, options \\ []) do + http_client().delete_request("/file/#{response_id}", options) + |> handle_response() + end + + @doc """ + HTTP response handling. + """ + def handle_response(%{body: "OK"}), do: {:ok, "OK"} - # Response handling - defp handle_response(%{body: %{"UserBalance" => _} = body}), - do: {:ok, BalanceCheckSuccessResponse.from_body(body)} + def handle_response(%{body: %{"UserBalance" => balance}}) when is_number(balance), + do: {:ok, balance} + + def handle_response(%{body: %{"FileUrl" => file_url} = body}) when is_binary(file_url), + do: {:ok, ApiSuccessResponse.from_body(body)} - defp handle_response(%{body: %{"FileUrl" => file_url} = body}) when is_binary(file_url), + def handle_response(%{body: %{"Success" => true} = body}), do: {:ok, ApiSuccessResponse.from_body(body)} - defp handle_response(%{body: %{"Error" => nil} = body}), + def handle_response(%{body: %{"Error" => nil} = body}), do: {:ok, ApiSuccessResponse.from_body(body)} - defp handle_response(%{body: %{"Error" => error}}), do: {:error, error} - defp handle_response(%{body: error}) when is_binary(error), do: {:error, error} - defp handle_response({:error, _} = error), do: error + def handle_response(%{body: %{"Error" => error}}), do: {:error, error} + def handle_response(%{body: error}) when is_binary(error), do: {:error, error} + def handle_response({:error, _} = error), do: error - defp handle_response(_) do + def handle_response(_) do {:error, "unknown response format"} end end diff --git a/lib/api2pdf/chrome.ex b/lib/api2pdf/chrome.ex index e639551..7190b3b 100644 --- a/lib/api2pdf/chrome.ex +++ b/lib/api2pdf/chrome.ex @@ -7,7 +7,8 @@ defmodule Api2pdf.Chrome do ChromeHtmlToImageRequest, ChromeHtmlToPdfRequest, ChromeUrlToImageRequest, - ChromeUrlToPdfRequest + ChromeUrlToPdfRequest, + ApiSuccessResponse } @doc """ @@ -103,18 +104,22 @@ defmodule Api2pdf.Chrome do def request(payload, options \\ []) def request(%ChromeHtmlToImageRequest{} = payload, options) do - Api2pdf.make_post_request("/chrome/image/html", payload, options) + Api2pdf.http_client().post_request("/chrome/image/html", payload, options) + |> Api2pdf.handle_response() end def request(%ChromeHtmlToPdfRequest{} = payload, options) do - Api2pdf.make_post_request("/chrome/pdf/html", payload, options) + Api2pdf.http_client().post_request("/chrome/pdf/html", payload, options) + |> Api2pdf.handle_response() end def request(%ChromeUrlToImageRequest{} = payload, options) do - Api2pdf.make_post_request("/chrome/image/url", payload, options) + Api2pdf.http_client().post_request("/chrome/image/url", payload, options) + |> Api2pdf.handle_response() end def request(%ChromeUrlToPdfRequest{} = payload, options) do - Api2pdf.make_post_request("/chrome/pdf/url", payload, options) + Api2pdf.http_client().post_request("/chrome/pdf/url", payload, options) + |> Api2pdf.handle_response() end end diff --git a/lib/api2pdf/client.ex b/lib/api2pdf/client.ex index 498b1fb..3327522 100644 --- a/lib/api2pdf/client.ex +++ b/lib/api2pdf/client.ex @@ -2,6 +2,7 @@ defmodule Api2pdf.ClientBehaviour do @callback post_request(url :: String.t(), payload :: struct, options :: keyword) :: {:error, any} | map @callback get_request(url :: String.t(), options :: keyword) :: {:error, any} | map + @callback delete_request(url :: String.t(), options :: keyword) :: {:error, any} | map end defmodule Api2pdf.Client do @@ -56,11 +57,18 @@ defmodule Api2pdf.Client do @spec get_request(String.t(), keyword) :: {:error, any} | map def get_request(endpoint, options \\ []) do client = make_client(options) - api_key = read_config(options, :api_key, "") - # put apikey into the query params - query = Keyword.get(options, :query, []) |> Keyword.put(:apikey, api_key) - case Tesla.get(client, endpoint, query) do + case Tesla.get(client, endpoint) do + {:ok, resp} -> resp + err -> err + end + end + + @spec delete_request(String.t(), keyword) :: {:error, any} | map + def delete_request(endpoint, options \\ []) do + client = make_client(options) + + case Tesla.delete(client, endpoint) do {:ok, resp} -> resp err -> err end diff --git a/lib/api2pdf/libre_office.ex b/lib/api2pdf/libre_office.ex index fa3173d..face753 100644 --- a/lib/api2pdf/libre_office.ex +++ b/lib/api2pdf/libre_office.ex @@ -20,7 +20,9 @@ defmodule Api2pdf.LibreOffice do @spec any_to_pdf(LibreOfficeRequest.t(), keyword) :: {:error, any} | {:ok, ApiSuccessResponse.t()} def any_to_pdf(%LibreOfficeRequest{} = payload, options \\ []), - do: Api2pdf.make_post_request("/libreoffice/any-to-pdf", payload, options) + do: + Api2pdf.http_client().post_request("/libreoffice/any-to-pdf", payload, options) + |> Api2pdf.handle_response() @doc """ Generate an image of the first page of a PDF or Office Document. @@ -36,7 +38,9 @@ defmodule Api2pdf.LibreOffice do @spec any_to_image(LibreOfficeRequest.t(), keyword) :: {:error, any} | {:ok, ApiSuccessResponse.t()} def any_to_image(%LibreOfficeRequest{} = payload, options \\ []), - do: Api2pdf.make_post_request("/libreoffice/thumbnail", payload, options) + do: + Api2pdf.http_client().post_request("/libreoffice/thumbnail", payload, options) + |> Api2pdf.handle_response() @doc """ Convert a PDF file to HTML using LibreOffice. **Limitation is that images will be lost**. @@ -50,7 +54,9 @@ defmodule Api2pdf.LibreOffice do @spec pdf_to_html(LibreOfficeRequest.t(), keyword) :: {:error, any} | {:ok, ApiSuccessResponse.t()} def pdf_to_html(%LibreOfficeRequest{} = payload, options \\ []), - do: Api2pdf.make_post_request("/libreoffice/pdf-to-html", payload, options) + do: + Api2pdf.http_client().post_request("/libreoffice/pdf-to-html", payload, options) + |> Api2pdf.handle_response() @doc """ Convert HTML to `.docx` format using LibreOffice. @@ -64,7 +70,9 @@ defmodule Api2pdf.LibreOffice do @spec html_to_docx(LibreOfficeRequest.t(), keyword) :: {:error, any} | {:ok, ApiSuccessResponse.t()} def html_to_docx(%LibreOfficeRequest{} = payload, options \\ []), - do: Api2pdf.make_post_request("/libreoffice/html-to-docx", payload, options) + do: + Api2pdf.http_client().post_request("/libreoffice/html-to-docx", payload, options) + |> Api2pdf.handle_response() @doc """ Convert HTML to `.xlsx` using LibreOffice. @@ -78,5 +86,7 @@ defmodule Api2pdf.LibreOffice do @spec html_to_xlsx(LibreOfficeRequest.t(), keyword) :: {:error, any} | {:ok, ApiSuccessResponse.t()} def html_to_xlsx(%LibreOfficeRequest{} = payload, options \\ []), - do: Api2pdf.make_post_request("/libreoffice/html-to-xlsx", payload, options) + do: + Api2pdf.http_client().post_request("/libreoffice/html-to-xlsx", payload, options) + |> Api2pdf.handle_response() end diff --git a/lib/api2pdf/model/balance_check_success_response.ex b/lib/api2pdf/model/balance_check_success_response.ex deleted file mode 100644 index 6ba875c..0000000 --- a/lib/api2pdf/model/balance_check_success_response.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule Api2pdf.Model.BalanceCheckSuccessResponse do - @moduledoc """ - https://app.swaggerhub.com/apis-docs/api2pdf/api2pdf/2.0.0#/BalanceCheckSuccess - """ - defstruct [:UserBalance] - - @type t :: %__MODULE__{ - UserBalance: number() - } - - @doc """ - Convert body from map to `Api2pdf.Model.BalanceCheckSuccessResponse` struct. - """ - @spec from_body(map) :: __MODULE__.t() - def from_body(body) do - %__MODULE__{UserBalance: Map.get(body, "UserBalance")} - end -end diff --git a/lib/api2pdf/wkhtml.ex b/lib/api2pdf/wkhtml.ex index 97d2835..f03c8b2 100644 --- a/lib/api2pdf/wkhtml.ex +++ b/lib/api2pdf/wkhtml.ex @@ -63,10 +63,12 @@ defmodule Api2pdf.Wkhtml do def request(payload, options \\ []) def request(%WkhtmlHtmlToPdfRequest{} = payload, options) do - Api2pdf.make_post_request("/wkhtml/pdf/html", payload, options) + Api2pdf.http_client().post_request("/wkhtml/pdf/html", payload, options) + |> Api2pdf.handle_response() end def request(%WkhtmlUrlToPdfRequest{} = payload, options) do - Api2pdf.make_post_request("/wkhtml/pdf/url", payload, options) + Api2pdf.http_client().post_request("/wkhtml/pdf/url", payload, options) + |> Api2pdf.handle_response() end end diff --git a/mix.exs b/mix.exs index d13184e..6f982db 100644 --- a/mix.exs +++ b/mix.exs @@ -6,7 +6,7 @@ defmodule Api2pdf.MixProject do def project do [ app: :api2pdf, - version: "0.2.1", + version: "0.3.0", elixir: "~> 1.13", start_permanent: Mix.env() == :prod, package: package(), diff --git a/test/api2pdf_test.exs b/test/api2pdf_test.exs index a722f8a..d41e6cd 100644 --- a/test/api2pdf_test.exs +++ b/test/api2pdf_test.exs @@ -5,48 +5,39 @@ defmodule Api2pdfTest do doctest Api2pdf - describe "make_post_request/3" do - test "it success (Error == nil)" do - expect(ClientMock, :post_request, fn url, payload, opts -> - assert url == "/test" - assert payload == %{} - assert opts == [tag: "test"] - %{body: %{"Error" => nil}} - end) - - assert {:ok, _} = Api2pdf.make_post_request("/test", %{}, tag: "test") - end - - test "it success (no Error, but FileUrl present)" do - expect(ClientMock, :post_request, fn url, payload, opts -> - assert url == "/test" - assert payload == %{} - assert opts == [tag: "test"] - %{body: %{"FileUrl" => "test"}} + describe "status/1" do + test "it success" do + expect(ClientMock, :get_request, fn url, opts -> + assert url == "/status" + assert opts == [] + %{body: "OK"} end) - assert {:ok, _} = Api2pdf.make_post_request("/test", %{}, tag: "test") + assert :ok = Api2pdf.status() end test "it failed (connection error)" do - expect(ClientMock, :post_request, fn _, _, _ -> + expect(ClientMock, :get_request, fn url, opts -> + assert url == "/status" + assert opts == [] {:error, :timeout} end) - assert {:error, :timeout} = Api2pdf.make_post_request("/test", %{}, tag: "test") + assert :error = Api2pdf.status() end - test "it failed (validation error)" do - expect(ClientMock, :post_request, fn _, _, _ -> - {:error, "Api key TEST invalid"} + test "it failed (api error)" do + expect(ClientMock, :get_request, fn url, opts -> + assert url == "/status" + assert opts == [] + %{body: "Invalid ApiKey"} end) - assert {:error, "Api key TEST invalid"} = - Api2pdf.make_post_request("/test", %{}, tag: "test") + assert :error = Api2pdf.status() end end - describe "check_balance/1" do + describe "balance/1" do test "it success" do expect(ClientMock, :get_request, fn url, opts -> assert url == "/balance" @@ -54,7 +45,7 @@ defmodule Api2pdfTest do %{body: %{"UserBalance" => 10.2}} end) - assert {:ok, %{UserBalance: 10.2}} = Api2pdf.check_balance() + assert {:ok, 10.2} = Api2pdf.balance() end test "it failed (connection error)" do @@ -64,7 +55,7 @@ defmodule Api2pdfTest do {:error, :timeout} end) - assert {:error, :timeout} = Api2pdf.check_balance() + assert {:error, :timeout} = Api2pdf.balance() end test "it failed (api error)" do @@ -74,7 +65,41 @@ defmodule Api2pdfTest do %{body: "Invalid ApiKey"} end) - assert {:error, "Invalid ApiKey"} = Api2pdf.check_balance() + assert {:error, "Invalid ApiKey"} = Api2pdf.balance() + end + end + + describe "delete_file/2" do + @response_id "test-id" + + test "it success" do + expect(ClientMock, :delete_request, fn url, opts -> + assert url == "/file/#{@response_id}" + assert opts == [] + %{body: %{"Success" => true}} + end) + + assert {:ok, _} = Api2pdf.delete_file(@response_id) + end + + test "it failed (connection error)" do + expect(ClientMock, :delete_request, fn url, opts -> + assert url == "/file/#{@response_id}" + assert opts == [] + {:error, :timeout} + end) + + assert {:error, :timeout} = Api2pdf.delete_file(@response_id) + end + + test "it failed (api error)" do + expect(ClientMock, :delete_request, fn url, opts -> + assert url == "/file/#{@response_id}" + assert opts == [] + %{body: "Invalid ApiKey"} + end) + + assert {:error, "Invalid ApiKey"} = Api2pdf.delete_file(@response_id) end end end From c04b2dae5bb66e1d567ef86144a65176b93b9c0e Mon Sep 17 00:00:00 2001 From: Eka Putra Date: Sun, 15 Dec 2024 14:13:38 +0800 Subject: [PATCH 2/2] added utility apis --- README.md | 8 ++-- lib/api2pdf.ex | 33 ++++++++++++++--- lib/api2pdf/chrome.ex | 13 +++---- lib/api2pdf/libre_office.ex | 36 ++++++++---------- lib/api2pdf/model/zip_files_request.ex | 33 +++++++++++++++++ lib/api2pdf/wkhtml.ex | 7 ++-- test/api2pdf/model/zip_files_request_test.exs | 31 ++++++++++++++++ test/api2pdf_test.exs | 37 +++++++++++++++++++ 8 files changed, 157 insertions(+), 41 deletions(-) create mode 100644 lib/api2pdf/model/zip_files_request.ex create mode 100644 test/api2pdf/model/zip_files_request_test.exs diff --git a/README.md b/README.md index 54b0a61..6059a59 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,10 @@ Below are lists of features that are supported and those that are not, grouped b - [ ] generate_barcode ### Utility Commands -- [x] check_balance (`Api2pdf.check_balance/1`) -- [x] delete_file -- [ ] zip_files -- [x] api_status +- [x] check_balance (`Api2pdf.balance/1`) +- [x] delete_file (`Api2pdf.delete_file/2`) +- [x] zip_files (`Api2pdf.zip_files/2`) +- [x] api_status (`Api2pdf.status/1`) ## Installation diff --git a/lib/api2pdf.ex b/lib/api2pdf.ex index a925f34..55af265 100644 --- a/lib/api2pdf.ex +++ b/lib/api2pdf.ex @@ -1,7 +1,7 @@ defmodule Api2pdf do @moduledoc File.read!("README.md") - alias Api2pdf.Model.ApiSuccessResponse + alias Api2pdf.Model.{ApiSuccessResponse, ZipFilesRequest} @doc """ Returns HTTP client. @@ -27,8 +27,7 @@ defmodule Api2pdf do """ @spec balance(keyword) :: {:error, any} | {:ok, number()} def balance(options \\ []) do - http_client().get_request("/balance", options) - |> handle_response() + http_client().get_request("/balance", options) |> handle_response() end @doc """ @@ -73,8 +72,32 @@ defmodule Api2pdf do """ @spec delete_file(String.t(), keyword) :: {:ok, ApiSuccessResponse.t()} | {:error, any()} def delete_file(response_id, options \\ []) do - http_client().delete_request("/file/#{response_id}", options) - |> handle_response() + http_client().delete_request("/file/#{response_id}", options) |> handle_response() + end + + @doc """ + Create a ZIP file from a list of files. + + https://app.swaggerhub.com/apis-docs/api2pdf/api2pdf/2.0.0#/Utility%20Commands/filesZip + + ## Examples + ```elixir + alias Api2pdf.Model.ZipFilesRequest + + files = ZipFilesRequest.new() + |> ZipFilesRequest.add("https://example.com/halo.png") + |> ZipFilesRequest.add("https://example.com/hola.png", "new-name.png") + + # api_key in config.exs + {:ok, _} = Api2pdf.zip_files(files) + + # OR, api_key as option + {:ok, _} = Api2pdf.zip_files(files, api_key: "YOUR-API-KEY") + ``` + """ + @spec zip_files(ZipFilesRequest.t(), keyword) :: {:error, any} | {:ok, ApiSuccessResponse.t()} + def zip_files(files, options \\ []) do + http_client().post_request("/zip?outputBinary=false", files, options) |> handle_response() end @doc """ diff --git a/lib/api2pdf/chrome.ex b/lib/api2pdf/chrome.ex index 7190b3b..97d57e8 100644 --- a/lib/api2pdf/chrome.ex +++ b/lib/api2pdf/chrome.ex @@ -2,6 +2,7 @@ defmodule Api2pdf.Chrome do @moduledoc """ Convert HTML document, web page to PDF or Image using Headless Chrome backend. """ + import Api2pdf, only: [http_client: 0, handle_response: 1] alias Api2pdf.Model.{ ChromeHtmlToImageRequest, @@ -104,22 +105,18 @@ defmodule Api2pdf.Chrome do def request(payload, options \\ []) def request(%ChromeHtmlToImageRequest{} = payload, options) do - Api2pdf.http_client().post_request("/chrome/image/html", payload, options) - |> Api2pdf.handle_response() + http_client().post_request("/chrome/image/html", payload, options) |> handle_response() end def request(%ChromeHtmlToPdfRequest{} = payload, options) do - Api2pdf.http_client().post_request("/chrome/pdf/html", payload, options) - |> Api2pdf.handle_response() + http_client().post_request("/chrome/pdf/html", payload, options) |> handle_response() end def request(%ChromeUrlToImageRequest{} = payload, options) do - Api2pdf.http_client().post_request("/chrome/image/url", payload, options) - |> Api2pdf.handle_response() + http_client().post_request("/chrome/image/url", payload, options) |> handle_response() end def request(%ChromeUrlToPdfRequest{} = payload, options) do - Api2pdf.http_client().post_request("/chrome/pdf/url", payload, options) - |> Api2pdf.handle_response() + http_client().post_request("/chrome/pdf/url", payload, options) |> handle_response() end end diff --git a/lib/api2pdf/libre_office.ex b/lib/api2pdf/libre_office.ex index face753..812af6a 100644 --- a/lib/api2pdf/libre_office.ex +++ b/lib/api2pdf/libre_office.ex @@ -2,6 +2,7 @@ defmodule Api2pdf.LibreOffice do @moduledoc """ Convert documents from and to various formats using Libre Office backend. """ + import Api2pdf, only: [http_client: 0, handle_response: 1] alias Api2pdf.Model.{ LibreOfficeRequest, @@ -19,10 +20,9 @@ defmodule Api2pdf.LibreOffice do """ @spec any_to_pdf(LibreOfficeRequest.t(), keyword) :: {:error, any} | {:ok, ApiSuccessResponse.t()} - def any_to_pdf(%LibreOfficeRequest{} = payload, options \\ []), - do: - Api2pdf.http_client().post_request("/libreoffice/any-to-pdf", payload, options) - |> Api2pdf.handle_response() + def any_to_pdf(%LibreOfficeRequest{} = payload, options \\ []) do + http_client().post_request("/libreoffice/any-to-pdf", payload, options) |> handle_response() + end @doc """ Generate an image of the first page of a PDF or Office Document. @@ -37,10 +37,9 @@ defmodule Api2pdf.LibreOffice do """ @spec any_to_image(LibreOfficeRequest.t(), keyword) :: {:error, any} | {:ok, ApiSuccessResponse.t()} - def any_to_image(%LibreOfficeRequest{} = payload, options \\ []), - do: - Api2pdf.http_client().post_request("/libreoffice/thumbnail", payload, options) - |> Api2pdf.handle_response() + def any_to_image(%LibreOfficeRequest{} = payload, options \\ []) do + http_client().post_request("/libreoffice/thumbnail", payload, options) |> handle_response() + end @doc """ Convert a PDF file to HTML using LibreOffice. **Limitation is that images will be lost**. @@ -53,10 +52,9 @@ defmodule Api2pdf.LibreOffice do """ @spec pdf_to_html(LibreOfficeRequest.t(), keyword) :: {:error, any} | {:ok, ApiSuccessResponse.t()} - def pdf_to_html(%LibreOfficeRequest{} = payload, options \\ []), - do: - Api2pdf.http_client().post_request("/libreoffice/pdf-to-html", payload, options) - |> Api2pdf.handle_response() + def pdf_to_html(%LibreOfficeRequest{} = payload, options \\ []) do + http_client().post_request("/libreoffice/pdf-to-html", payload, options) |> handle_response() + end @doc """ Convert HTML to `.docx` format using LibreOffice. @@ -69,10 +67,9 @@ defmodule Api2pdf.LibreOffice do """ @spec html_to_docx(LibreOfficeRequest.t(), keyword) :: {:error, any} | {:ok, ApiSuccessResponse.t()} - def html_to_docx(%LibreOfficeRequest{} = payload, options \\ []), - do: - Api2pdf.http_client().post_request("/libreoffice/html-to-docx", payload, options) - |> Api2pdf.handle_response() + def html_to_docx(%LibreOfficeRequest{} = payload, options \\ []) do + http_client().post_request("/libreoffice/html-to-docx", payload, options) |> handle_response() + end @doc """ Convert HTML to `.xlsx` using LibreOffice. @@ -85,8 +82,7 @@ defmodule Api2pdf.LibreOffice do """ @spec html_to_xlsx(LibreOfficeRequest.t(), keyword) :: {:error, any} | {:ok, ApiSuccessResponse.t()} - def html_to_xlsx(%LibreOfficeRequest{} = payload, options \\ []), - do: - Api2pdf.http_client().post_request("/libreoffice/html-to-xlsx", payload, options) - |> Api2pdf.handle_response() + def html_to_xlsx(%LibreOfficeRequest{} = payload, options \\ []) do + http_client().post_request("/libreoffice/html-to-xlsx", payload, options) |> handle_response() + end end diff --git a/lib/api2pdf/model/zip_files_request.ex b/lib/api2pdf/model/zip_files_request.ex new file mode 100644 index 0000000..e98b06c --- /dev/null +++ b/lib/api2pdf/model/zip_files_request.ex @@ -0,0 +1,33 @@ +defmodule Api2pdf.Model.ZipFilesRequest do + @moduledoc """ + Compose payload for zipping multiple files into a single archive. + + https://app.swaggerhub.com/apis-docs/api2pdf/api2pdf/2.0.0#/UtilityZipAllFiles + + ## Examples + ```elixir + files = ZipFilesRequest.new() + |> ZipFilesRequest.add("https://example.com/halo.png") + |> ZipFilesRequest.add("https://example.com/hola.png", "new-name.png") + ``` + """ + alias Api2pdf.Model.ZipFilesRequest + @enforce_keys [:files] + + defstruct [:files] + + @type file :: %{ + url: String.t(), + fileName: String.t() + } + @type t :: %__MODULE__{ + files: [file] + } + + def new(), do: %__MODULE__{files: []} + + def add(%ZipFilesRequest{files: files} = zipfiles, url, name \\ nil) when is_binary(url) do + name = name || Path.basename(url) + %{zipfiles | files: [%{url: url, fileName: name} | files]} + end +end diff --git a/lib/api2pdf/wkhtml.ex b/lib/api2pdf/wkhtml.ex index f03c8b2..700b404 100644 --- a/lib/api2pdf/wkhtml.ex +++ b/lib/api2pdf/wkhtml.ex @@ -2,6 +2,7 @@ defmodule Api2pdf.Wkhtml do @moduledoc """ Convert HTML document or web page to PDF using Wkhtml backend. """ + import Api2pdf, only: [http_client: 0, handle_response: 1] alias Api2pdf.Model.{ ApiSuccessResponse, @@ -63,12 +64,10 @@ defmodule Api2pdf.Wkhtml do def request(payload, options \\ []) def request(%WkhtmlHtmlToPdfRequest{} = payload, options) do - Api2pdf.http_client().post_request("/wkhtml/pdf/html", payload, options) - |> Api2pdf.handle_response() + http_client().post_request("/wkhtml/pdf/html", payload, options) |> handle_response() end def request(%WkhtmlUrlToPdfRequest{} = payload, options) do - Api2pdf.http_client().post_request("/wkhtml/pdf/url", payload, options) - |> Api2pdf.handle_response() + http_client().post_request("/wkhtml/pdf/url", payload, options) |> handle_response() end end diff --git a/test/api2pdf/model/zip_files_request_test.exs b/test/api2pdf/model/zip_files_request_test.exs new file mode 100644 index 0000000..f1d0b06 --- /dev/null +++ b/test/api2pdf/model/zip_files_request_test.exs @@ -0,0 +1,31 @@ +defmodule Api2pdf.Model.ZipFilesRequestTest do + use ExUnit.Case + + alias Api2pdf.Model.ZipFilesRequest + + describe "ZipFilesRequest" do + test "new/0" do + assert %ZipFilesRequest{files: []} = ZipFilesRequest.new() + end + + test "add/2" do + files = + ZipFilesRequest.new() + |> ZipFilesRequest.add("https://example.com/halo.png") + |> ZipFilesRequest.add("https://example.com/hola.png", "new-name.png") + + assert %ZipFilesRequest{ + files: [ + %{ + url: "https://example.com/hola.png", + fileName: "new-name.png" + }, + %{ + url: "https://example.com/halo.png", + fileName: "halo.png" + } + ] + } = files + end + end +end diff --git a/test/api2pdf_test.exs b/test/api2pdf_test.exs index d41e6cd..1e4ff2a 100644 --- a/test/api2pdf_test.exs +++ b/test/api2pdf_test.exs @@ -1,6 +1,8 @@ defmodule Api2pdfTest do use ExUnit.Case import Mox + alias Api2pdf.Model.ZipFilesRequest + setup :verify_on_exit! doctest Api2pdf @@ -102,4 +104,39 @@ defmodule Api2pdfTest do assert {:error, "Invalid ApiKey"} = Api2pdf.delete_file(@response_id) end end + + describe "zip_files/2" do + @files ZipFilesRequest.new() |> ZipFilesRequest.add("https://example.com/halo.png") + + test "it success" do + expect(ClientMock, :post_request, fn url, payload, opts -> + assert url == "/zip?outputBinary=false" + assert payload == @files + assert opts == [] + %{body: %{"Success" => true}} + end) + + assert {:ok, _} = Api2pdf.zip_files(@files) + end + + test "it failed (connection error)" do + expect(ClientMock, :post_request, fn url, _payload, opts -> + assert url == "/zip?outputBinary=false" + assert opts == [] + {:error, :timeout} + end) + + assert {:error, :timeout} = Api2pdf.zip_files(@files) + end + + test "it failed (api error)" do + expect(ClientMock, :post_request, fn url, _payload, opts -> + assert url == "/zip?outputBinary=false" + assert opts == [] + %{body: "Invalid ApiKey"} + end) + + assert {:error, "Invalid ApiKey"} = Api2pdf.zip_files(@files) + end + end end