Giter Site home page Giter Site logo

nebo15 / multiverse Goto Github PK

View Code? Open in Web Editor NEW
93.0 6.0 11.0 96 KB

Elixir package that allows to add compatibility layers via API gateways.

Home Page: https://hex.pm/packages/multiverse

License: MIT License

Elixir 100.00%
api plug elixir hex versioning gateways elixir-lang

multiverse's Introduction

Multiverse

Deps Status Hex.pm Downloads Latest Version License Build Status Coverage Status Ebert

This plug helps to manage multiple API versions based on request and response gateways. This is an awesome practice to hide your backward compatibility. It allows to have your code in a latest possible version, without duplicating controllers or models. We use it in production.

Compatibility Layers

Inspired by Stripe API. Read more at MOVE FAST, DON'T BREAK YOUR API or API versioning.

Goals

  • reduce changes required to support multiple API versions;
  • provide a way to test and schedule API version releases;
  • to have minimum dependencies and low performance hit;
  • to be flexible enough for most of projects to adopt it.

Adapters

Multiverse allows you to use a custom adapter which can, for eg.:

  • store consumer version upon his first request and re-use it as default each time consumer is using your API, eliminating need of passing version headers for them (a.k.a. version pinning). Change this version when consumer has explicitly set it;
  • use other than ISO date version types, eg. incremental counters (v1, v2);
  • handle malformed versions by responding with JSON errors.

Default adapter works with ISO-8601 date from x-api-version header (configurable). For malformed versions it would log a warning and fallback to the default date (configured via :default_version setting):

  • :first - apply all gates by default. This option is useful when you integrate Multiverse in existing project and API consumers are not ready to accept latest changes by default;
  • :latest - user current date as default version. This option is useful when there are no legacy clients or there was no breaking changes before those clients started to send API version.

ISO date adapter allows API clients to use channel name instead of date:

  • latest channel would fallback to the current date;
  • edge channel would disable all changes altogether.

Channels allow you to plan version releases upfront and test them without affecting users, just set future date for a change and pass it explicitly or use edge channel to test latest application version.

Installation

The package (take look at hex.pm) can be installed as:

  1. Add multiverse to your list of dependencies in mix.exs:
def deps do
  [{:multiverse, "~> 2.0.0"}]
end
  1. Make sure that multiverse is available at runtime in your production:
def application do
  [applications: [:multiverse]]
end

How to use

  1. Insert this plug into your API pipeline (in your router.ex):
pipeline :api do
  plug :accepts, ["json"]
  plug :put_secure_browser_headers

  plug Multiverse, default_version: :latest
end
  1. Define module that handles change
defmodule AccountTypeChange do
  @behaviour Multiverse.Change

  def handle_request(%Plug.Conn{} = conn) do
    # Mutate your request here
    IO.inspect "AccountTypeChange.handle_request applied to request"
    conn
  end

  def handle_response(%Plug.Conn{} = conn) do
    # Mutate your response here
    IO.inspect "AccountTypeChange.handle_response applied to response"
    conn
  end
end
  1. Enable the change:
pipeline :api do
  plug :accepts, ["json"]
  plug :put_secure_browser_headers

  plug Multiverse,
    default_version: :latest,
    gates: %{
      ~D[2016-07-21] => [AccountTypeChange]
    }
end
  1. Send your API requests with X-API-Version header with version lower or equal to 2016-07-20.

Overriding version header

You can use any version headers by passing option to Multiverse:

pipeline :api do
  plug :accepts, ["json"]
  plug :put_secure_browser_headers

  plug Multiverse,
    default_version: :latest,
    version_header: "x-my-version-header",
    gates: %{
      ~D[2016-07-21] => [AccountTypeChange]
    }
end

Using custom adapters

You can use your own adapter which implements Multiverse.Adapter behaviour:

pipeline :api do
  plug :accepts, ["json"]
  plug :put_secure_browser_headers

  plug Multiverse,
    default_version: :latest,
    adapter: MyApp.SmartMultiverseAdapter,
    gates: %{
      ~D[2016-07-21] => [AccountTypeChange]
    }
end

Structuring your tests

  1. Split your tests into versions:
$ ls -l test/acceptance
total 0
drwxr-xr-x  2 andrew  staff  68 Aug  1 19:23 AccountTypeChange
drwxr-xr-x  2 andrew  staff  68 Aug  1 19:24 OlderChange
  1. Avoid touching request or response in old tests. Create API gates and matching folder in acceptance tests.

Other things you might want to do

  1. Store Multiverse configuration in config.ex:
use Mix.Config

config :my_app, MyApp.Endpoint,
  default_version: :latest,
  gates: %{
    ~D[2016-07-21] => [AccountTypeChange]
  }
plug Multiverse, otp_app: :my_app, endpoint: __MODULE__
  1. Generate API documentation from changes @moduledoc's.

  2. Other awesome stuff. Open an issue and tell me about it! :).

License

See LICENSE.md.

multiverse's People

Contributors

andrewdryga avatar nburkley avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

multiverse's Issues

Version header on the response

Would be nice to have a version header in the response.

  • If no version was specified, I'd default to the latest one (but not the latest date, the latest date specified in the gate configuration).

  • If a version was specified or the error callback loaded a different version, then I'd display that (good sanity check for users that they actually got the version they requested).

README.md is incorrect on default_values?

README states you can use :latest and :edge channels as away of controlling the versions. However, :edge doesn't exist in ~> 2.0.0.

Possible values are :latest and :oldest

** (ArgumentError) invalid Multiverse.Adapters.ISODate :default_version config value, possible values: :latest, :oldest, got: :edge
    lib/multiverse/adapters/iso_date.ex:44: Multiverse.Adapters.ISODate.init/2
    lib/multiverse/adapter.ex:68: Multiverse.Adapter.compile_config!/2
    lib/multiverse.ex:60: Multiverse.init/1

RFC: Add Plug/Phoenix-style macros for route matching in gateways

Whenever gateway is applied, it's responsibility of the package user to identify on which endpoints it should run, but it would be much nicer to be able to use route matching there, like this:

# No method means all methods
mutate_request "/plans/:id" do

end

# Only for GET requests
mutate_request :get, "/plans" do

end

mutate_response "/plans/:id" do

end

mutate_response :get, "/plans" do

end

Additional benefit is that gate would be self-documenting in a way that it's easy to see all changes per endpoint and even to generate simple changelog for them.

TODO:

  • Do not forget to add new macros to .formatter.exs so that they won't be wrapped in brackets.

Workaround JSON encoding/decoding

Right now conn.resp_body in response mutator would have IO list with a JSON-encoded response. We need a way to process the response without decoding it in each response change.

Be able to specify more than one gate per version.

Stripe does something like this:

class VersionChanges
  VERSIONS = {
    '2017-05-25' => [
      Change::AccountTypes,
      Change::CollapseEventRequest,
      Change::EventAccountToUserID
    ],
    '2017-04-06' => [Change::LegacyTransfers],
    '2017-02-14' => [
      Change::AutoexpandChargeDispute,
      Change::AutoexpandChargeRule
    ],
    '2017-01-27' => [Change::SourcedTransfersOnBts],
    ...
  }
end

That way one version change can be composed of several smaller transformations.

Move release date to ENV

Whenever you write code it's hard to tell which on date your version will be released, so it's a good idea to move version date to environment, thus DevOps can manage release date automatically without re-building code.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.