-- | Functions for performing actions against the API. module Strive.Actions where import Data.Aeson (FromJSON, Value, encode) import Data.ByteString (ByteString) import Data.ByteString.Char8 (unpack) import Data.ByteString.Lazy (toStrict) import Data.Default (Default, def) import Data.List (intercalate) import Data.Monoid ((<>)) import Data.Time.Clock (UTCTime) import Network.HTTP.Conduit (RequestBody (RequestBodyBS), requestBody, responseBody, responseStatus) import Network.HTTP.Types (Query, methodDelete, methodPost, noContent204, renderQuery, toQuery) import Strive.Client (Client, buildClient) import Strive.Internal.HTTP (buildRequest, decodeValue, delete, get, performRequest, post, put) import qualified Strive.Options as O import qualified Strive.Types as T -- | Helper function for easily performing actions. with :: Default a => [a -> a] -> a with = foldr ($) def -- | Infix alias of 'with'. infixr 0 ? (?) :: Default a => (a -> b) -> [a -> a] -> b (?) = (. with) -- * Authentication -- | buildAuthorizeUrl :: Integer -> String -> O.BuildAuthorizeUrlOptions -> String buildAuthorizeUrl clientId redirectUri options = "https://www.strava.com/oauth/authorize" <> unpack (renderQuery True query) where query = toQuery [ ("client_id", show clientId) , ("redirect_uri", redirectUri) , ("response_type", "code") ] <> toQuery options -- | exchangeToken :: Integer -> String -> String -> IO (Either String T.TokenExchangeResponse) exchangeToken clientId clientSecret code = do client <- buildClient "" post client resource query where resource = "oauth/token" query = [ ("client_id", show clientId) , ("client_secret", clientSecret) , ("code", code) ] -- | deauthorize :: Client -> IO (Either String T.DeauthorizationResponse) deauthorize client = post client resource query where resource = "oauth/deauthorize" query = [] :: Query -- * Athletes -- | getCurrentAthlete :: Client -> IO (Either String T.AthleteDetailed) getCurrentAthlete client = get client resource query where resource = "api/v3/athlete" query = [] :: Query -- | getAthlete :: Client -> Integer -> IO (Either String T.AthleteSummary) getAthlete client athleteId = get client resource query where resource = "api/v3/athletes/" <> show athleteId query = [] :: Query -- | updateCurrentAthlete :: Client -> O.UpdateCurrentAthleteOptions -> IO (Either String T.AthleteDetailed) updateCurrentAthlete client options = put client resource query where resource = "api/v3/athlete" query = toQuery options -- | getAthleteCrs :: Client -> Integer -> O.GetAthleteCrsOptions -> IO (Either String [T.EffortDetailed]) getAthleteCrs client athleteId options = get client resource query where resource = "api/v3/athletes/" <> show athleteId <> "/koms" query = toQuery options -- * Friends and Followers -- | getCurrentFriends :: Client -> O.GetCurrentFriendsOptions -> IO (Either String [T.AthleteSummary]) getCurrentFriends client options = get client resource query where resource = "api/v3/athlete/friends" query = toQuery options -- | getFriends :: Client -> Integer -> O.GetFriendsOptions -> IO (Either String [T.AthleteSummary]) getFriends client athleteId options = get client resource query where resource = "api/v3/athletes/" <> show athleteId <> "/friends" query = toQuery options -- | getCurrentFollowers :: Client -> O.GetCurrentFollowersOptions -> IO (Either String [T.AthleteSummary]) getCurrentFollowers client options = get client resource query where resource = "api/v3/athlete/followers" query = toQuery options -- | getFollowers :: Client -> Integer -> O.GetFollowersOptions -> IO (Either String [T.AthleteSummary]) getFollowers client athleteId options = get client resource query where resource = "api/v3/athletes/" <> show athleteId <> "/followers" query = toQuery options -- | getCommonFriends :: Client -> Integer -> O.GetCommonFriendsOptions -> IO (Either String [T.AthleteSummary]) getCommonFriends client athleteId options = get client resource query where resource = "api/v3/athletes/" <> show athleteId <> "/both-following" query = toQuery options -- * Activities -- | createActivity :: Client -> String -> String -> UTCTime -> Integer -> O.CreateActivityOptions -> IO (Either String T.ActivityDetailed) createActivity client name type_ startDateLocal elapsedTime options = post client resource query where resource = "api/v3/activities" query = toQuery [ ("name", name) , ("type", type_) , ("start_date_local", unpack (toStrict (encode startDateLocal))) , ("elapsed_time", show elapsedTime) ] <> toQuery options -- | getActivity :: Client -> Integer -> O.GetActivityOptions -> IO (Either String T.ActivitySummary) getActivity client activityId options = get client resource query where resource = "api/v3/activities/" <> show activityId query = toQuery options -- | updateActivity :: Client -> Integer -> O.UpdateActivityOptions -> IO (Either String T.ActivityDetailed) updateActivity client activityId options = put client resource query where resource = "api/v3/activities/" <> show activityId query = toQuery options -- | deleteActivity :: Client -> Integer -> IO (Either String ()) deleteActivity client activityId = do request <- buildRequest methodDelete client resource query response <- performRequest client request return (if responseStatus response == noContent204 then Right () else Left (unpack (toStrict (responseBody response)))) where resource = "api/v3/activities/" <> show activityId query = [] :: Query -- | getCurrentActivities :: Client -> O.GetCurrentActivitiesOptions -> IO (Either String [T.ActivitySummary]) getCurrentActivities client options = get client resource query where resource = "api/v3/athlete/activities" query = toQuery options -- | getFeed :: Client -> O.GetFeedOptions -> IO (Either String [T.ActivitySummary]) getFeed client options = get client resource query where resource = "api/v3/activities/following" query = toQuery options -- | getActivityZones :: Client -> Integer -> IO (Either String [T.ActivityZoneDetailed]) getActivityZones client activityId = get client resource query where resource = "api/v3/activities/" <> show activityId <> "/zones" query = [] :: Query -- | getActivityLaps :: Client -> Integer -> IO (Either String [T.ActivityLapSummary]) getActivityLaps client activityId = get client resource query where resource = "api/v3/activities/" <> show activityId <> "/laps" query = [] :: Query -- * Comments -- | getActivityComments :: Client -> Integer -> O.GetActivityCommentsOptions -> IO (Either String [T.CommentSummary]) getActivityComments client activityId options = get client resource query where resource = "api/v3/activities/" <> show activityId <> "/comments" query = toQuery options -- * Kudos -- | getActivityKudoers :: Client -> Integer -> O.GetActivityKudoersOptions -> IO (Either String [T.AthleteSummary]) getActivityKudoers client activityId options = get client resource query where resource = "api/v3/activities/" <> show activityId <> "/kudos" query = toQuery options -- * Photos -- | getActivityPhotos :: Client -> Integer -> IO (Either String [T.PhotoSummary]) getActivityPhotos client activityId = get client resource query where resource = "api/v3/activities/" <> show activityId <> "/photos" query = [] :: Query -- * Clubs -- | getClub :: Client -> Integer -> IO (Either String T.ClubDetailed) getClub client clubId = get client resource query where resource = "api/v3/clubs/" <> show clubId query = [] :: Query -- | getCurrentClubs :: Client -> IO (Either String [T.ClubSummary]) getCurrentClubs client = get client resource query where resource = "api/v3/athlete/clubs" query = [] :: Query -- | getClubMembers :: Client -> Integer -> O.GetClubMembersOptions -> IO (Either String [T.AthleteSummary]) getClubMembers client clubId options = get client resource query where resource = "api/v3/clubs/" <> show clubId <> "/members" query = toQuery options -- | getClubActivities :: Client -> Integer -> O.GetClubActivitiesOptions -> IO (Either String [T.ActivitySummary]) getClubActivities client clubId options = get client resource query where resource = "api/v3/clubs/" <> show clubId <> "/activities" query = toQuery options -- * Gear -- | getGear :: Client -> String -> IO (Either String T.GearDetailed) getGear client gearId = get client resource query where resource = "api/v3/gear/" <> gearId query = [] :: Query -- * Segments -- | getSegment :: Client -> Integer -> IO (Either String T.SegmentDetailed) getSegment client segmentId = get client resource query where resource = "api/v3/segments/" <> show segmentId query = [] :: Query -- | getStarredSegments :: Client -> O.GetStarredSegmentsOptions -> IO (Either String [T.SegmentSummary]) getStarredSegments client options = get client resource query where resource = "api/v3/segments/starred" query = toQuery options -- | getSegmentEfforts :: Client -> Integer -> O.GetSegmentEffortsOptions -> IO (Either String [T.EffortDetailed]) getSegmentEfforts client segmentId options = get client resource query where resource = "api/v3/segments/" <> show segmentId <> "/all_efforts" query = toQuery options -- | getSegmentLeaderboard :: Client -> Integer -> O.GetSegmentLeaderboardOptions -> IO (Either String T.SegmentLeaderboardResponse) getSegmentLeaderboard client segmentId options = get client resource query where resource = "api/v3/segments/" <> show segmentId <> "/leaderboard" query = toQuery options -- | exploreSegments :: Client -> (Double, Double, Double, Double) -> O.ExploreSegmentsOptions -> IO (Either String T.SegmentExplorerResponse) exploreSegments client (south, west, north, east) options = get client resource query where resource = "api/v3/segments/explore" query = toQuery [ ("bounds", intercalate "," (fmap show [south, west, north, east])) ] <> toQuery options -- * Segment Efforts -- | getSegmentEffort :: Client -> Integer -> IO (Either String T.EffortDetailed) getSegmentEffort client effortId = get client resource query where resource = "api/v3/segment_efforts/" <> show effortId query = [] :: Query -- * Streams -- | getActivityStreams :: Client -> Integer -> [String] -> O.GetStreamsOptions -> IO (Either String [T.StreamDetailed]) getActivityStreams = flip getStreams "activities" -- | getEffortStreams :: Client -> Integer -> [String] -> O.GetStreamsOptions -> IO (Either String [T.StreamDetailed]) getEffortStreams = flip getStreams "segment_efforts" -- | getSegmentStreams :: Client -> Integer -> [String] -> O.GetStreamsOptions -> IO (Either String [T.StreamDetailed]) getSegmentStreams = flip getStreams "segments" getStreams :: FromJSON a => Client -> String -> Integer -> [String] -> O.GetStreamsOptions -> IO (Either String a) getStreams client kind id types options = get client resource query where resource = concat [ "api/v3/" , kind , "/" , show id , "/streams/" , intercalate "," types ] query = toQuery options -- * Uploads -- | uploadActivity :: Client -> ByteString -> String -> O.UploadActivityOptions -> IO (Either String T.UploadStatus) uploadActivity client body dataType options = do initialRequest <- buildRequest methodPost client resource query let request = initialRequest { requestBody = RequestBodyBS body } response <- performRequest client request return (decodeValue response) where resource = "api/v3/uploads" query = toQuery [ ("data_type", dataType) ] <> toQuery options -- | getUpload :: Client -> Integer -> IO (Either String T.UploadStatus) getUpload client uploadId = get client resource query where resource = "api/v3/uploads/" <> show uploadId query = [] :: Query