aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Duarte <jeancduarte@gmail.com>2015-12-04 17:43:57 -0200
committerJean Duarte <jeancduarte@gmail.com>2015-12-04 17:43:57 -0200
commit04c99f0cc94cb3108e8686d323169bc563e7dc4d (patch)
tree102a95cbba4d29c9553b5fcffeb42e93e78235b3
parent284d77c86efc9c587a846b8effa2964e3b7d59e4 (diff)
parent670aa48b4c88276f9ef68a733b5dd6150685777f (diff)
downloadmailchimp-04c99f0cc94cb3108e8686d323169bc563e7dc4d.tar.gz
mailchimp-04c99f0cc94cb3108e8686d323169bc563e7dc4d.tar.xz
merged with raw1z/mailchimp
-rw-r--r--README.md21
-rw-r--r--lib/account.ex41
-rw-r--r--lib/config.ex77
-rw-r--r--lib/httpclient.ex39
-rw-r--r--lib/link.ex20
-rw-r--r--lib/list.ex88
-rw-r--r--lib/mailchimp.ex76
-rw-r--r--lib/member.ex30
-rw-r--r--mix.exs7
-rw-r--r--mix.lock2
10 files changed, 248 insertions, 153 deletions
diff --git a/README.md b/README.md
index a713b54..b07b0d6 100644
--- a/README.md
+++ b/README.md
@@ -21,27 +21,16 @@ def application do
end
```
-## Usage
+## API
-1. Put your API key in your *config.exs* file:
+Put your API key in your *config.exs* file:
```elixir
config :mailchimp,
apikey: "your api-us10"
```
+For now Mailchimp only supports HTTP Basic Auth.
-2. Start a new process:
+## Documentation
- Mailchimp.start_link
-
-### Getting Account Details
-
- Mailchimp.get_account_details()
-
-### Getting All Lists
-
- Mailchimp.get_all_lists()
-
-### Adding a Member to a List
-
- Mailchimp.add_member("list_id", "e-mail")
+TODO
diff --git a/lib/account.ex b/lib/account.ex
index 586d274..7e24858 100644
--- a/lib/account.ex
+++ b/lib/account.ex
@@ -1,10 +1,41 @@
defmodule Mailchimp.Account do
- import Mailchimp.HTTPClient
+ alias HTTPoison.Response
+ alias Mailchimp.HTTPClient
+ alias Mailchimp.Link
+ alias Mailchimp.List
- def get_details(config) do
- map_header = %{"Authorization" => "apikey #{config.apikey}"}
- url = config.apiroot
- get(url, map_header)
+ defstruct account_id: nil, account_name: nil, contact: nil, last_login: nil, total_subscribers: 0, links: []
+
+ def new(attributes) do
+ %__MODULE__{
+ account_id: attributes[:account_id],
+ account_name: attributes[:account_name],
+ contact: attributes[:contact],
+ last_login: attributes[:last_login],
+ total_subscribers: attributes[:total_subscribers],
+ links: Link.get_links_from_attributes(attributes)
+ }
+ end
+
+ def get do
+ {:ok, response} = HTTPClient.get("/")
+ case response do
+ %Response{status_code: 200, body: body} ->
+ {:ok, __MODULE__.new(body)}
+
+ %Response{status_code: _, body: body} ->
+ {:error, body}
+ end
end
+ def lists(%__MODULE__{links: %{"lists" => %Link{href: href}}}) do
+ {:ok, response} = HTTPClient.get(href)
+ case response do
+ %Response{status_code: 200, body: body} ->
+ {:ok, Enum.map(body.lists, &List.new(&1))}
+
+ %Response{status_code: _, body: body} ->
+ {:error, body}
+ end
+ end
end
diff --git a/lib/config.ex b/lib/config.ex
new file mode 100644
index 0000000..8778e5b
--- /dev/null
+++ b/lib/config.ex
@@ -0,0 +1,77 @@
+defmodule Mailchimp.Config do
+ use GenServer
+
+ defstruct api_key: nil, api_version: "3.0"
+
+ require Logger
+
+ # Public API
+
+ def start_link(%__MODULE__{}=config) do
+ Agent.start_link(fn -> config end, name: __MODULE__)
+ end
+
+ def start_link do
+ config = %__MODULE__{
+ api_key: get_api_key_from_config,
+ api_version: get_api_version_from_config
+ }
+
+ Agent.start_link(fn -> config end, name: __MODULE__)
+ end
+
+ def root_endpoint do
+ Agent.get(__MODULE__, fn %{api_key: api_key, api_version: api_version} ->
+ {:ok, shard} = get_shard(api_key)
+ "https://#{shard}.api.mailchimp.com/#{api_version}/"
+ end)
+ end
+
+ def api_key do
+ Agent.get(__MODULE__, fn %{api_key: api_key} -> api_key end)
+ end
+
+ def api_version do
+ Agent.get(__MODULE__, fn %{api_version: api_version} -> api_version end)
+ end
+
+ def update(config) when is_list(config) do
+ Agent.update(__MODULE__, fn current_config ->
+ Enum.reduce(config, current_config, fn({k,v}, acc) -> Map.put(acc, k, v) end)
+ end)
+ end
+
+ # Private methods
+
+ defp sanitize_api_key({:system, env_var}) do
+ sanitize_api_key System.get_env(env_var)
+ end
+
+ defp sanitize_api_key(api_key) do
+ api_key
+ end
+
+ defp get_api_key_from_config do
+ sanitize_api_key(Application.get_env(:mailchimp, :apikey)) || sanitize_api_key(Application.get_env(:mailchimp, :api_key))
+ end
+
+ defp get_api_version_from_config do
+ Application.get_env(:mailchimp, :api_version) || "3.0"
+ end
+
+ defp get_shard(api_key) do
+ shard = api_key
+ |> String.split(~r{-})
+ |> List.last
+
+ case shard do
+ nil ->
+ Logger.error "[mailchimp] This doesn't look like an API Key: #{api_key}"
+ Logger.error "[mailchimp] The API Key should have both a key and a server name, separated by a dash, like this: abcdefg8abcdefg6abcdefg4-us1"
+ :error
+
+ _ ->
+ {:ok, shard}
+ end
+ end
+end
diff --git a/lib/httpclient.ex b/lib/httpclient.ex
index bd58b92..433b57c 100644
--- a/lib/httpclient.ex
+++ b/lib/httpclient.ex
@@ -1,33 +1,26 @@
defmodule Mailchimp.HTTPClient do
+ use HTTPoison.Base
- def get(url, header) do
- case HTTPoison.get(url, header) do
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
- process_response_body body
- {:ok, %HTTPoison.Response{status_code: 404}} ->
- "Not found :("
- {:error, %HTTPoison.Error{reason: reason}} ->
- reason
- end
- end
+ alias Mailchimp.Config
- def post(url, body, header) do
- case HTTPoison.post(url, body, header) do
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
- process_response_body body
- {:ok, %HTTPoison.Response{status_code: 400, body: body}} ->
- process_response_body body
- {:ok, %HTTPoison.Response{status_code: 404}} ->
- "Not found :("
- {:error, %HTTPoison.Error{reason: reason}} ->
- reason
+ def process_url(url) do
+ root_endpoint = Config.root_endpoint
+ if String.starts_with?(url, root_endpoint) do
+ url
+ else
+ root_endpoint <> url
end
end
def process_response_body(body) do
- body
- |> Poison.decode!
- |> Enum.map(fn({k, v}) -> {String.to_atom(k), v} end)
+ Poison.decode!(body, keys: :atoms)
end
+ def process_request_headers(headers) do
+ encoded_api = Base.encode64(":#{Config.api_key}")
+ headers
+ |> Enum.into(%{})
+ |> Map.put("Authorization", "Basic #{encoded_api}")
+ |> Enum.into([])
+ end
end
diff --git a/lib/link.ex b/lib/link.ex
new file mode 100644
index 0000000..fc355b9
--- /dev/null
+++ b/lib/link.ex
@@ -0,0 +1,20 @@
+defmodule Mailchimp.Link do
+ defstruct rel: nil, href: nil, method: nil, schema: nil, target_schema: nil
+
+ def new(attributes) do
+ %__MODULE__{
+ rel: attributes[:rel],
+ href: attributes[:href],
+ method: attributes[:method],
+ schema: attributes[:schema],
+ target_schema: attributes[:targetSchema]
+ }
+ end
+
+ def get_links_from_attributes(attributes) do
+ (attributes._links || [])
+ |> Enum.map(&__MODULE__.new(&1))
+ |> Enum.map(&({&1.rel, &1}))
+ |> Enum.into(%{})
+ end
+end
diff --git a/lib/list.ex b/lib/list.ex
index 8589eb2..81853e4 100644
--- a/lib/list.ex
+++ b/lib/list.ex
@@ -1,47 +1,63 @@
defmodule Mailchimp.List do
- import Mailchimp.HTTPClient
+ alias HTTPoison.Response
+ alias Mailchimp.HTTPClient
+ alias Mailchimp.Link
+ alias Mailchimp.Member
- def all(config) do
- map_header = %{"Authorization" => "apikey #{config.apikey}"}
- config.apiroot
- |> build_url
- |> get(map_header)
- end
+ defstruct id: nil, name: nil, contact: nil, permission_reminder: nil, use_archive_bar: nil, campaign_defaults: nil, notify_on_subscribe: nil, notify_on_unsubscribe: nil, list_rating: nil, email_type_option: nil, subscribe_url_short: nil, subscribe_url_long: nil, beamer_address: nil, visibility: nil, modules: [], stats: nil, links: []
- def members(config, list_id) do
- map_header = %{"Authorization" => "apikey #{config.apikey}"}
- config.apiroot <> "lists/" <> list_id <> "/members"
- |> get(map_header)
+ def new(attributes) do
+ %__MODULE__{
+ id: attributes[:id],
+ name: attributes[:name],
+ contact: attributes[:contact],
+ permission_reminder: attributes[:permission_reminder],
+ use_archive_bar: attributes[:use_archive_bar],
+ campaign_defaults: attributes[:campaign_defaults],
+ notify_on_subscribe: attributes[:notify_on_subscribe],
+ notify_on_unsubscribe: attributes[:notify_on_unsubscribe],
+ list_rating: attributes[:list_rating],
+ email_type_option: attributes[:email_type_option],
+ subscribe_url_short: attributes[:subscribe_url_short],
+ subscribe_url_long: attributes[:subscribe_url_long],
+ beamer_address: attributes[:beamer_address],
+ visibility: attributes[:visibility],
+ modules: attributes[:modules],
+ stats: attributes[:stats],
+ links: Link.get_links_from_attributes(attributes)
+ }
end
- def add_member(config, %{"list_id" => list_id, "email" => email}) do
- map_header = %{"Authorization" => "apikey #{config.apikey}"}
- {:ok, body} = Poison.encode(%{email_address: email, status: "subscribed"})
- config.apiroot <> "lists/" <> list_id <> "/members"
- |> post(body, map_header)
- end
+ def members(%__MODULE__{links: %{"members" => %Link{href: href}}}) do
+ {:ok, response} = HTTPClient.get(href)
+ case response do
+ %Response{status_code: 200, body: body} ->
+ {:ok, Enum.map(body.members, &Member.new(&1))}
- def add_pending_member(config, %{"list_id" => list_id, "email" => email}) do
- map_header = %{"Authorization" => "apikey #{config.apikey}"}
- {:ok, body} = Poison.encode(%{email_address: email, status: "pending"})
- config.apiroot <> "lists/" <> list_id <> "/members"
- |> post(body, map_header)
+ %Response{status_code: _, body: body} ->
+ {:error, body}
+ end
end
- def build_url(root) do
- params = [
- {:fields, ["lists.id", "lists.name", "lists.stats.member_count"]},
- {:count, 10},
- {:offset, 0}
- ]
- fields = "fields=" <> as_string(params[:fields])
- count = "count=" <> to_string params[:count]
- offset = "offset=" <> to_string params[:offset]
- url = root <> "lists?" <> fields <> "&" <> count <> "&" <> offset
- end
+ def create_member(%__MODULE__{links: %{"members" => %Link{href: href}}}, email_address, status, merge_fields \\ %{}) when is_binary(email_address) and is_map(merge_fields) and status in [:subscribed, :pending, :unsubscribed, :cleaned] do
+ {:ok, response} = HTTPClient.get(href)
+ case response do
+ %Response{status_code: 200, body: body} ->
+ links = Link.get_links_from_attributes(body)
+ href = links["create"].href
+ data = %{email_address: email_address, status: status, merge_fields: merge_fields}
- def as_string(param) do
- Enum.reduce(param, fn(s, acc) -> acc<>","<>s end)
- end
+ {:ok, response} = HTTPClient.post href, Poison.encode!(data)
+ case response do
+ %Response{status_code: 200, body: body} ->
+ {:ok, Member.new(body)}
+
+ %Response{status_code: _, body: body} ->
+ {:error, body}
+ end
+ %Response{status_code: _, body: body} ->
+ {:error, body}
+ end
+ end
end
diff --git a/lib/mailchimp.ex b/lib/mailchimp.ex
index 4281a36..5a40919 100644
--- a/lib/mailchimp.ex
+++ b/lib/mailchimp.ex
@@ -1,76 +1,14 @@
defmodule Mailchimp do
use Application
- use GenServer
- require Logger
- @apikey Application.get_env :mailchimp, :apikey
+ def start(_type, _args) do
+ import Supervisor.Spec, warn: false
- ### Public API
- def start_link do
- shard = get_shard
- apiroot = "https://#{shard}.api.mailchimp.com/3.0/"
- config = %{apiroot: apiroot, apikey: @apikey}
- GenServer.start_link(Mailchimp, config, name: :mailchimp)
- end
-
- def get_account_details do
- GenServer.call(:mailchimp, :account_details)
- end
-
- def get_all_lists do
- GenServer.call(:mailchimp, :all_lists)
- end
-
- def get_list_members(list_id) do
- GenServer.call(:mailchimp, {:list_members, list_id})
- end
-
- def add_member(list_id, email) do
- GenServer.call(:mailchimp, {:add_member, list_id, email})
- end
+ children = [
+ worker(Mailchimp.Config, []),
+ ]
- def add_pending_member(list_id, email) do
- GenServer.call(:mailchimp, {:add_pending_member, list_id, email})
+ opts = [strategy: :one_for_one, name: Aliver.Supervisor]
+ Supervisor.start_link(children, opts)
end
-
- ### Server API
- def handle_call(:account_details, _from, config) do
- details = Mailchimp.Account.get_details(config)
- {:reply, details, config}
- end
-
- def handle_call(:all_lists, _from, config) do
- lists = Mailchimp.List.all(config)
- {:reply, lists, config}
- end
-
- def handle_call({:list_members, list_id}, _from, config) do
- members = Mailchimp.List.members(config, list_id)
- {:reply, members, config}
- end
-
- def handle_call({:add_member, list_id, email}, _from, config) do
- member = Mailchimp.List.add_member(config, %{"list_id" => list_id, "email" => email})
- {:reply, member, config}
- end
-
- def handle_call({:add_pending_member, list_id, email}, _from, config) do
- member = Mailchimp.List.add_pending_member(config, %{"list_id" => list_id, "email" => email})
- {:reply, member, config}
- end
-
- def get_shard do
- parts = @apikey
- |> String.split(~r{-})
-
- case length(parts) do
- 2 ->
- List.last parts
- _ ->
- Logger.error "This doesn't look like an API Key: #{@apikey}"
- Logger.info "The API Key should have both a key and a server name, separated by a dash, like this: abcdefg8abcdefg6abcdefg4-us1"
- {:error}
- end
- end
-
end
diff --git a/lib/member.ex b/lib/member.ex
new file mode 100644
index 0000000..3f49722
--- /dev/null
+++ b/lib/member.ex
@@ -0,0 +1,30 @@
+defmodule Mailchimp.Member do
+ alias Mailchimp.Link
+
+ defstruct email_address: nil, email_client: nil, email_type: nil, id: nil, ip_opt: nil, ip_signup: nil, language: nil, last_changed: nil, list_id: nil, location: nil, member_rating: nil, merge_fields: nil, stats: nil, status: nil, status_if_new: nil, timestamp_opt: nil, timestamp_signup: nil, unique_email_id: nil, vip: nil
+
+ def new(attributes) do
+ %{
+ email_address: attributes[:email_address],
+ email_client: attributes[:email_client],
+ email_type: attributes[:email_type],
+ id: attributes[:id],
+ ip_opt: attributes[:ip_opt],
+ ip_signup: attributes[:ip_signup],
+ language: attributes[:language],
+ last_changed: attributes[:last_changed],
+ list_id: attributes[:list_id],
+ location: attributes[:location],
+ member_rating: attributes[:member_rating],
+ merge_fields: attributes[:merge_fields],
+ stats: attributes[:stats],
+ status: attributes[:status],
+ status_if_new: attributes[:status_if_new],
+ timestamp_opt: attributes[:timestamp_opt],
+ timestamp_signup: attributes[:timestamp_signup],
+ unique_email_id: attributes[:unique_email_id],
+ vip: attributes[:vip],
+ links: Link.get_links_from_attributes(attributes)
+ }
+ end
+end
diff --git a/mix.exs b/mix.exs
index bee5510..5436fa8 100644
--- a/mix.exs
+++ b/mix.exs
@@ -13,7 +13,8 @@ defmodule Mailchimp.Mixfile do
end
def application do
- [applications: [:logger, :httpoison]]
+ [mod: {Mailchimp, []},
+ applications: [:logger, :httpoison]]
end
defp description do
@@ -23,8 +24,8 @@ defmodule Mailchimp.Mixfile do
end
defp deps do
- [{:httpoison, "~> 0.6"},
- {:poison, "~> 1.4"}]
+ [{:httpoison, "~> 0.7.3"},
+ {:poison, "~> 1.5.0"}]
end
defp package do
diff --git a/mix.lock b/mix.lock
index f4b1089..9922629 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,5 +1,5 @@
%{"hackney": {:hex, :hackney, "1.3.2"},
- "httpoison": {:hex, :httpoison, "0.7.4"},
+ "httpoison": {:hex, :httpoison, "0.7.3"},
"idna": {:hex, :idna, "1.0.2"},
"poison": {:hex, :poison, "1.5.0"},
"ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.5"}}