defmodule Flickrex do @moduledoc ~S""" Flickr client library for Elixir. ## Configuration config :flickrex, :oauth, [ consumer_key: "...", consumer_secret: "...", ] The configuration also accepts `access_token` and `access_token_secret` keys, but it is highly recommended to store these values separately for each authenticated user, rather than setting them globally. ## Examples flickrex = Flickrex.new {:ok, photos} = Flickrex.get(flickrex, "flickr.photos.getRecent", per_page: 5) Instead of using `get/3` and `post/3` directly, refer to the `Flickr` Module API. ## Authentication Certain Flickr methods require authorization from a user account. You must present an authorizaton URL to the user, and obtain a verification code that can be exchanged for access tokens. You can store and re-use the access tokens without having to repeat the authorization step. ### Manual Verification flickrex = Flickrex.new {:ok, request} = Flickrex.fetch_request_token(flickrex) auth_url = Flickrex.get_authorize_url(request) # Open the URL in your browser, authorize the app, and get the verify token verify = "..." {:ok, access} = Flickrex.fetch_access_token(flickrex, request, verify) flickrex = Flickrex.put_access_token(flickrex, access) # Test that the login was successful {:ok, login} = Flickr.Test.login(flickrex) ### Callback Verification Specify a callback URL when generating the request token: flickrex = Flickrex.new url = "https://example.com/check" {:ok, request} = Flickrex.fetch_request_token(flickrex, oauth_callback: url) auth_url = Flickrex.get_authorize_url(request) Present the `auth_url` to the user and ask them to complete the authorization process. Save the `request.token` and the `request.secret`. After following the `auth_url` and authorizing your app, the user will be re-directed to: ```sh https://example.com/check?oauth_token=FOO&oauth_verifier=BAZ ``` The `oauth_token` in the URL query corresponds to the `request.token` from the previous step, which you will need to recall the request token `secret`. # use `oauth_token` to look up the request token and secret {:ok, access} = Flickrex.fetch_access_token(flickrex, request_token, request_secret, oauth_verifier) flickrex = Flickrex.put_access_token(flickrex, access) Finally, save `flickrex.access.token` and `flickrex.access.secret` for this user, which you can re-use. ## Re-authenticating Look up the `access_token` and `access_token_secret` you have saved for the user, and use them to generate a new client: flickrex = Flickrex.new |> Flickrex.put_access_token(access_token, access_token_secret) """ alias Flickrex.API alias Flickrex.API.Auth alias Flickrex.Client alias Flickrex.Parser alias Flickrex.Schema @type response :: Parser.response @doc """ Creates a Flickrex client using the application config """ @spec new :: Client.t def new do new(Application.get_env(:flickrex, :oauth)) end @doc """ Creates a Flickrex client using the provided config The accepted parameters are: * `:consumer_token` - Flickr API key * `:consumer_secret` - Flicrkr API shared secret * `:access_token` - Per-user access token * `:access_token_secret` - Per-user access token secret """ @spec new(Keyword.t) :: Client.t defdelegate new(config), to: Client @doc """ Updates a Flickrex client with a config value """ @spec update(Client.t, atom, String.t) :: Client.t defdelegate update(client, key, value), to: Client, as: :put @doc """ Adds an access token to a client """ @spec put_access_token(Client.t, Schema.Access.t) :: Client.t def put_access_token(client, %Schema.Access{oauth_token: token, oauth_token_secret: secret}) do put_access_token(client, token, secret) end @doc """ Adds an access token and secret to a client """ @spec put_access_token(Client.t, String.t, String.t) :: Client.t def put_access_token(client, token, secret) do client |> Client.put(:access_token, token) |> Client.put(:access_token_secret, secret) end @doc """ Fetches a temporary token to authenticate the user to your application ## Options * `oauth_callback` - For web apps, the URL to redirect the user to after completing the authorization sequence. The URL will include query params `oauth_token` and `oauth_verifier`. If this option is not set, the user will be presented with a verification code that they must present to your application manually. """ @spec fetch_request_token(Client.t, Keyword.t) :: {:ok, Client.Request.t} | {:error, binary} def fetch_request_token(client, params \\ []) do Auth.fetch_request_token(client, params) end @doc """ Generates a Flickr authorization page URL for a user ## Examples: {:ok, request} = Flickrex.fetch_request_token(flickrex) auth_url = Flickrex.get_authorize_url(request) """ @spec get_authorize_url(Client.Request.t, Keyword.t) :: binary def get_authorize_url(request_token, params \\ []) do Auth.get_authorize_url(request_token, params) end @doc """ Fetches an access token from Flickr The function takes an existing Flickrex client, a request token struct, and a verify token generated by the Flickr authorization step. ## Examples: {:ok, access} = Flickrex.fetch_access_token(flickrex, token, verify) """ @spec fetch_access_token(Client.t, Client.Request.t, binary) :: Schema.Access.t | {:error, term} defdelegate fetch_access_token(client, request_token, verify), to: API.Auth @doc """ Fetches an access token from Flickr The function takes an existing Flickrex client, a request token and secret, and a verify token generated by the Flickr authorization step. """ @spec fetch_access_token(Client.t, String.t, String.t, binary) :: Schema.Access.t | {:error, term} def fetch_access_token(client, token, secret, verify) do fetch_access_token(client, %Client.Request{token: token, secret: secret}, verify) end @doc """ Makes a GET request to the Flickr REST endpoint ## Examples: {:ok, response} = Flickrex.get(flickrex, "flickr.photos.getRecent", per_page: 5) """ @spec get(Client.t, binary, Keyword.t) :: response def get(client, method, args \\ []) do call(client, :get, method, args) end @doc """ Makes a POST request to the Flickr REST endpoint ## Examples: {:ok, response} = Flickrex.post(flickrex, "flickr.photos.addTags", photo_id: photo_id, tags: "tag1,tag2") """ @spec post(Client.t, binary, Keyword.t) :: response def post(client, method, args \\ []) do call(client, :post, method, args) end @spec call(Client.t, :get | :post, binary, Keyword.t) :: response defp call(client, http_method, method, args) do case API.Base.call(client, http_method, method, args) do {:ok, result} -> Parser.parse(result) result -> result end end end