{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}-- | The Trajectory API, or a subset of it at least. This mirrors the-- underlying implementation, which ties stories to iterations.moduleTrajectory.API(getStories,moduleTrajectory.Types)whereimportData.DataimportData.AesonimportControl.Applicative((<$>),(<*>))importData.List(intercalate)importData.Attoparsec.ByteString.LazyimportqualifiedData.ByteString.Char8asBSimportqualifiedData.ByteString.Lazy.Char8asLBSimportqualifiedNetwork.HTTP.TypesasTypesimportNetwork.HTTP.EnumeratorimportText.URIimportqualifiedControl.ExceptionasEimportData.Maybe(fromMaybe)importTrajectory.Types-- | Get all the incomplete stories and iterations for a given user key,-- account name, and project name. Since stories and iterations are tied-- together in the underlying API, this produces them as a pair.---- It produces an IO of either an error or the stories/iterations pair. The-- error can come from the HTTP, or from non-JSON input, or from a change to-- the JSON.---- > do-- > possibleStories <- getStories "abcdefg" "thoughtbot" "opensource"-- > case possibleStories of-- > (Left error) -> putStrLn $ "got the error: " ++ show error-- > (Right (stories,iterations)) ->-- > putStrLn $ intercalate "\n" $-- > (map formatStory stories) ++ (map formatIteration iterations)getStories::String->String->String->IO(EitherError([Story],[Iteration]))getStorieskeyaccountNameprojectName=doleturl=buildUrl[key,"accounts",accountName,"projects",projectName,"stories.json"]result<-doHttps(BS.pack"GET")urlNothingreturn$either(Left.HTTPConnectionError)(extractStories.parseJson.responseBody)resultwhereextractStories::(EitherErrorStories)->(EitherError([Story],[Iteration]))extractStories(Leftl)=LeftlextractStories(Right(Storiesstoriesiterations))=Right(stories,iterations)dataStories=Stories[Story][Iteration]deriving(Show,Eq,Typeable,Data)instanceFromJSONStorywhereparseJSON(Objecto)=Story<$>o.:"archived"<*>o.:?"assignee_id"<*>o.:?"branch"<*>o.:"created_at"<*>o.:"deleted"<*>o.:"design_needed"<*>o.:"development_needed"<*>o.:"id"<*>o.:?"idea_id"<*>o.:"iteration_id"<*>o.:"points"<*>o.:"position"<*>o.:"state"<*>o.:"task_type"<*>o.:"title"<*>o.:"updated_at"<*>o.:"user_id"<*>o.:"comments_count"<*>o.:?"assignee_name"<*>o.:"user_name"<*>o.:"state_events"<*>o.:?"idea_subject"parseJSON_=fail"Could not build a Story"instanceFromJSONIterationwhereparseJSON(Objecto)=Iteration<$>o.:"accepted_points"<*>o.:"complete"<*>o.:"created_at"<*>o.:"estimated_points"<*>o.:"estimated_velocity"<*>o.:"id"<*>o.:"starts_on"<*>o.:"stories_count"<*>o.:"team_strength"<*>o.:"updated_at"<*>o.:"percent_complete"<*>o.:"current?"<*>o.:"unstarted_stories_count"<*>o.:"accepted_stories_count"<*>o.:"started_stories_count"<*>o.:"delivered_stories_count"<*>o.:"comments_count"parseJSON_=fail"Could not build an Iteration"instanceFromJSONStorieswhereparseJSON(Objecto)=Stories<$>o.:"stories"<*>o.:"iterations"parseJSON_=fail"Could not build Stories"buildUrl::[String]->StringbuildUrlpaths="https://www.apptrajectory.com/api/"++intercalate"/"pathsdoHttps::BS.ByteString->String->Maybe(RequestBodyIO)->IO(EitherE.IOExceptionResponse)doHttpsmethodurlbody=dolet(Justuri)=parseURIurl(Justhost)=uriRegNameurirequestBody=fromMaybe(RequestBodyBS$BS.pack"")bodyqueryString=Types.parseQuery$BS.pack$fromMaybe""$uriQueryurirequest=def{method=method,secure=True,host=BS.packhost,port=443,path=BS.pack$uriPathuri,requestBody=requestBody,queryString=queryString}(getResponserequest>>=return.Right)`catch`(return.Left)wheregetResponserequest=withManager$\manager->httpLbsrequestmanagerparseJson::(FromJSONb,Showb)=>LBS.ByteString->EitherErrorbparseJsonjsonString=letparsed=parse(fromJSON<$>json)jsonStringincaseparsedofData.Attoparsec.ByteString.Lazy.Done_jsonResult->docasejsonResultof(Successs)->Rights(Errore)->Left$JsonError$e++" on the JSON: "++LBS.unpackjsonString(Fail__e)->Left$ParseErrore