defmodule Spotify.Search do @moduledoc """ Spotify search endpoints. Spotify allows querying for artists, albums, playlists, and tracks. """ use Spotify.Responder import Spotify.Helpers alias Spotify.{ Album, Artist, Client, Paging, Playlist, Track } @keys ~w[albums artists playlists tracks] @doc """ Search for a playlist. [Spotify Documentation](https://developer.spotify.com/web-api/search-item/) **Method**: `GET` **Required Params:** `q`, `type` **Optional Params:** `limit`, `offset`, `market` Spotify.Search.query(conn, q: "foo", type: "playlist") # => {:ok, %{ items: [%Spotify.Playlist{..} ...]}} """ def query(conn, params) do url = query_url(params) conn |> Client.get(url) |> handle_response end @doc """ Search for a playlist, artist, album, or track. iex> Spotify.Search.query_url(q: "foo", type: "playlist") "https://api.spotify.com/v1/search?q=foo&type=playlist" """ def query_url(params) do "https://api.spotify.com/v1/search" <> query_string(params) end @doc """ Implements the hook required by the Responder behaviour """ def build_response(body) do body |> map_paging |> append_items end defp map_paging(body), do: {Paging.response(body, []), body} defp append_items({paging, body}) do body |> Map.take(@keys) |> Map.to_list() |> Enum.flat_map_reduce([], &reducer/2) |> update_paging(paging) end defp reducer({key, data}, acc), do: {map_to_struct(key, data["items"]), acc} defp update_paging({items, _rest}, paging), do: paging |> Map.put(:items, items) defp map_to_struct("artists", artists), do: Artist.build_artists(artists) defp map_to_struct("tracks", tracks), do: Track.build_tracks(tracks) defp map_to_struct("playlists", playlists), do: Playlist.build_playlists(playlists) defp map_to_struct("albums", albums), do: Enum.map(albums, &to_struct(Album, &1)) end