Skip to content

Commit

Permalink
Accept headers to put_blob
Browse files Browse the repository at this point in the history
Co-authored-by: Aman Kumar Singh <[email protected]>
  • Loading branch information
HarisudhanRavi and amanksingh01 committed Nov 22, 2024
1 parent 894784c commit 61bfc29
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 17 deletions.
29 changes: 21 additions & 8 deletions lib/azurex/blob.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ defmodule Azurex.Blob do
argument of `nil` will result in the blob being assigned the default content
type `"application/octet-stream"`.
## The `options` Argument
The last argument of this function is a keyword list that can contain the following options:
* `params`: a list of additional query parameters to include in the request
* `headers`: a list of additional headers to include in the request
* any other options that can be passed to `HTTPoison.request/1`
## Examples
iex> put_blob("filename.txt", "file contents", "text/plain")
Expand All @@ -55,7 +62,10 @@ defmodule Azurex.Blob do
iex> put_blob("filename.txt", "file contents", "text/plain", "container")
:ok
iex> put_blob("filename.txt", "file contents", "text/plain", nil, timeout: 10)
iex> put_blob("filename.txt", "file contents", "text/plain", nil, params: [timeout: 10])
:ok
iex> put_blob("filename.txt", "file contents", "text/plain", nil, params: [timeout: 10], headers: [{"x-ms-meta-foo", "bar"}])
:ok
iex> put_blob("filename.txt", "file contents", "text/plain")
Expand All @@ -71,40 +81,43 @@ defmodule Azurex.Blob do
) ::
:ok
| {:error, HTTPoison.AsyncResponse.t() | HTTPoison.Error.t() | HTTPoison.Response.t()}
def put_blob(name, blob, content_type, container \\ nil, params \\ [])
def put_blob(name, blob, content_type, container \\ nil, options \\ [])

def put_blob(name, {:stream, bitstream}, content_type, container, params) do
def put_blob(name, {:stream, bitstream}, content_type, container, options) do
content_type = content_type || "application/octet-stream"

bitstream
|> Stream.transform(
fn -> [] end,
fn chunk, acc ->
with {:ok, block_id} <- Block.put_block(container, chunk, name, params) do
with {:ok, block_id} <- Block.put_block(container, chunk, name, options) do
{[], [block_id | acc]}
end
end,
fn acc ->
Block.put_block_list(acc, container, name, content_type, params)
Block.put_block_list(acc, container, name, content_type, options)
end
)
|> Stream.run()
end

def put_blob(name, blob, content_type, container, params) do
def put_blob(name, blob, content_type, container, options) do
content_type = content_type || "application/octet-stream"
{params, options} = Keyword.pop(options, :params, [])
{headers, options} = Keyword.pop(options, :headers, [])
headers = Enum.map(headers, fn {k, v} -> {to_string(k), v} end)

%HTTPoison.Request{
method: :put,
url: get_url(container, name),
params: params,
body: blob,
headers: [
{"x-ms-blob-type", "BlockBlob"}
{"x-ms-blob-type", "BlockBlob"} | headers
],
# Blob storage only answers when the whole file has been uploaded, so recv_timeout
# is not applicable for the put request, so we set it to infinity
options: [recv_timeout: :infinity]
options: [recv_timeout: :infinity] ++ options
}
|> SharedKey.sign(
storage_account_name: Config.storage_account_name(),
Expand Down
20 changes: 14 additions & 6 deletions lib/azurex/blob/block.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ defmodule Azurex.Blob.Block do
"""
@spec put_block(String.t(), bitstring(), String.t(), list()) ::
{:ok, String.t()} | {:error, term()}
def put_block(container, chunk, name, params) do
def put_block(container, chunk, name, options) do
block_id = build_block_id()
content_type = "application/octet-stream"
{params, options} = Keyword.pop(options, :params, [])
{headers, options} = Keyword.pop(options, :headers, [])
headers = Enum.map(headers, fn {k, v} -> {to_string(k), v} end)
params = [{:comp, "block"}, {:blockid, block_id} | params]

%HTTPoison.Request{
Expand All @@ -31,8 +34,9 @@ defmodule Azurex.Blob.Block do
body: chunk,
headers: [
{"content-type", content_type},
{"content-length", byte_size(chunk)}
]
{"content-length", byte_size(chunk)} | headers
],
options: options
}
|> SharedKey.sign(
storage_account_name: Config.storage_account_name(),
Expand All @@ -54,7 +58,10 @@ defmodule Azurex.Blob.Block do
"""
@spec put_block_list(list(), String.t(), String.t(), String.t() | nil, list()) ::
:ok | {:error, term()}
def put_block_list(block_ids, container, name, blob_content_type, params) do
def put_block_list(block_ids, container, name, blob_content_type, options) do
{params, options} = Keyword.pop(options, :params, [])
{headers, options} = Keyword.pop(options, :headers, [])
headers = Enum.map(headers, fn {k, v} -> {to_string(k), v} end)
params = [{:comp, "blocklist"} | params]
content_type = "text/plain; charset=UTF-8"
blob_content_type = blob_content_type || "application/octet-stream"
Expand All @@ -79,8 +86,9 @@ defmodule Azurex.Blob.Block do
body: body,
headers: [
{"content-type", content_type},
{"x-ms-blob-content-type", blob_content_type}
]
{"x-ms-blob-content-type", blob_content_type} | headers
],
options: options
}
|> SharedKey.sign(
storage_account_name: Config.storage_account_name(),
Expand Down
24 changes: 21 additions & 3 deletions test/integration/blob_integration_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,18 @@ defmodule Azurex.BlobIntegrationTests do
) == {:ok, @sample_file_contents}
end

test "passing container and params" do
test "passing container and options with params and headers" do
blob_name = make_blob_name()

assert Blob.put_blob(
blob_name,
@sample_file_contents,
"text/plain",
@integration_testing_container,
timeout: 10,
ignored_param: "ignored_param_value"
options: [
params: [timeout: 10, ignored_param: "ignored_param_value"],
headers: [ignored_header: "ignored_header_value"]
]
) == :ok

assert Blob.get_blob(
Expand Down Expand Up @@ -122,6 +124,22 @@ defmodule Azurex.BlobIntegrationTests do
assert headers["content-md5"] ==
:crypto.hash(:md5, @sample_file_contents) |> Base.encode64()
end

test "passing custom headers" do
blob_name = make_blob_name()

assert Blob.put_blob(
blob_name,
@sample_file_contents,
"text/plain",
nil,
headers: ["x-ms-meta-foo": "bar"]
) == :ok

assert {:ok, headers} = Blob.head_blob(blob_name)
headers = Map.new(headers)
assert headers["x-ms-meta-foo"] == "bar"
end
end

describe "copying a blob" do
Expand Down

0 comments on commit 61bfc29

Please sign in to comment.