Skip to content

Commit

Permalink
Converted every api call to Either (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrf7 authored May 17, 2020
1 parent 7f93cac commit 72a1a05
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.mfriend.djapp.reviewrequests

import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.mfriend.djapp.common.db.daos.TrackDao
import com.mfriend.djapp.common.db.entities.Track
import com.mfriend.djapp.spotifyapi.SpotifyApi
import com.mfriend.djapp.spotifyapi.models.PlaylistDto
import com.mfriend.djapp.spotifyapi.models.SpotifyErrorBody
import com.mfriend.djapp.typeconverters.toTrack

/**
Expand All @@ -22,13 +25,23 @@ class ReviewRequestRepo(private val spotifyApi: SpotifyApi, private val trackDao
/**
* Gets the list of songs requested to add to the playlist
*/
suspend fun getRequests(): Either<Throwable, List<Track>> = Either.catch {
if (trackDao.getAll().isEmpty()) {
spotifyApi.getUsersTopTracks().items.map { trackDTO ->
trackDao.insert(trackDTO.toTrack())
suspend fun getRequests(): Either<SpotifyErrorBody, List<Track>> {
val allTracks = trackDao.getAll()
return if (allTracks.isEmpty()) {
val tracks =
spotifyApi.getUsersTopTracks().map { it.items }

// If successful, insert all the tracks into the DB, otherwise return the error
when (tracks) {
is Either.Right -> {
tracks.b.forEach { trackDao.insert(it.toTrack()) }
trackDao.getAll().right()
}
is Either.Left -> tracks.a.left()
}
} else {
allTracks.right()
}
trackDao.getAll()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ReviewRequestsViewModel(
// Get the requests and show the first one, unless an error occurred
_currentTrack.value = reviewRequestRepo.getRequests().fold(
ifLeft = {
Timber.e(it, "Got an exception trying to get requests")
Timber.e("Got an exception trying to get requests $it")
TrackReviewErrors.CommunicationError.left()
},
ifRight = { requests ->
Expand All @@ -56,6 +56,7 @@ class ReviewRequestsViewModel(
fun addSongPressed() {
_currentTrack.value?.map { trackToAdd ->
viewModelScope.launch {
// TODO handle the error case
reviewRequestRepo.addSongToPlaylist(trackToAdd, playlist)
nextSong()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.observe
import androidx.navigation.fragment.findNavController
Expand Down Expand Up @@ -53,8 +54,20 @@ class PlaylistFragment : Fragment() {
}

playlistViewModel.playlists.observe(viewLifecycleOwner) {
playlistAdapter.items = it
playlistAdapter.notifyDataSetChanged()
// Show either the list of playlists or an error
it.fold(
ifRight = {
playlistAdapter.items = it
playlistAdapter.notifyDataSetChanged()
},
ifLeft = {
Toast.makeText(
requireContext(),
"Something went wrong",
Toast.LENGTH_LONG
).show()
}
)
}
playlistViewModel.selectedPlaylistDto.observeEvent(this) {
val action =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,23 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.right
import com.mfriend.djapp.common.helper.Event
import com.mfriend.djapp.spotifyapi.SpotifyApi
import com.mfriend.djapp.spotifyapi.models.PlaylistDto
import com.mfriend.djapp.spotifyapi.models.PlaylistRequestDto
import kotlinx.coroutines.launch
import retrofit2.HttpException
import timber.log.Timber

class PlaylistViewModel(private val spotifyApi: SpotifyApi) : ViewModel() {
private val _selectPlaylistDto: MutableLiveData<Event<PlaylistDto>> = MutableLiveData()
val selectedPlaylistDto: LiveData<Event<PlaylistDto>> = _selectPlaylistDto

init {
viewModelScope.launch {
val userEither = spotifyApi.getCurrentUserEither()
Timber.d("User: $userEither")
}
}

val playlists: LiveData<List<PlaylistDto>> = liveData {
emit(emptyList())
emit(spotifyApi.getUsersPlaylists().items)
val playlists: LiveData<Either<Unit, List<PlaylistDto>>> = liveData {
emit(emptyList<PlaylistDto>().right())
emit(spotifyApi.getUsersPlaylists().map { it.items }.mapLeft { Unit })
}

fun playlistSelected(selectedPlaylistDto: PlaylistDto) {
Expand All @@ -37,16 +32,18 @@ class PlaylistViewModel(private val spotifyApi: SpotifyApi) : ViewModel() {
viewModelScope.launch {
// Need user id for the request for some reason.
// TODO Cache it somewhere so we dont fetch it when creating a new playlist
val userId = spotifyApi.getCurrentUser().id
val userId = spotifyApi.getCurrentUser().getOrElse { null }?.id ?: return@launch
val newPlaylist = PlaylistRequestDto(playlistName, "Playlist made by DJ App")
val createdPlaylist = try {
spotifyApi.createPlaylist(newPlaylist, userId)
} catch (e: HttpException) {
Timber.e(e, "Got an http exception trying to create playlist")
return@launch
}
_selectPlaylistDto.value =
Event(createdPlaylist)

val createPlaylistResponse = spotifyApi.createPlaylist(newPlaylist, userId)
createPlaylistResponse.fold(
ifLeft = {
Timber.e("Got error creating playlist $it")
},
ifRight = {
_selectPlaylistDto.value = Event(it)
}
)
}
}
}
35 changes: 20 additions & 15 deletions app/src/main/java/com/mfriend/djapp/spotifyapi/SpotifyApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import arrow.core.Either
import com.mfriend.djapp.spotifyapi.models.Pager
import com.mfriend.djapp.spotifyapi.models.PlaylistDto
import com.mfriend.djapp.spotifyapi.models.PlaylistRequestDto
import com.mfriend.djapp.spotifyapi.models.PlaylistSnapshot
import com.mfriend.djapp.spotifyapi.models.SpotifyErrorBody
import com.mfriend.djapp.spotifyapi.models.TrackDTO
import com.mfriend.djapp.spotifyapi.models.UserDto
Expand All @@ -13,10 +14,12 @@ import retrofit2.http.Headers
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
import retrofit2.http.Url

typealias SpotifyResponseEither<T> = Either<SpotifyErrorBody, T>

/**
* Interface for methods to interact with the spotify web api.
* Interface for methods to interact with the spotify web api. All requests in this interface will
* return an [Either.left] of [SpotifyErrorBody] to denote the error instead of throwing an exception
*
* Created by mfriend on 2020-01-05.
*/
Expand All @@ -25,46 +28,48 @@ interface SpotifyApi {
* Returns the currently authenticated [UserDto]
*/
@GET("me")
suspend fun getCurrentUser(): UserDto

@GET("me")
suspend fun getCurrentUserEither(): Either<SpotifyErrorBody, UserDto>
suspend fun getCurrentUser(): SpotifyResponseEither<UserDto>

/**
* Gets the playlists the currently authenticated user has on their account
*
* @return A [Pager] that contains a list of [PlaylistDto] in [Pager.items]
* @return A [Pager] that contains a list of [PlaylistDto] in [Pager.items] or [SpotifyErrorBody]
*/
@GET("me/playlists")
suspend fun getUsersPlaylists(): Pager<PlaylistDto>
suspend fun getUsersPlaylists(): SpotifyResponseEither<Pager<PlaylistDto>>

/**
* Creates a [PlaylistDto] for the currently authenticated [UserDto] from a given [PlaylistRequestDto] and returns it
*
* TODO cache userId so its not required
* @return Either a [PlaylistDto] representing the created playlist or [SpotifyErrorBody]
*/
@Headers("Content-Type: application/json", "Accept: application/json")
@POST("users/{user_id}/playlists")
suspend fun createPlaylist(
@Body playlist: PlaylistRequestDto,
@Path("user_id") userId: String
): PlaylistDto
): SpotifyResponseEither<PlaylistDto>

/**
* Adds song denoted by [songUri] to playlist denoted by [playlistId]
*
* @return [PlaylistSnapshot] that can be used to get a snapshot of the [PlaylistDto]
* represented by [playlistId] the moment after adding [songUri] or a [SpotifyErrorBody]
*/
@Headers("Content-Type: application/json", "Accept: application/json")
@POST("playlists/{playlist_id}/tracks")
suspend fun addSong(@Path("playlist_id") playlistId: String, @Query("uris") songUri: String)
suspend fun addSong(
@Path("playlist_id") playlistId: String,
@Query("uris") songUri: String
): SpotifyResponseEither<PlaylistSnapshot>

/**
* Gets a personalized list of the current users most listened to tracks
*
* @param limit max number of tracks to get in the response, default: 50
* @return Either a [Pager] of [TrackDTO] that contains the users top [limit] tracks and urls to
* get the next page or a [SpotifyErrorBody]
*/
@GET("https://api.spotify.com/v1/me/top/tracks")
suspend fun getUsersTopTracks(@Query("limit") limit: Int = 50): Pager<TrackDTO>

@GET
suspend fun getMoreTracks(@Url href: String): Pager<TrackDTO>
suspend fun getUsersTopTracks(@Query("limit") limit: Int = 50): SpotifyResponseEither<Pager<TrackDTO>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.mfriend.djapp.spotifyapi.models

import com.squareup.moshi.Json

data class PlaylistSnapshot(
@Json(name = "snapshot_id")
val snapshotId: String
)

0 comments on commit 72a1a05

Please sign in to comment.