lib/github/plugin.ex

defmodule GitHub.Plugin do
  @moduledoc """
  Plugins provide functionality for the API client

  > #### Note {:.info}
  >
  > This module is unlikely to be used directly by applications. The documentation contained here
  > is useful for developers who would like to implement new functionality for the client.

  This library has no opinions on how to complete API requests. Instead of requiring a particular
  HTTP client or JSON serializer, requests are completed using a series of plugins called a
  **stack**. To modify the behavior of the client, applications can add or modify plugins.

  Plugins are functions that accept a `GitHub.Operation` struct — and any options — and return
  either an `:ok` or an `:error` tuple:

      alias GitHub.{Error, Operation}

      @spec my_plugin(Operation.t(), keyword) :: {:ok, Operation.t()} | {:error, Error.t()}
      def my_plugin(operation, opts) do
        # ...
      end

  An `:error` tuple will immediately stop the stack and return the error. An `:ok` tuple will
  continue running the stack with the modified operation struct. If the stack concludes with a
  successful operation, the `response_body` is returned.

  ## Options

  Plugins may accept options that modify their behavior. It is recommended that they consider
  options from three sources:

  1. Options passed to the operation function (available in the `__opts__` key of the operation
    struct's private field). This is likely to include options not relevant to the plugin.
  2. Options passed to the plugin definition (available as the second argument of the plugin
    function).
  3. Options defined globally using the application environment.

  This hierarchy can be accomplished by calling `Keyword.merge/2` on the plugin options argument and
  the operation's options (order matters!), then calling `GitHub.Config.plugin_config/4` to
  incorporate the application environment as a fallback. (For required options, the sibling
  function `GitHub.Config.plugin_config!/3` is also available.)

  In this way, plugins can be configured at all levels of the code:

      # Highest precedence: options from the caller
      GitHub.Repos.get("aj-foster", "open-api-github", some: :option)

      # Medium precedence: options in the plugin definition
      {MyPlugin, :my_function, some: :option}

      # Lowest precedence: global configuration
      config :oapi_github, MyPlugin, some: :option

  Plugins should take care to document their available options and avoid naming collisions.
  """
end