ExFTP.StorageConnector behaviour (ExFTP v1.0.4)
View SourceA behaviour defining a Storage Connector.
Storage Connectors are used by the FTP interface to interact with a particular type of storage.
๐ See Also
๐ Resources
Summary
Types
State held onto by the server and modified by the StorageConnector
.
Information about a given file, directory, or symlink
A string representing a file path (e.g "/path/to/file.txt"
or "/path/to/dir/"
)
A Port representing a socket to communicate with an FTP client.
Callbacks
Create a function/1 that writes a stream to storage
Deletes a given directory
Deletes a given file
Whether a given path is an existing directory
Returns a stream to read the raw bytes of an object specified by a given path
Returns content_info/0
representing a single object in a given directory
Returns a list of content_info/0
representing each object in a given directory
Returns the current working directory
Creates a directory, given a path
Types
@type connector_state() :: %{}
State held onto by the server and modified by the StorageConnector
.
It may be used by the storage connector to keep stateful values.
โ ๏ธ Reminders
Special Keys
current_working_directory:
String.t/0
should always exist. It represents the "directory" we're operating from
๐ Resources
@type content_info() :: %{ file_name: String.t(), modified_datetime: DateTime.t(), size: integer(), access: :read | :write | :read_write | :none, type: :directory | :symlink | :file }
Information about a given file, directory, or symlink
๐ท๏ธ Keys
- filename ::
String.t/0
e.g "my_file.txt", "my_dir/" or "my_sym_link -> target/" - modified_datetime ::
DateTime.t/0
- size ::
integer
e.g 1000 access ::
:read | :write | :read_write | :none
type ::
:directory | :symlink | :file
๐ See Also
๐ Resources
@type path() :: String.t()
A string representing a file path (e.g "/path/to/file.txt"
or "/path/to/dir/"
)
โ ๏ธ Reminders
Relative paths
The Server will ensure all paths sent to the connector are absolute paths,
so you don't need to worry about handling relative paths, or ..
notations
๐ Resources
@type socket() :: port()
A Port representing a socket to communicate with an FTP client.
โ ๏ธ Reminders
Sockets are everywhere
This socket represents the TCP connection between the FTP Server and the client (often through port 21)
While related, this socket is not a PASV socket, which is a negotiated, temporary socket for sending or receiving data.
๐ Resources
Callbacks
@callback create_write_func(path(), connector_state(), opts :: list()) :: function()
Create a function/1 that writes a stream to storage
๐ท๏ธ Params
- path ::
path/0
- connector_state ::
connector_state/0
- opts :: list of options
๐ป Examples
@impl StorageConnector
def create_write_func(path, connector_state, opts \ []) do
fn stream ->
fs = File.stream!(path)
try do
_ =
stream
|> chunk_stream(opts)
|> Enum.into(fs)
{:ok, connector_state}
rescue
_ ->
{:error, "Failed to transfer"}
end
end
end
@callback delete_directory(path(), connector_state()) :: {:ok, connector_state()} | {:error, term()}
Deletes a given directory
๐ท๏ธ Params
- path ::
path/0
- connector_state ::
connector_state/0
โคต๏ธ Returns
โ On Success
{:ok, connector_state}
โ On Failure
{:error, err}
๐ป Examples
iex> alias ExFTP.Storage.FileConnector
iex> connector_state = %{current_working_directory: "/"}
iex> dir_to_make = File.cwd!() |> Path.join("new_dir")
iex> {:ok, connector_state} = FileConnector.make_directory(dir_to_make, connector_state)
iex> dir_to_rm = dir_to_make
iex> {:ok, connector_state} = FileConnector.delete_directory(dir_to_rm, connector_state)
iex> FileConnector.directory_exists?(dir_to_rm, connector_state)
false
๐ Resources
- ๐ RFC 959 (page-32)
- ๐ RFC 3659
- ๐ฌ Contact the maintainer (he's happy to help!)
@callback delete_file(path(), connector_state()) :: {:ok, connector_state()} | {:error, term()}
Deletes a given file
๐ท๏ธ Params
- path ::
path/0
- connector_state ::
connector_state/0
โคต๏ธ Returns
โ On Success
{:ok, connector_state}
โ On Failure
{:error, err}
๐ Resources
- ๐ RFC 959 (page-32)
- ๐ RFC 3659
- ๐ฌ Contact the maintainer (he's happy to help!)
@callback directory_exists?(path(), connector_state()) :: boolean()
Whether a given path is an existing directory
๐ท๏ธ Params
- path ::
path/0
- connector_state ::
connector_state/0
โคต๏ธ Returns
โ On Success
`true` or `false`
๐ป Examples
iex> alias ExFTP.Storage.FileConnector
iex> FileConnector.directory_exists?("/tmp", %{current_working_directory: "/"})
true
iex> FileConnector.directory_exists?("/does-not-exist", %{current_working_directory: "/"})
false
๐ Resources
- ๐ RFC 959 (page-32)
- ๐ RFC 3659
- ๐ฌ Contact the maintainer (he's happy to help!)
@callback get_content(path(), connector_state()) :: {:ok, any()} | {:error, term()}
Returns a stream to read the raw bytes of an object specified by a given path
๐ท๏ธ Params
- path ::
path/0
- connector_state ::
connector_state/0
โคต๏ธ Returns
โ On Success
{:ok, data}
โ On Failure
{:error, err}
๐ป Examples
iex> alias ExFTP.Storage.FileConnector
iex> connector_state = %{current_working_directory: "/"}
iex> file_to_get_content = File.cwd!() |> File.ls!() |> Enum.filter(&String.contains?(&1,".")) |> hd()
iex> path = Path.join(File.cwd!(), file_to_get_content)
iex> {:ok, _data} = FileConnector.get_content(path, connector_state)
๐ Resources
- ๐ RFC 959 (page-30)
- ๐ RFC 3659
- ๐ฌ Contact the maintainer (he's happy to help!)
@callback get_content_info(path(), connector_state()) :: {:ok, content_info()} | {:error, term()}
Returns content_info/0
representing a single object in a given directory
๐ท๏ธ Params
- path ::
path/0
- connector_state ::
connector_state/0
โคต๏ธ Returns
โ On Success
{:ok, %{...}}
โ On Failure
{:error, err}
๐ป Examples
iex> alias ExFTP.Storage.FileConnector
iex> connector_state = %{current_working_directory: "/"}
iex> file_to_get_info = File.cwd!() |> File.ls!() |> hd()
iex> path = Path.join(File.cwd!(), file_to_get_info)
iex> {:ok, content_info} = FileConnector.get_content_info(path, connector_state)
๐ Resources
- ๐ RFC 959 (page-32)
- ๐ RFC 3659
- ๐ฌ Contact the maintainer (he's happy to help!)
@callback get_directory_contents(path(), connector_state()) :: {:ok, [content_info()]} | {:error, term()}
Returns a list of content_info/0
representing each object in a given directory
๐ท๏ธ Params
- path ::
path/0
- connector_state ::
connector_state/0
โคต๏ธ Returns
โ On Success
{:ok, [%{...}, ...]}
โ On Failure
{:error, err}
๐ป Examples
iex> alias ExFTP.Storage.FileConnector
iex> connector_state = %{current_working_directory: "/"}
iex> dir = File.cwd!()
iex> {:ok, _content_infos} = FileConnector.get_directory_contents(dir, connector_state)
๐ Resources
- ๐ RFC 959 (page-32)
- ๐ RFC 3659
- ๐ฌ Contact the maintainer (he's happy to help!)
@callback get_working_directory(connector_state()) :: String.t()
Returns the current working directory
๐ท๏ธ Params
- connector_state ::
connector_state/0
โคต๏ธ Returns
โ On Success
(The working directory)
๐ป Examples
iex> alias ExFTP.Storage.FileConnector
iex> FileConnector.get_working_directory(%{current_working_directory: "/"})
"/"
โ ๏ธ Reminders
Doesn't the connector_state already have it?
Most Storage Connectors will just return what's already in the connector_state. However, this method is implemented just in case a Connector has a different way of determining the current working directory.
๐ Resources
- ๐ RFC 959 (page-32)
- ๐ RFC 3659
- ๐ฌ Contact the maintainer (he's happy to help!)
@callback make_directory(path(), connector_state()) :: {:ok, connector_state()} | {:error, term()}
Creates a directory, given a path
๐ท๏ธ Params
- path ::
path/0
- connector_state ::
connector_state/0
โคต๏ธ Returns
โ On Success
{:ok, connector_state}
โ On Failure
{:error, err}
๐ป Examples
iex> alias ExFTP.Storage.FileConnector
iex> connector_state = %{current_working_directory: "/"}
iex> dir_to_make = File.cwd!() |> Path.join("new_dir")
iex> {:ok, connector_state} = FileConnector.make_directory(dir_to_make, connector_state)
iex> FileConnector.directory_exists?(dir_to_make, connector_state)
true
iex> {:ok, _connector_state} = FileConnector.delete_directory(dir_to_make, connector_state)
iex> FileConnector.directory_exists?(dir_to_rm, connector_state)
false
๐ Resources
- ๐ RFC 959 (page-32)
- ๐ RFC 3659
- ๐ฌ Contact the maintainer (he's happy to help!)