diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..3b4db1a --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,5 @@ +[ + import_deps: [], + inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"], + subdirectories: [] +] diff --git a/lib/arc/actions/delete.ex b/lib/arc/actions/delete.ex index ffe24d5..9b7da5f 100644 --- a/lib/arc/actions/delete.ex +++ b/lib/arc/actions/delete.ex @@ -2,7 +2,7 @@ defmodule Arc.Actions.Delete do defmacro __using__(_) do quote do def delete(args), do: Arc.Actions.Delete.delete(__MODULE__, args) - + defoverridable [{:delete, 1}] end end @@ -26,12 +26,13 @@ defmodule Arc.Actions.Delete do defp do_delete(definition, {file, scope}) do if definition.async do definition.__versions - |> Enum.map(fn(r) -> async_delete_version(definition, r, {file, scope}) end) - |> Enum.each(fn(task) -> Task.await(task, version_timeout()) end) + |> Enum.map(fn r -> async_delete_version(definition, r, {file, scope}) end) + |> Enum.each(fn task -> Task.await(task, version_timeout()) end) else definition.__versions - |> Enum.map(fn(version) -> delete_version(definition, version, {file, scope}) end) + |> Enum.map(fn version -> delete_version(definition, version, {file, scope}) end) end + :ok end diff --git a/lib/arc/actions/store.ex b/lib/arc/actions/store.ex index 0fb70e3..42be727 100644 --- a/lib/arc/actions/store.ex +++ b/lib/arc/actions/store.ex @@ -17,39 +17,43 @@ defmodule Arc.Actions.Store do # Private # - defp put(_definition, { error = {:error, _msg}, _scope}), do: error - defp put(definition, {%Arc.File{}=file, scope}) do + defp put(_definition, {error = {:error, _msg}, _scope}), do: error + + defp put(definition, {%Arc.File{} = file, scope}) do case definition.validate({file, scope}) do true -> put_versions(definition, {file, scope}) - _ -> {:error, :invalid_file} + _ -> {:error, :invalid_file} end end defp put_versions(definition, {file, scope}) do if definition.async do definition.__versions - |> Enum.map(fn(r) -> async_process_version(definition, r, {file, scope}) end) - |> Enum.map(fn(task) -> Task.await(task, version_timeout()) end) + |> Enum.map(fn r -> async_process_version(definition, r, {file, scope}) end) + |> Enum.map(fn task -> Task.await(task, version_timeout()) end) |> ensure_all_success - |> Enum.map(fn({v, r}) -> async_put_version(definition, v, {r, scope}) end) - |> Enum.map(fn(task) -> Task.await(task, version_timeout()) end) + |> Enum.map(fn {v, r} -> async_put_version(definition, v, {r, scope}) end) + |> Enum.map(fn task -> Task.await(task, version_timeout()) end) |> handle_responses(file.file_name) else definition.__versions - |> Enum.map(fn(version) -> process_version(definition, version, {file, scope}) end) + |> Enum.map(fn version -> process_version(definition, version, {file, scope}) end) |> ensure_all_success - |> Enum.map(fn({version, result}) -> put_version(definition, version, {result, scope}) end) + |> Enum.map(fn {version, result} -> put_version(definition, version, {result, scope}) end) |> handle_responses(file.file_name) end end defp ensure_all_success(responses) do - errors = Enum.filter(responses, fn({_version, resp}) -> elem(resp, 0) == :error end) + errors = Enum.filter(responses, fn {_version, resp} -> elem(resp, 0) == :error end) if Enum.empty?(errors), do: responses, else: errors end defp handle_responses(responses, filename) do - errors = Enum.filter(responses, fn(resp) -> elem(resp, 0) == :error end) |> Enum.map(fn(err) -> elem(err, 1) end) + errors = + Enum.filter(responses, fn resp -> elem(resp, 0) == :error end) + |> Enum.map(fn err -> elem(err, 1) end) + if Enum.empty?(errors), do: {:ok, filename}, else: {:error, errors} end @@ -75,11 +79,17 @@ defmodule Arc.Actions.Store do defp put_version(definition, version, {result, scope}) do case result do - {:error, error} -> {:error, error} - {:ok, nil} -> {:ok, nil} + {:error, error} -> + {:error, error} + + {:ok, nil} -> + {:ok, nil} + {:ok, file} -> - file_name = Arc.Definition.Versioning.resolve_file_name(definition, version, {file, scope}) - file = %Arc.File{file | file_name: file_name} + file_name = + Arc.Definition.Versioning.resolve_file_name(definition, version, {file, scope}) + + file = %Arc.File{file | file_name: file_name} result = definition.__storage.put(definition, version, {file, scope}) result end diff --git a/lib/arc/actions/url.ex b/lib/arc/actions/url.ex index d254a6a..157575c 100644 --- a/lib/arc/actions/url.ex +++ b/lib/arc/actions/url.ex @@ -2,9 +2,9 @@ defmodule Arc.Actions.Url do defmacro __using__(_) do quote do def urls(file, options \\ []) do - Enum.into __MODULE__.__versions, %{}, fn(r) -> + Enum.into(__MODULE__.__versions(), %{}, fn r -> {r, __MODULE__.url(file, r, options)} - end + end) end def url(file), do: url(file, nil) @@ -21,8 +21,9 @@ defmodule Arc.Actions.Url do do: url(definition, file, Enum.at(definition.__versions, 0), options) # Transform standalone file into a tuple of {file, scope} - def url(definition, file, version, options) when is_binary(file) or is_map(file) or is_nil(file), - do: url(definition, {file, nil}, version, options) + def url(definition, file, version, options) + when is_binary(file) or is_map(file) or is_nil(file), + do: url(definition, {file, nil}, version, options) # Transform file-path into a map with a file_name key def url(definition, {file, scope}, version, options) when is_binary(file) do @@ -43,7 +44,9 @@ defmodule Arc.Actions.Url do defp build(definition, version, file_and_scope, options) do case Arc.Definition.Versioning.resolve_file_name(definition, version, file_and_scope) do - nil -> nil + nil -> + nil + _ -> definition.__storage.url(definition, version, file_and_scope, options) end diff --git a/lib/arc/definition/storage.ex b/lib/arc/definition/storage.ex index 150a4ee..c5d7312 100644 --- a/lib/arc/definition/storage.ex +++ b/lib/arc/definition/storage.ex @@ -13,7 +13,14 @@ defmodule Arc.Definition.Storage do def default_url(_), do: nil def __storage, do: Application.get_env(:arc, :storage, Arc.Storage.S3) - defoverridable [storage_dir: 2, filename: 2, validate: 1, default_url: 1, default_url: 2, __storage: 0, bucket: 0, asset_host: 0] + defoverridable storage_dir: 2, + filename: 2, + validate: 1, + default_url: 1, + default_url: 2, + __storage: 0, + bucket: 0, + asset_host: 0 @before_compile Arc.Definition.Storage end diff --git a/lib/arc/definition/versioning.ex b/lib/arc/definition/versioning.ex index 010ab99..395abfe 100644 --- a/lib/arc/definition/versioning.ex +++ b/lib/arc/definition/versioning.ex @@ -11,9 +11,9 @@ defmodule Arc.Definition.Versioning do conversion = definition.transform(version, {file, scope}) case conversion do - :skip -> nil + :skip -> nil {_, _, ext} -> "#{name}.#{ext}" - _ -> "#{name}#{Path.extname(file.file_name)}" + _ -> "#{name}#{Path.extname(file.file_name)}" end end diff --git a/lib/arc/file.ex b/lib/arc/file.ex index ff6a48b..de63374 100644 --- a/lib/arc/file.ex +++ b/lib/arc/file.ex @@ -9,7 +9,7 @@ defmodule Arc.File do |> Base.encode32() |> Kernel.<>(extension) - Path.join(System.tmp_dir, file_name) + Path.join(System.tmp_dir(), file_name) end # Given a remote file @@ -21,7 +21,7 @@ defmodule Arc.File do {:ok, local_path} -> %Arc.File{path: local_path, file_name: filename} :error -> {:error, :invalid_file_path} end -end + end # Accepts a path def new(path) when is_binary(path) do @@ -89,7 +89,7 @@ end timeout: Application.get_env(:arc, :timeout, 10_000), max_retries: Application.get_env(:arc, :max_retries, 3), backoff_factor: Application.get_env(:arc, :backoff_factor, 1000), - backoff_max: Application.get_env(:arc, :backoff_max, 30_000), + backoff_max: Application.get_env(:arc, :backoff_max, 30_000) ] request(remote_path, options) @@ -97,14 +97,17 @@ end defp request(remote_path, options, tries \\ 0) do case :hackney.get(URI.to_string(remote_path), [], "", options) do - {:ok, 200, _headers, client_ref} -> :hackney.body(client_ref) + {:ok, 200, _headers, client_ref} -> + :hackney.body(client_ref) + {:error, %{reason: :timeout}} -> case retry(tries, options) do {:ok, :retry} -> request(remote_path, options, tries + 1) {:error, :out_of_tries} -> {:error, :timeout} end - _ -> {:error, :arc_httpoison_error} + _ -> + {:error, :arc_httpoison_error} end end @@ -116,7 +119,8 @@ end :timer.sleep(backoff) {:ok, :retry} - true -> {:error, :out_of_tries} + true -> + {:error, :out_of_tries} end end end diff --git a/lib/arc/processor.ex b/lib/arc/processor.ex index 039c467..affc860 100644 --- a/lib/arc/processor.ex +++ b/lib/arc/processor.ex @@ -6,7 +6,9 @@ defmodule Arc.Processor do defp apply_transformation(_, :skip), do: {:ok, nil} defp apply_transformation(file, :noaction), do: {:ok, file} - defp apply_transformation(file, {:noaction}), do: {:ok, file} # Deprecated + # Deprecated + defp apply_transformation(file, {:noaction}), do: {:ok, file} + defp apply_transformation(file, {cmd, conversion, _}) do apply_transformation(file, {cmd, conversion}) end diff --git a/lib/arc/storage/local.ex b/lib/arc/storage/local.ex index 4d380fd..e036b70 100644 --- a/lib/arc/storage/local.ex +++ b/lib/arc/storage/local.ex @@ -16,11 +16,12 @@ defmodule Arc.Storage.Local do def url(definition, version, file_and_scope, _options \\ []) do local_path = build_local_path(definition, version, file_and_scope) - url = if String.starts_with?(local_path, "/") do - local_path - else - "/" <> local_path - end + url = + if String.starts_with?(local_path, "/") do + local_path + else + "/" <> local_path + end url |> URI.encode() end diff --git a/lib/arc/storage/s3.ex b/lib/arc/storage/s3.ex index cd15eba..f712308 100644 --- a/lib/arc/storage/s3.ex +++ b/lib/arc/storage/s3.ex @@ -1,12 +1,11 @@ defmodule Arc.Storage.S3 do require Logger - @default_expiry_time 60*5 + @default_expiry_time 60 * 5 def put(definition, version, {file, scope}) do destination_dir = definition.storage_dir(version, {file, scope}) s3_bucket = s3_bucket(definition) s3_key = Path.join(destination_dir, file.file_name) - asset_host = asset_host(definition) acl = definition.acl(version, {file, scope}) s3_options = @@ -20,7 +19,7 @@ defmodule Arc.Storage.S3 do def url(definition, version, file_and_scope, options \\ []) do case Keyword.get(options, :signed, false) do false -> build_url(definition, version, file_and_scope, options) - true -> build_signed_url(definition, version, file_and_scope, options) + true -> build_signed_url(definition, version, file_and_scope, options) end end @@ -40,11 +39,12 @@ defmodule Arc.Storage.S3 do defp ensure_keyword_list(map) when is_map(map), do: Map.to_list(map) # If the file is stored as a binary in-memory, send to AWS in a single request - defp do_put(file=%Arc.File{binary: file_binary}, {s3_bucket, s3_key, s3_options}) when is_binary(file_binary) do + defp do_put(file = %Arc.File{binary: file_binary}, {s3_bucket, s3_key, s3_options}) + when is_binary(file_binary) do ExAws.S3.put_object(s3_bucket, s3_key, file_binary, s3_options) |> ExAws.request() |> case do - {:ok, _res} -> {:ok, file.file_name} + {:ok, _res} -> {:ok, file.file_name} {:error, error} -> {:error, error} end end @@ -62,23 +62,23 @@ defmodule Arc.Storage.S3 do end rescue e in ExAws.Error -> - Logger.error(inspect e) + Logger.error(inspect(e)) Logger.error(e.message) {:error, :invalid_bucket} end defp build_url(definition, version, file_and_scope, _options) do - url = Path.join host(definition), s3_key(definition, version, file_and_scope) + url = Path.join(host(definition), s3_key(definition, version, file_and_scope)) url |> URI.encode() end defp build_signed_url(definition, version, file_and_scope, options) do # Previous arc argument was expire_in instead of expires_in # check for expires_in, if not present, use expire_at. - options = put_in options[:expires_in], Keyword.get(options, :expires_in, options[:expire_in]) + options = put_in(options[:expires_in], Keyword.get(options, :expires_in, options[:expire_in])) # fallback to default, if neither is present. - options = put_in options[:expires_in], options[:expires_in] || @default_expiry_time - options = put_in options[:virtual_host], virtual_host() + options = put_in(options[:expires_in], options[:expires_in] || @default_expiry_time) + options = put_in(options[:virtual_host], virtual_host()) config = ExAws.Config.new(:s3, Application.get_all_env(:ex_aws)) s3_key = s3_key(definition, version, file_and_scope) s3_bucket = s3_bucket(definition) @@ -111,7 +111,7 @@ defmodule Arc.Storage.S3 do defp default_host(definition) do case virtual_host() do true -> "https://#{s3_bucket(definition)}.s3.amazonaws.com" - _ -> "https://s3.amazonaws.com/#{s3_bucket(definition)}" + _ -> "https://s3.amazonaws.com/#{s3_bucket(definition)}" end end diff --git a/lib/arc/transformations/convert.ex b/lib/arc/transformations/convert.ex index a9b60d5..72a119c 100644 --- a/lib/arc/transformations/convert.ex +++ b/lib/arc/transformations/convert.ex @@ -1,14 +1,20 @@ defmodule Arc.Transformations.Convert do def apply(cmd, file, args) do new_path = Arc.File.generate_temporary_path(file) - args = if is_function(args), do: args.(file.path, new_path), else: [file.path | (String.split(args, " ") ++ [new_path])] - program = to_string(cmd) + + args = + if is_function(args), + do: args.(file.path, new_path), + else: [file.path | String.split(args, " ") ++ [new_path]] + + program = to_string(cmd) ensure_executable_exists!(program) case System.cmd(program, args_list(args), stderr_to_stdout: true) do {_, 0} -> {:ok, %Arc.File{file | path: new_path}} + {error_message, _exit_code} -> {:error, error_message} end diff --git a/lib/mix/tasks/g.ex b/lib/mix/tasks/g.ex index 1992035..86c08b7 100644 --- a/lib/mix/tasks/g.ex +++ b/lib/mix/tasks/g.ex @@ -26,8 +26,9 @@ defmodule Mix.Tasks.Arc do """ def run([model_name]) do - app_name = Mix.Project.config[:app] - if (File.exists?("lib/#{app_name}_web/")) do + app_name = Mix.Project.config()[:app] + + if File.exists?("lib/#{app_name}_web/") do project_module_name = camelize(to_string(app_name)) generate_phx_uploader_file(model_name, project_module_name) else @@ -36,13 +37,13 @@ defmodule Mix.Tasks.Arc do end def run([model_name, path]) do - app_name = Mix.Project.config[:app] + app_name = Mix.Project.config()[:app] project_module_name = camelize(to_string(app_name)) generate_uploader_file(model_name, project_module_name, path) end def run(_) do - IO.puts "Incorrect syntax. Please try mix arc.g " + IO.puts("Incorrect syntax. Please try mix arc.g ") end defp generate_uploader_file(model_name, project_module_name, path) do @@ -51,19 +52,25 @@ defmodule Mix.Tasks.Arc do end defp generate_phx_uploader_file(model_name, project_module_name) do - app_name = Mix.Project.config[:app] - model_destination = Path.join(System.cwd(), "/lib/#{app_name}_web/uploaders/#{underscore(model_name)}.ex") + app_name = Mix.Project.config()[:app] + + model_destination = + Path.join(System.cwd(), "/lib/#{app_name}_web/uploaders/#{underscore(model_name)}.ex") + create_uploader(model_name, project_module_name, model_destination) end defp create_uploader(model_name, project_module_name, model_destination) do - create_file model_destination, uploader_template( + create_file( + model_destination, + uploader_template( model_name: model_name, uploader_model_name: Module.concat(project_module_name, camelize(model_name)) + ) ) end - embed_template :uploader, """ + embed_template(:uploader, """ defmodule <%= inspect @uploader_model_name %> do use Arc.Definition @@ -114,7 +121,6 @@ defmodule Mix.Tasks.Arc do # [content_type: MIME.from_path(file.file_name)] # end end - """ - + """) end end diff --git a/mix.exs b/mix.exs index f2bdcba..870f772 100644 --- a/mix.exs +++ b/mix.exs @@ -4,14 +4,16 @@ defmodule Arc.Mixfile do @version "0.10.0" def project do - [app: :arc, - version: @version, - elixir: "~> 1.4", - deps: deps(), - - # Hex - description: description(), - package: package()] + [ + app: :arc, + version: @version, + elixir: "~> 1.4", + deps: deps(), + + # Hex + description: description(), + package: package() + ] end defp description do @@ -21,18 +23,21 @@ defmodule Arc.Mixfile do end defp package do - [maintainers: ["Sean Stavropoulos"], - licenses: ["Apache 2.0"], - links: %{"GitHub" => "https://github.com/stavro/arc"}, - files: ~w(mix.exs README.md CHANGELOG.md lib)] + [ + maintainers: ["Sean Stavropoulos"], + licenses: ["Apache 2.0"], + links: %{"GitHub" => "https://github.com/stavro/arc"}, + files: ~w(mix.exs README.md CHANGELOG.md lib) + ] end def application do [ - applications: [ - :logger, - :hackney, - ] ++ applications(Mix.env) + applications: + [ + :logger, + :hackney + ] ++ applications(Mix.env()) ] end diff --git a/test/actions/store_test.exs b/test/actions/store_test.exs index 858795f..c345d01 100644 --- a/test/actions/store_test.exs +++ b/test/actions/store_test.exs @@ -7,7 +7,9 @@ defmodule ArcTest.Actions.Store do use Arc.Actions.Store use Arc.Definition.Storage - def validate({file, _}), do: String.ends_with?(file.file_name, ".png") || String.ends_with?(file.file_name, ".ico") + def validate({file, _}), + do: String.ends_with?(file.file_name, ".png") || String.ends_with?(file.file_name, ".ico") + def transform(:skipped, _), do: :skip def transform(_, _), do: :noaction def __versions, do: [:original, :thumb, :skipped] @@ -22,49 +24,73 @@ defmodule ArcTest.Actions.Store do end test "single binary argument is interpreted as file path" do - with_mock Arc.Storage.S3, [put: fn(DummyDefinition, _, {%{file_name: "image.png", path: @img}, nil}) -> {:ok, "resp"} end] do + with_mock Arc.Storage.S3, + put: fn DummyDefinition, _, {%{file_name: "image.png", path: @img}, nil} -> + {:ok, "resp"} + end do assert DummyDefinition.store(@img) == {:ok, "image.png"} end end test "two-tuple argument interpreted as path and scope" do - with_mock Arc.Storage.S3, [put: fn(DummyDefinition, _, {%{file_name: "image.png", path: @img}, :scope}) -> {:ok, "resp"} end] do + with_mock Arc.Storage.S3, + put: fn DummyDefinition, _, {%{file_name: "image.png", path: @img}, :scope} -> + {:ok, "resp"} + end do assert DummyDefinition.store({@img, :scope}) == {:ok, "image.png"} end end test "map with a filename and path" do - with_mock Arc.Storage.S3, [put: fn(DummyDefinition, _, {%{file_name: "image.png", path: @img}, nil}) -> {:ok, "resp"} end] do + with_mock Arc.Storage.S3, + put: fn DummyDefinition, _, {%{file_name: "image.png", path: @img}, nil} -> + {:ok, "resp"} + end do assert DummyDefinition.store(%{filename: "image.png", path: @img}) == {:ok, "image.png"} end end test "two-tuple with Plug.Upload and a scope" do - with_mock Arc.Storage.S3, [put: fn(DummyDefinition, _, {%{file_name: "image.png", path: @img}, :scope}) -> {:ok, "resp"} end] do - assert DummyDefinition.store({%{filename: "image.png", path: @img}, :scope}) == {:ok, "image.png"} + with_mock Arc.Storage.S3, + put: fn DummyDefinition, _, {%{file_name: "image.png", path: @img}, :scope} -> + {:ok, "resp"} + end do + assert DummyDefinition.store({%{filename: "image.png", path: @img}, :scope}) == + {:ok, "image.png"} end end test "error from ExAws on upload to S3" do - with_mock Arc.Storage.S3, [put: fn(DummyDefinition, _, {%{file_name: "image.png", path: @img}, :scope}) -> {:error, {:http_error, 404, "XML"}} end] do - assert DummyDefinition.store({%{filename: "image.png", path: @img}, :scope}) == {:error, [{:http_error, 404, "XML"}, {:http_error, 404, "XML"}]} + with_mock Arc.Storage.S3, + put: fn DummyDefinition, _, {%{file_name: "image.png", path: @img}, :scope} -> + {:error, {:http_error, 404, "XML"}} + end do + assert DummyDefinition.store({%{filename: "image.png", path: @img}, :scope}) == + {:error, [{:http_error, 404, "XML"}, {:http_error, 404, "XML"}]} end end test "timeout" do - Application.put_env :arc, :version_timeout, 1 + Application.put_env(:arc, :version_timeout, 1) catch_exit do - with_mock Arc.Storage.S3, [put: fn(DummyDefinition, _, {%{file_name: "image.png", path: @img}, :scope}) -> :timer.sleep(100) && {:ok, "favicon.ico"} end] do - assert DummyDefinition.store({%{filename: "image.png", path: @img}, :scope}) == {:ok, "image.png"} + with_mock Arc.Storage.S3, + put: fn DummyDefinition, _, {%{file_name: "image.png", path: @img}, :scope} -> + :timer.sleep(100) && {:ok, "favicon.ico"} + end do + assert DummyDefinition.store({%{filename: "image.png", path: @img}, :scope}) == + {:ok, "image.png"} end end - Application.put_env :arc, :version_timeout, 15_000 + Application.put_env(:arc, :version_timeout, 15_000) end test "accepts remote files" do - with_mock Arc.Storage.S3, [put: fn(DummyDefinition, _, {%{file_name: "favicon.ico", path: _}, nil}) -> {:ok, "favicon.ico"} end] do + with_mock Arc.Storage.S3, + put: fn DummyDefinition, _, {%{file_name: "favicon.ico", path: _}, nil} -> + {:ok, "favicon.ico"} + end do assert DummyDefinition.store("https://www.google.com/favicon.ico") == {:ok, "favicon.ico"} end end diff --git a/test/actions/url_test.exs b/test/actions/url_test.exs index ba23fbc..fb679d0 100644 --- a/test/actions/url_test.exs +++ b/test/actions/url_test.exs @@ -25,30 +25,39 @@ defmodule ArcTest.Actions.Url do end test_with_mock "delegates url generation to the storage engine", Arc.Storage.S3, - [url: fn(DummyDefinition, :original, {%{file_name: "file.png"}, nil}, []) -> :ok end] do + url: fn DummyDefinition, :original, {%{file_name: "file.png"}, nil}, [] -> :ok end do assert DummyDefinition.url("file.png") == :ok end test_with_mock "optional atom as a second argument specifies the version", Arc.Storage.S3, - [url: fn(DummyDefinition, :thumb, {%{file_name: "file.png"}, nil}, []) -> :ok end] do + url: fn DummyDefinition, :thumb, {%{file_name: "file.png"}, nil}, [] -> :ok end do assert DummyDefinition.url("file.png", :thumb) == :ok end test_with_mock "optional list as a second argument specifies the options", Arc.Storage.S3, - [url: fn(DummyDefinition, :original, {%{file_name: "file.png"}, nil}, [signed: true, expires_in: 10]) -> :ok end] do + url: fn DummyDefinition, + :original, + {%{file_name: "file.png"}, nil}, + [signed: true, expires_in: 10] -> + :ok + end do assert DummyDefinition.url("file.png", signed: true, expires_in: 10) == :ok end test_with_mock "optional tuple for file including scope", Arc.Storage.S3, - [url: fn(DummyDefinition, :original, {%{file_name: "file.png"}, :scope}, []) -> :ok end] do + url: fn DummyDefinition, :original, {%{file_name: "file.png"}, :scope}, [] -> :ok end do assert DummyDefinition.url({"file.png", :scope}) == :ok end test_with_mock "optional tuple for file including scope 2", Arc.Storage.S3, - [url: fn - (DummyDefinition, :original, {%{file_name: "file.png"}, :scope}, [signed: true]) -> :ok - (DummyDefinition, :thumb, {%{file_name: "file.png"}, :scope}, [signed: true]) -> :ok - end] do - assert DummyDefinition.urls({"file.png", :scope}, signed: true) == %{original: :ok, thumb: :ok, skipped: nil} + url: fn + DummyDefinition, :original, {%{file_name: "file.png"}, :scope}, [signed: true] -> :ok + DummyDefinition, :thumb, {%{file_name: "file.png"}, :scope}, [signed: true] -> :ok + end do + assert DummyDefinition.urls({"file.png", :scope}, signed: true) == %{ + original: :ok, + thumb: :ok, + skipped: nil + } end end diff --git a/test/processor_test.exs b/test/processor_test.exs index 7a88a50..243d343 100644 --- a/test/processor_test.exs +++ b/test/processor_test.exs @@ -10,8 +10,14 @@ defmodule ArcTest.Processor do def validate({file, _}), do: String.ends_with?(file.file_name, ".png") def transform(:original, _), do: :noaction def transform(:thumb, _), do: {:convert, "-strip -thumbnail 10x10"} - def transform(:med, _), do: {:convert, fn(input, output) -> " #{input} -strip -thumbnail 10x10 #{output}" end, :jpg} - def transform(:small, _), do: {:convert, fn(input, output) -> [input, "-strip", "-thumbnail", "10x10", output] end, :jpg} + + def transform(:med, _), + do: {:convert, fn input, output -> " #{input} -strip -thumbnail 10x10 #{output}" end, :jpg} + + def transform(:small, _), + do: + {:convert, fn input, output -> [input, "-strip", "-thumbnail", "10x10", output] end, :jpg} + def transform(:skipped, _), do: :skip def __versions, do: [:original, :thumb] end @@ -38,13 +44,15 @@ defmodule ArcTest.Processor do end test "returns nil for :skip transformations" do - assert {:ok, nil} = Arc.Processor.process(DummyDefinition, :skipped, {Arc.File.new(@img), nil}) + assert {:ok, nil} = + Arc.Processor.process(DummyDefinition, :skipped, {Arc.File.new(@img), nil}) end test "transforms a copied version of file according to the specified transformation" do {:ok, new_file} = Arc.Processor.process(DummyDefinition, :thumb, {Arc.File.new(@img), nil}) assert new_file.path != @img - assert "128x128" == geometry(@img) #original file untouched + # original file untouched + assert "128x128" == geometry(@img) assert "10x10" == geometry(new_file.path) cleanup(new_file.path) end @@ -52,7 +60,8 @@ defmodule ArcTest.Processor do test "transforms a copied version of file according to a function transformation that returns a string" do {:ok, new_file} = Arc.Processor.process(DummyDefinition, :med, {Arc.File.new(@img), nil}) assert new_file.path != @img - assert "128x128" == geometry(@img) #original file untouched + # original file untouched + assert "128x128" == geometry(@img) assert "10x10" == geometry(new_file.path) cleanup(new_file.path) end @@ -60,16 +69,25 @@ defmodule ArcTest.Processor do test "transforms a copied version of file according to a function transformation that returns a list" do {:ok, new_file} = Arc.Processor.process(DummyDefinition, :small, {Arc.File.new(@img), nil}) assert new_file.path != @img - assert "128x128" == geometry(@img) #original file untouched + # original file untouched + assert "128x128" == geometry(@img) assert "10x10" == geometry(new_file.path) cleanup(new_file.path) end test "transforms a file given as a binary" do img_binary = File.read!(@img) - {:ok, new_file} = Arc.Processor.process(DummyDefinition, :small, {Arc.File.new(%{binary: img_binary, filename: "image.png"}), nil}) + + {:ok, new_file} = + Arc.Processor.process( + DummyDefinition, + :small, + {Arc.File.new(%{binary: img_binary, filename: "image.png"}), nil} + ) + assert new_file.path != @img - assert "128x128" == geometry(@img) #original file untouched + # original file untouched + assert "128x128" == geometry(@img) assert "10x10" == geometry(new_file.path) cleanup(new_file.path) end @@ -77,13 +95,15 @@ defmodule ArcTest.Processor do test "file names with spaces" do {:ok, new_file} = Arc.Processor.process(DummyDefinition, :thumb, {Arc.File.new(@img2), nil}) assert new_file.path != @img2 - assert "128x128" == geometry(@img2) #original file untouched + # original file untouched + assert "128x128" == geometry(@img2) assert "10x10" == geometry(new_file.path) cleanup(new_file.path) end test "returns tuple in an invalid transformation" do - assert {:error, _} = Arc.Processor.process(BrokenDefinition, :thumb, {Arc.File.new(@img), nil}) + assert {:error, _} = + Arc.Processor.process(BrokenDefinition, :thumb, {Arc.File.new(@img), nil}) end test "raises an error if the given transformation executable cannot be found" do diff --git a/test/storage/local_test.exs b/test/storage/local_test.exs index ad8dea7..fa4ca44 100644 --- a/test/storage/local_test.exs +++ b/test/storage/local_test.exs @@ -6,12 +6,11 @@ defmodule ArcTest.Storage.Local do setup_all do File.mkdir_p("arctest/uploads") - on_exit fn -> + on_exit(fn -> File.rm_rf("arctest/uploads") - end + end) end - defmodule DummyDefinition do use Arc.Actions.Store use Arc.Definition.Storage @@ -24,14 +23,31 @@ defmodule ArcTest.Storage.Local do def __versions, do: [:original, :thumb, :skipped] def storage_dir(_, _), do: "arctest/uploads" def __storage, do: Arc.Storage.Local - def filename(:original, {file, _}), do: "original-#{Path.basename(file.file_name, Path.extname(file.file_name))}" - def filename(:thumb, {file, _}), do: "1/thumb-#{Path.basename(file.file_name, Path.extname(file.file_name))}" - def filename(:skipped, {file, _}), do: "1/skipped-#{Path.basename(file.file_name, Path.extname(file.file_name))}" + + def filename(:original, {file, _}), + do: "original-#{Path.basename(file.file_name, Path.extname(file.file_name))}" + + def filename(:thumb, {file, _}), + do: "1/thumb-#{Path.basename(file.file_name, Path.extname(file.file_name))}" + + def filename(:skipped, {file, _}), + do: "1/skipped-#{Path.basename(file.file_name, Path.extname(file.file_name))}" end test "put, delete, get" do - assert {:ok, "original-image.png"} == Arc.Storage.Local.put(DummyDefinition, :original, {Arc.File.new(%{filename: "original-image.png", path: @img}), nil}) - assert {:ok, "1/thumb-image.png"} == Arc.Storage.Local.put(DummyDefinition, :thumb, {Arc.File.new(%{filename: "1/thumb-image.png", path: @img}), nil}) + assert {:ok, "original-image.png"} == + Arc.Storage.Local.put( + DummyDefinition, + :original, + {Arc.File.new(%{filename: "original-image.png", path: @img}), nil} + ) + + assert {:ok, "1/thumb-image.png"} == + Arc.Storage.Local.put( + DummyDefinition, + :thumb, + {Arc.File.new(%{filename: "1/thumb-image.png", path: @img}), nil} + ) assert File.exists?("arctest/uploads/original-image.png") assert File.exists?("arctest/uploads/1/thumb-image.png") @@ -45,23 +61,38 @@ defmodule ArcTest.Storage.Local do end test "save binary" do - Arc.Storage.Local.put(DummyDefinition, :original, {Arc.File.new(%{binary: "binary", filename: "binary.png"}), nil}) + Arc.Storage.Local.put( + DummyDefinition, + :original, + {Arc.File.new(%{binary: "binary", filename: "binary.png"}), nil} + ) + assert true == File.exists?("arctest/uploads/binary.png") end test "encoded url" do - url = DummyDefinition.url(Arc.File.new(%{binary: "binary", filename: "binary file.png"}), :original) + url = + DummyDefinition.url( + Arc.File.new(%{binary: "binary", filename: "binary file.png"}), + :original + ) + assert "/arctest/uploads/original-binary%20file.png" == url end test "url for skipped version" do - url = DummyDefinition.url(Arc.File.new(%{binary: "binary", filename: "binary file.png"}), :skipped) + url = + DummyDefinition.url( + Arc.File.new(%{binary: "binary", filename: "binary file.png"}), + :skipped + ) + assert url == nil end test "if one transform fails, they all fail" do filepath = @badimg - [filename] = String.split(@img, "/") |> Enum.reverse |> Enum.take(1) + [filename] = String.split(@img, "/") |> Enum.reverse() |> Enum.take(1) assert File.exists?(filepath) DummyDefinition.store(filepath) diff --git a/test/storage/s3_test.exs b/test/storage/s3_test.exs index 76b16b7..c39ec1a 100644 --- a/test/storage/s3_test.exs +++ b/test/storage/s3_test.exs @@ -12,7 +12,9 @@ defmodule ArcTest.Storage.S3 do def acl(_, {_, :private}), do: :private def s3_object_headers(:original, {_, :with_content_type}), do: [content_type: "image/gif"] - def s3_object_headers(:original, {_, :with_content_disposition}), do: %{content_disposition: "attachment; filename=abc.png"} + + def s3_object_headers(:original, {_, :with_content_disposition}), + do: %{content_disposition: "attachment; filename=abc.png"} end defmodule DefinitionWithThumbnail do @@ -73,10 +75,11 @@ defmodule ArcTest.Storage.S3 do char_header = to_charlist(header) - assert to_charlist(value) == Enum.find_value(headers, fn( - {^char_header, value}) -> value - _ -> nil - end) + assert to_charlist(value) == + Enum.find_value(headers, fn + {^char_header, value} -> value + _ -> nil + end) end end @@ -104,7 +107,12 @@ defmodule ArcTest.Storage.S3 do end defmacro assert_public_with_extension(definition, args, version, extension) do - quote bind_quoted: [definition: definition, version: version, args: args, extension: extension] do + quote bind_quoted: [ + definition: definition, + version: version, + args: args, + extension: extension + ] do url = definition.url(args, version) {:ok, {{_, code, msg}, headers, _}} = :httpc.request(to_charlist(url)) assert code == 200 @@ -116,11 +124,12 @@ defmodule ArcTest.Storage.S3 do setup_all do Application.ensure_all_started(:hackney) Application.ensure_all_started(:ex_aws) - Application.put_env :arc, :virtual_host, false - Application.put_env :arc, :bucket, { :system, "ARC_TEST_BUCKET" } + Application.put_env(:arc, :virtual_host, false) + Application.put_env(:arc, :bucket, {:system, "ARC_TEST_BUCKET"}) + # Application.put_env :ex_aws, :s3, [scheme: "https://", host: "s3.amazonaws.com", region: "us-west-2"] - Application.put_env :ex_aws, :access_key_id, System.get_env("ARC_TEST_S3_KEY") - Application.put_env :ex_aws, :secret_access_key, System.get_env("ARC_TEST_S3_SECRET") + Application.put_env(:ex_aws, :access_key_id, System.get_env("ARC_TEST_S3_KEY")) + Application.put_env(:ex_aws, :secret_access_key, System.get_env("ARC_TEST_S3_SECRET")) # Application.put_env :ex_aws, :region, "us-east-1" # Application.put_env :ex_aws, :scheme, "https://" end @@ -140,13 +149,15 @@ defmodule ArcTest.Storage.S3 do @tag :s3 @tag timeout: 15000 test "virtual_host" do - with_env :arc, :virtual_host, true, fn -> - assert "https://#{env_bucket()}.s3.amazonaws.com/arctest/uploads/image.png" == DummyDefinition.url(@img) - end - - with_env :arc, :virtual_host, false, fn -> - assert "https://s3.amazonaws.com/#{env_bucket()}/arctest/uploads/image.png" == DummyDefinition.url(@img) - end + with_env(:arc, :virtual_host, true, fn -> + assert "https://#{env_bucket()}.s3.amazonaws.com/arctest/uploads/image.png" == + DummyDefinition.url(@img) + end) + + with_env(:arc, :virtual_host, false, fn -> + assert "https://s3.amazonaws.com/#{env_bucket()}/arctest/uploads/image.png" == + DummyDefinition.url(@img) + end) end @tag :s3 @@ -154,22 +165,23 @@ defmodule ArcTest.Storage.S3 do test "custom asset_host" do custom_asset_host = "https://some.cloudfront.com" - with_env :arc, :asset_host, custom_asset_host, fn -> + with_env(:arc, :asset_host, custom_asset_host, fn -> assert "#{custom_asset_host}/arctest/uploads/image.png" == DummyDefinition.url(@img) - end + end) - with_env :arc, :asset_host, {:system, "ARC_ASSET_HOST"}, fn -> + with_env(:arc, :asset_host, {:system, "ARC_ASSET_HOST"}, fn -> System.put_env("ARC_ASSET_HOST", custom_asset_host) assert "#{custom_asset_host}/arctest/uploads/image.png" == DummyDefinition.url(@img) - end + end) - with_env :arc, :asset_host, false, fn -> - assert "https://s3.amazonaws.com/#{env_bucket()}/arctest/uploads/image.png" == DummyDefinition.url(@img) - end + with_env(:arc, :asset_host, false, fn -> + assert "https://s3.amazonaws.com/#{env_bucket()}/arctest/uploads/image.png" == + DummyDefinition.url(@img) + end) end @tag :s3 - @tag timeout: 150000 + @tag timeout: 150_000 test "custom asset_host in definition" do custom_asset_host = "https://example.com" @@ -194,7 +206,7 @@ defmodule ArcTest.Storage.S3 do @tag :s3 @tag timeout: 15000 test "private put and signed get" do - #put the image as private + # put the image as private assert {:ok, "image.png"} == DummyDefinition.store({@img, :private}) assert_private(DummyDefinition, "image.png") delete_and_assert_not_found(DummyDefinition, "image.png") @@ -212,22 +224,32 @@ defmodule ArcTest.Storage.S3 do @tag timeout: 15000 test "content_disposition" do {:ok, "image.png"} = DummyDefinition.store({@img, :with_content_disposition}) - assert_header(DummyDefinition, "image.png", "content-disposition", "attachment; filename=abc.png") + + assert_header( + DummyDefinition, + "image.png", + "content-disposition", + "attachment; filename=abc.png" + ) + delete_and_assert_not_found(DummyDefinition, "image.png") end @tag :s3 - @tag timeout: 150000 + @tag timeout: 150_000 test "delete with scope" do scope = %{id: 1} {:ok, path} = DefinitionWithScope.store({"test/support/image.png", scope}) - assert "https://s3.amazonaws.com/#{env_bucket()}/uploads/with_scopes/1/image.png" == DefinitionWithScope.url({path, scope}) + + assert "https://s3.amazonaws.com/#{env_bucket()}/uploads/with_scopes/1/image.png" == + DefinitionWithScope.url({path, scope}) + assert_public(DefinitionWithScope, {path, scope}) delete_and_assert_not_found(DefinitionWithScope, {path, scope}) end @tag :s3 - @tag timeout: 150000 + @tag timeout: 150_000 test "with bucket" do url = "https://s3.amazonaws.com/#{env_bucket()}/uploads/image.png" assert url == DefinitionWithBucket.url("test/support/image.png") @@ -236,16 +258,16 @@ defmodule ArcTest.Storage.S3 do end @tag :s3 - @tag timeout: 150000 + @tag timeout: 150_000 test "put with error" do Application.put_env(:arc, :bucket, "unknown-bucket") {:error, res} = DummyDefinition.store("test/support/image.png") - Application.put_env :arc, :bucket, env_bucket() + Application.put_env(:arc, :bucket, env_bucket()) assert res end @tag :s3 - @tag timeout: 150000 + @tag timeout: 150_000 test "put with converted version" do assert {:ok, "image.png"} == DefinitionWithThumbnail.store(@img) assert_public_with_extension(DefinitionWithThumbnail, "image.png", :thumb, ".jpg") @@ -253,7 +275,7 @@ defmodule ArcTest.Storage.S3 do end @tag :s3 - @tag timeout: 150000 + @tag timeout: 150_000 test "url for a skipped version" do assert nil == DefinitionWithSkipped.url("image.png") end diff --git a/test/test_helper.exs b/test/test_helper.exs index 35c4a3e..db5c8bd 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,2 +1,2 @@ -ExUnit.configure exclude: [:s3] +ExUnit.configure(exclude: [:s3]) ExUnit.start()