# `Firkin.Backend`
[🔗](https://harton.dev/james/firkin)

Behaviour for S3 storage backends.

Implement this behaviour to provide actual storage operations behind the
S3-compatible HTTP interface. All callbacks receive an auth context map
containing the authenticated identity from `lookup_credential/1`.

Multipart upload callbacks are optional — the server returns 501 Not
Implemented for multipart operations if they are not defined.

# `auth_context`

```elixir
@type auth_context() :: %{access_key_id: String.t(), identity: term()}
```

# `bucket`

```elixir
@type bucket() :: String.t()
```

# `key`

```elixir
@type key() :: String.t()
```

# `s3_error`

```elixir
@type s3_error() :: Firkin.Error.t()
```

# `abort_multipart_upload`
*optional* 

```elixir
@callback abort_multipart_upload(auth_context(), bucket(), key(), upload_id :: String.t()) ::
  :ok | {:error, s3_error()}
```

# `complete_multipart_upload`
*optional* 

```elixir
@callback complete_multipart_upload(
  auth_context(),
  bucket(),
  key(),
  upload_id :: String.t(),
  parts :: [{pos_integer(), String.t()}]
) :: {:ok, Firkin.CompleteResult.t()} | {:error, s3_error()}
```

# `copy_object`

```elixir
@callback copy_object(
  auth_context(),
  bucket(),
  key(),
  source_bucket :: bucket(),
  source_key :: key()
) :: {:ok, Firkin.CopyResult.t()} | {:error, s3_error()}
```

# `create_bucket`

```elixir
@callback create_bucket(auth_context(), bucket()) :: :ok | {:error, s3_error()}
```

# `create_multipart_upload`
*optional* 

```elixir
@callback create_multipart_upload(auth_context(), bucket(), key(), opts :: map()) ::
  {:ok, upload_id :: String.t()} | {:error, s3_error()}
```

# `delete_bucket`

```elixir
@callback delete_bucket(auth_context(), bucket()) :: :ok | {:error, s3_error()}
```

# `delete_object`

```elixir
@callback delete_object(auth_context(), bucket(), key()) :: :ok | {:error, s3_error()}
```

# `delete_objects`

```elixir
@callback delete_objects(auth_context(), bucket(), [key()]) ::
  {:ok, Firkin.DeleteResult.t()} | {:error, s3_error()}
```

# `get_bucket_location`

```elixir
@callback get_bucket_location(auth_context(), bucket()) ::
  {:ok, String.t()} | {:error, s3_error()}
```

# `get_object`

```elixir
@callback get_object(auth_context(), bucket(), key(), Firkin.GetOpts.t()) ::
  {:ok, Firkin.Object.t()} | {:error, s3_error()}
```

# `head_bucket`

```elixir
@callback head_bucket(auth_context(), bucket()) :: :ok | {:error, s3_error()}
```

# `head_object`

```elixir
@callback head_object(auth_context(), bucket(), key()) ::
  {:ok, Firkin.ObjectMeta.t()} | {:error, s3_error()}
```

# `list_buckets`

```elixir
@callback list_buckets(auth_context()) ::
  {:ok, [Firkin.Bucket.t()]} | {:error, s3_error()}
```

# `list_multipart_uploads`
*optional* 

```elixir
@callback list_multipart_uploads(auth_context(), bucket(), opts :: map()) ::
  {:ok, Firkin.MultipartList.t()} | {:error, s3_error()}
```

# `list_objects_v2`

```elixir
@callback list_objects_v2(auth_context(), bucket(), Firkin.ListOpts.t()) ::
  {:ok, Firkin.ListResult.t()} | {:error, s3_error()}
```

# `list_parts`
*optional* 

```elixir
@callback list_parts(
  auth_context(),
  bucket(),
  key(),
  upload_id :: String.t(),
  opts :: map()
) ::
  {:ok, Firkin.PartList.t()} | {:error, s3_error()}
```

# `lookup_credential`

```elixir
@callback lookup_credential(access_key_id :: String.t()) ::
  {:ok, Firkin.Credential.t()} | {:error, :not_found}
```

# `put_object`

```elixir
@callback put_object(
  auth_context(),
  bucket(),
  key(),
  body :: iodata(),
  Firkin.PutOpts.t()
) ::
  {:ok, etag :: String.t()} | {:error, s3_error()}
```

# `put_object_stream`
*optional* 

```elixir
@callback put_object_stream(
  auth_context(),
  bucket(),
  key(),
  body :: Enumerable.t(),
  Firkin.PutOpts.t()
) :: {:ok, etag :: String.t()} | {:error, s3_error()}
```

# `upload_part`
*optional* 

```elixir
@callback upload_part(
  auth_context(),
  bucket(),
  key(),
  upload_id :: String.t(),
  part_number :: pos_integer(),
  body :: iodata()
) :: {:ok, etag :: String.t()} | {:error, s3_error()}
```

# `upload_part_stream`
*optional* 

```elixir
@callback upload_part_stream(
  auth_context(),
  bucket(),
  key(),
  upload_id :: String.t(),
  part_number :: pos_integer(),
  body :: Enumerable.t()
) :: {:ok, etag :: String.t()} | {:error, s3_error()}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
