SiteMaps.Adapter behaviour (site_maps v0.1.0)

Copy Markdown View Source

Storage behaviour used to publish, read, list and delete sitemap files.

An adapter is configured as a {module, opts} tuple. Keys are always relative to the adapter root (a directory, a bucket prefix, ...), use / as separator and never start with /.

Built-in adapters:

read/2 returns the raw stored bytes, so a .gz key comes back gzipped. A missing key is always reported as {:error, :not_found} and deleting a missing key is :ok. last_modified is kept with second precision by every adapter.

Every adapter rejects keys failing validate_key/1 with {:error, :invalid_key}.

Summary

Functions

Content type derived from the key extension.

Deletes key. Deleting a missing key is :ok.

Lists the keys starting with prefix, sorted.

Reads the raw bytes stored under key.

Checks that key is a relative path without empty, . or .. segments, segments named like a temporary file (.<name>.tmp), whitespace, control characters or the URL-sensitive characters ?, # and %.

Stores source under key.

Types

key()

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

opts()

@type opts() :: keyword()

read_meta()

@type read_meta() :: %{
  content_type: String.t() | nil,
  last_modified: DateTime.t() | nil
}

source()

@type source() :: {:file, Path.t()} | iodata()

t()

@type t() :: {module(), opts()}

write_meta()

@type write_meta() :: %{
  optional(:content_type) => String.t(),
  optional(:cache_control) => String.t() | nil,
  optional(:last_modified) => DateTime.t() | nil
}

Callbacks

delete(opts, key)

@callback delete(opts(), key()) :: :ok | {:error, term()}

list(opts, prefix)

@callback list(opts(), prefix :: String.t()) :: {:ok, [key()]} | {:error, term()}

read(opts, key)

@callback read(opts(), key()) ::
  {:ok, binary(), read_meta()} | {:error, :not_found | term()}

write(opts, key, source, write_meta)

@callback write(opts(), key(), source(), write_meta()) :: :ok | {:error, term()}

Functions

content_type(key)

@spec content_type(key()) :: String.t()

Content type derived from the key extension.

iex> SiteMaps.Adapter.content_type("posts/sitemap1.xml.gz")
"application/gzip"
iex> SiteMaps.Adapter.content_type("sitemap.xml")
"application/xml"

delete(arg, key)

@spec delete(t(), key()) :: :ok | {:error, term()}

Deletes key. Deleting a missing key is :ok.

list(arg, prefix)

@spec list(t(), String.t()) :: {:ok, [key()]} | {:error, term()}

Lists the keys starting with prefix, sorted.

read(arg, key)

@spec read(t(), key()) :: {:ok, binary(), read_meta()} | {:error, :not_found | term()}

Reads the raw bytes stored under key.

validate_key(key)

@spec validate_key(term()) :: :ok | {:error, :invalid_key}

Checks that key is a relative path without empty, . or .. segments, segments named like a temporary file (.<name>.tmp), whitespace, control characters or the URL-sensitive characters ?, # and %.

iex> SiteMaps.Adapter.validate_key("posts/sitemap1.xml")
:ok
iex> SiteMaps.Adapter.validate_key("/sitemap.xml")
{:error, :invalid_key}
iex> SiteMaps.Adapter.validate_key("sitemap.xml?v=1")
{:error, :invalid_key}

write(arg, key, source, meta)

@spec write(t(), key(), source(), write_meta()) :: :ok | {:error, term()}

Stores source under key.