1+ #r " packages/Hopac/lib/net45/Hopac.Core.dll"
2+ #r " packages/Hopac/lib/net45/Hopac.Platform.dll"
3+ #r " packages/Hopac/lib/net45/Hopac.dll"
4+ #r " packages/Hopac/lib/net45/Hopac.dll"
5+ #r " packages/FSharp.Data/lib/net45/FSharp.Data.dll"
6+ #r " packages/Http.fs/lib/net461/HttpFs.dll"
7+ #r " packages/System.Net.Http/lib/net46/System.Net.Http.dll"
8+
9+ open Hopac
10+ open FSharp.Data
11+ open HttpFs.Client
12+ open System
13+
14+ let httpGet url = job {
15+ let request =
16+ Request.createUrl Get url
17+ |> Request.setHeader ( UserAgent " FsHopac" )
18+ let! getResponseResult =
19+ getResponse request |> Job.catch
20+ match getResponseResult with
21+ | Choice1Of2 response ->
22+ match response.statusCode with
23+ | 200 ->
24+ let! body = Response.readBodyAsString response
25+ return Ok body
26+ | _ -> return Error ( Exception( " invalid response for " + url))
27+ | Choice2Of2 ex -> return Error ex
28+ }
29+
30+
31+ type GitHubUser = JsonProvider< " https://api.github.com/users/tamizhvendan" >
32+
33+ type Profile = {
34+ Name : string
35+ AvatarUrl : string
36+ }
37+
38+ let profile ( gitHubUser : GitHubUser.Root ) = {
39+ Name = gitHubUser.Name
40+ AvatarUrl = gitHubUser.AvatarUrl
41+ }
42+
43+
44+ let host = " https://api.github.com"
45+ let userUrl = sprintf " %s /users/%s " host
46+
47+ let getGitHubProfile username = job {
48+ let! response = username |> userUrl |> httpGet
49+ let user =
50+ response
51+ |> Result.map GitHubUser.Parse
52+ |> Result.map profile
53+ return user
54+ }
55+
56+
57+ type GitHubRepos = JsonProvider< " https://api.github.com/users/tamizhvendan/repos" >
58+
59+ type UserRepo = {
60+ Name : string
61+ StargazersCount : int
62+ }
63+ let userRepo ( repo : GitHubRepos.Root ) = {
64+ Name = repo.Name
65+ StargazersCount = repo.StargazersCount
66+ }
67+
68+ let isOwnRepo ( repo : GitHubRepos.Root ) = not repo.Fork
69+
70+ let topThreeUserRepos ( repos : GitHubRepos.Root []) =
71+ let takeCount =
72+ let reposCount = Array.length repos
73+ if reposCount > 3 then 3 else reposCount
74+ repos
75+ |> Array.filter isOwnRepo
76+ |> Array.map userRepo
77+ |> Array.sortByDescending ( fun repo -> repo.StargazersCount)
78+ |> Array.take takeCount
79+
80+ let reposUrl = sprintf " %s /users/%s /repos" host
81+
82+ let getTopThreeUserRepos username = job {
83+ let! response = username |> reposUrl |> httpGet
84+ let topThreeUserRepos =
85+ response
86+ |> Result.map GitHubRepos.Parse
87+ |> Result.map topThreeUserRepos
88+ return topThreeUserRepos
89+ }
90+
91+ let languagesUrl repoName userName =
92+ sprintf " %s /repos/%s /%s /languages" host userName repoName
93+
94+ let parseLanguagesJson languagesJson =
95+ languagesJson
96+ |> JsonValue.Parse
97+ |> JsonExtensions.Properties
98+ |> Array.map fst
99+
100+ let getUserRepoLanguages repoName username = job {
101+ let! response = languagesUrl repoName username |> httpGet
102+ let languages =
103+ response
104+ |> Result.map parseLanguagesJson
105+ return languages
106+ }
107+
108+ type UserRepoDto = {
109+ Name : string
110+ StargazersCount : int
111+ Languages : string []
112+ }
113+ let userRepoDto ( userRepo : UserRepo ) languagesResult =
114+ languagesResult
115+ |> Result.map ( fun languages -> {
116+ Name = userRepo.Name
117+ StargazersCount = userRepo.StargazersCount
118+ Languages = languages
119+ })
120+
121+ type ProfileDto = {
122+ Name : string
123+ AvatarUrl : string
124+ TopThreeRepos : UserRepoDto list
125+ }
126+
127+ type ResultBuilder () =
128+ member __.Bind ( r , binder ) = Result.bind binder r
129+ member __.Return ( value ) = Ok value
130+
131+ let result = ResultBuilder()
132+
133+ let rec transform ( results : Result < 'a , Exception > list ) : Result < 'a list , Exception > =
134+ let values =
135+ results
136+ |> List.choose ( function | Ok v -> Some v | _ -> None)
137+ if values.Length = results.Length then
138+ Ok values
139+ else
140+ let ex =
141+ results
142+ |> List.choose ( function | Error ex -> Some ex | _ -> None)
143+ |> AggregateException
144+ Error ( ex :> Exception)
145+
146+
147+ let profileDto ( profileResult : Result < Profile , Exception >) userRepoDtosResults = result {
148+ let! profile = profileResult
149+ let! userRepoDtos =
150+ transform userRepoDtosResults
151+ let profileDto = {
152+ Name = profile.Name
153+ AvatarUrl = profile.AvatarUrl
154+ TopThreeRepos = userRepoDtos
155+ }
156+ return profileDto
157+ }
158+
159+
160+ let getUserRepoLanguagesJobs username ( repos : UserRepo []) =
161+ repos
162+ |> Array.map ( fun repo ->
163+ getUserRepoLanguages repo.Name username
164+ |> Job.map ( userRepoDto repo))
165+ |> Job.conCollect
166+ |> Job.map ( fun x -> x.ToArray() |> Array.toList)
167+
168+ open Hopac.Infixes
169+ let getProfileDto username = job {
170+ let! profile , topThreeUserRepos =
171+ getGitHubProfile username <*>
172+ getTopThreeUserRepos username
173+ let userRepoDtosJobResult =
174+ topThreeUserRepos
175+ |> Result.map ( getUserRepoLanguagesJobs username)
176+ match userRepoDtosJobResult with
177+ | Ok userRepoDtosJob ->
178+ let! userRepoDtos = userRepoDtosJob
179+ return profileDto profile userRepoDtos
180+ | Error ex -> return Error ex
181+ }
182+
183+ getProfileDto " tamizhvendan" |> run
184+ getProfileDto " demystifyfp" |> run
0 commit comments