-
Notifications
You must be signed in to change notification settings - Fork 0
Use cohttp and eio instead of curly #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
f20e522
926cd69
09f67f1
8cc5f0d
4500195
f65013c
4014d8d
5c43d98
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,9 @@ | ||
| ## unreleased | ||
|
|
||
| ### Changed | ||
|
|
||
| - Use cohttp and eio instead of curly (#<PR_NUMBER>, @gpetiot) | ||
|
|
||
| ## 2.0.1 | ||
|
|
||
| ### Fixed | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -97,19 +97,50 @@ let version = | |
|
|
||
| let info = Cmd.info "get-activity" ~version | ||
|
|
||
| module Client = struct | ||
| let null_auth ?ip:_ ~host:_ _ = Ok None | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We really should make a cohttp-eio-tls package with a proper authenticator. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can take a look at doing it this week.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, but I'm only going to work on this PR during Hacking Days, so no pressure! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've updated the example to use a proper authenticator now: mirage/ocaml-cohttp#1091 It's just: let authenticator =
match Ca_certs.authenticator () with
| Ok x -> x
| Error (`Msg m) -> Fmt.failwith "Failed to create system store X509 authenticator: %s" m |
||
|
|
||
| let https ~authenticator = | ||
| let tls_config = | ||
| match Tls.Config.client ~authenticator () with | ||
| | Error (`Msg msg) -> failwith ("tls configuration problem: " ^ msg) | ||
| | Ok tls_config -> tls_config | ||
| in | ||
| fun uri raw -> | ||
| let host = | ||
| Uri.host uri | ||
| |> Option.map (fun x -> Domain_name.(host_exn (of_string_exn x))) | ||
| in | ||
| Tls_eio.client_of_flow ?host tls_config raw | ||
|
|
||
| let make env = | ||
| Cohttp_eio.Client.make | ||
| ~https:(Some (https ~authenticator:null_auth)) | ||
| env#net | ||
| end | ||
|
|
||
| let run_eio f = | ||
| Eio_main.run @@ fun env -> | ||
| Mirage_crypto_rng_eio.run (module Mirage_crypto_rng.Fortuna) env @@ fun () -> | ||
| Eio.Switch.run @@ fun sw -> | ||
| let client = Client.make env in | ||
| f env sw client | ||
|
|
||
| let run () period user : unit = | ||
| match mode with | ||
| | `Normal -> | ||
| run_eio @@ fun _env sw client -> | ||
| Period.with_period period ~last_fetch_file ~f:(fun period -> | ||
| let* token = get_token () in | ||
| let request = Contributions.request ~period ~user ~token in | ||
| let* contributions = Graphql.exec request in | ||
| let* contributions = Graphql.Request.exec client sw request in | ||
| show ~period ~user contributions) | ||
| | `Save -> | ||
| run_eio @@ fun _env sw client -> | ||
| Period.with_period period ~last_fetch_file ~f:(fun period -> | ||
| let* token = get_token () in | ||
| let request = Contributions.request ~period ~user ~token in | ||
| let* contributions = Graphql.exec request in | ||
| let* contributions = Graphql.Request.exec client sw request in | ||
| Yojson.Safe.to_file "activity.json" contributions) | ||
| | `Load -> | ||
| (* When testing formatting changes, it is quicker to fetch the data once and then load it again for each test: *) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,7 +30,7 @@ | |
| (alcotest :with-test) | ||
| (ppx_expect :with-test) | ||
| astring | ||
| curly | ||
| cohttp-eio | ||
| (fmt | ||
| (>= 0.8.7)) | ||
| logs | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| (library | ||
| (name get_activity) | ||
| (public_name get-activity-lib) | ||
| (libraries astring curly fmt logs yojson) | ||
| (libraries astring cohttp-eio fmt logs yojson unix) | ||
| (preprocess | ||
| (pps ppx_yojson_conv))) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,47 +1,57 @@ | ||
| let ( let* ) = Result.bind | ||
| let ( / ) a b = Yojson.Safe.Util.member b a | ||
|
|
||
| type request = { | ||
| meth : Curly.Meth.t; | ||
| url : string; | ||
| headers : Curly.Header.t; | ||
| body : Yojson.Safe.t; | ||
| } | ||
| module Request = struct | ||
| type t = { request : Cohttp.Request.t; uri : Uri.t; body : Cohttp_eio.Body.t } | ||
|
|
||
| let request ?variables ~token ~query () = | ||
| let body = | ||
| `Assoc | ||
| (("query", `String query) | ||
| :: | ||
| (match variables with | ||
| | None -> [] | ||
| | Some v -> [ ("variables", `Assoc v) ])) | ||
| in | ||
| let url = "https://api.github.com/graphql" in | ||
| let headers = [ ("Authorization", "bearer " ^ token) ] in | ||
| { meth = `POST; url; headers; body } | ||
| let make ?variables ~token ~query () = | ||
| let body = | ||
| `Assoc | ||
| (("query", `String query) | ||
| :: | ||
| (match variables with | ||
| | None -> [] | ||
| | Some v -> [ ("variables", `Assoc v) ])) | ||
| |> Yojson.Safe.to_string |> Cohttp_eio.Body.of_string | ||
| in | ||
| let uri = Uri.of_string "https://api.github.com/graphql" in | ||
| let meth = `POST in | ||
| let headers = Cohttp.Header.init_with "Authorization" ("bearer " ^ token) in | ||
| let request = Cohttp.Request.make ~meth ~headers uri in | ||
| { request; uri; body } | ||
|
|
||
| let exec request = | ||
| let { meth; url; headers; body } = request in | ||
| let body = Yojson.Safe.to_string body in | ||
| let request = Curly.Request.make ~headers ~body ~url ~meth () in | ||
| Logs.debug (fun m -> m "request: @[%a@]@." Curly.Request.pp request); | ||
| match Curly.run request with | ||
| | Ok ({ Curly.Response.body; _ } as response) -> ( | ||
| Logs.debug (fun m -> m "response: @[%a@]@." Curly.Response.pp response); | ||
| let json = Yojson.Safe.from_string body in | ||
| match json / "message" with | ||
| | `Null -> Ok json | ||
| | `String e -> | ||
| Error (`Msg (Format.asprintf "@[<v2>GitHub returned errors: %s@]" e)) | ||
| | _errors -> | ||
| Error | ||
| (`Msg | ||
| (Format.asprintf "@[<v2>GitHub returned errors: %a@]" | ||
| (Yojson.Safe.pretty_print ~std:true) | ||
| json))) | ||
| | Error e -> | ||
| Error | ||
| (`Msg | ||
| (Format.asprintf | ||
| "@[<v2>Error performing GraphQL query on GitHub: %a@]" | ||
| Curly.Error.pp e)) | ||
| let exec client sw { request; body; uri } = | ||
| Logs.debug (fun m -> m "request: @[%a@]@." Cohttp.Request.pp_hum request); | ||
| let headers = request.headers in | ||
| let resp, body = Cohttp_eio.Client.post ~sw ~body ~headers client uri in | ||
| match resp.status with | ||
| | `OK -> ( | ||
| Logs.debug (fun m -> m "response: @[%a@]@." Http.Response.pp resp); | ||
| let* body = (Eio.Buf_read.(parse take_all) body) ~max_size:max_int in | ||
| let json = Yojson.Safe.from_string body in | ||
| match json / "message" with | ||
| | `Null -> Ok json | ||
| | `String e -> | ||
| Error | ||
| (`Msg (Format.asprintf "@[<v2>GitHub returned errors: %s@]" e)) | ||
| | _errors -> | ||
| Error | ||
| (`Msg | ||
| (Format.asprintf "@[<v2>GitHub returned errors: %a@]" | ||
| (Yojson.Safe.pretty_print ~std:true) | ||
| json))) | ||
| | status -> | ||
| Error | ||
| (`Msg | ||
| (Fmt.str | ||
| "@[<v2>Error performing GraphQL query on GitHub: Unexpected \ | ||
| HTTP status %a@]" | ||
| Http.Status.pp status)) | ||
|
|
||
| let pp ppf { request; uri = _; body = _ } = | ||
| let pp_request ppf r = | ||
| Fmt.pf ppf "@[<v>request =@;<1 2>@[<v2>%a@]@]" Cohttp.Request.pp_hum r | ||
| in | ||
| let pp_body ppf () = Fmt.pf ppf "@[<v>body =@;<1 2><...>@]" in | ||
| Fmt.pf ppf "@[<v2>{@ %a;@ %a@ }@]" pp_request request pp_body () | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,18 @@ | ||
| type request = { | ||
| meth : Curly.Meth.t; | ||
| url : string; | ||
| headers : Curly.Header.t; | ||
| body : Yojson.Safe.t; | ||
| } | ||
| module Request : sig | ||
| type t | ||
|
|
||
| val request : | ||
| ?variables:(string * Yojson.Safe.t) list -> | ||
| token:string -> | ||
| query:string -> | ||
| unit -> | ||
| request | ||
| val make : | ||
| ?variables:(string * Yojson.Safe.t) list -> | ||
| token:string -> | ||
| query:string -> | ||
| unit -> | ||
| t | ||
|
|
||
| val exec : request -> (Yojson.Safe.t, [ `Msg of string ]) result | ||
| val exec : | ||
| Cohttp_eio.Client.t -> | ||
| Eio.Switch.t -> | ||
gpetiot marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| t -> | ||
| (Yojson.Safe.t, [ `Msg of string ]) result | ||
|
|
||
| val pp : t Fmt.t | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,3 @@ | ||
| let () = | ||
| Alcotest.run "get-activity-lib" | ||
| [ | ||
| Test_token.suite; | ||
| Test_period.suite; | ||
| Test_graphql.suite; | ||
| Test_contributions.suite; | ||
| ] | ||
| [ Test_token.suite; Test_period.suite; Test_contributions.suite ] |
Uh oh!
There was an error while loading. Please reload this page.