package polymarket
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
OCaml client library for the Polymarket prediction market API
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.2.0.tar.gz
md5=4eb4c5d2f63ff081c9713d90be5a51b2
sha512=0e3de0c9b40683e09ab8f9f966a44784ef1b9b482c3eefef84104a7e8042c92f1d79893ee9588b24fa3d0decaed7f365509f4d1c23c66ce8328efb64e721f276
doc/src/polymarket.http/client.ml.html
Source file client.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247(** Generic HTTP client for Polymarket APIs. This module provides a reusable HTTP client. Uses cohttp-eio for HTTP requests. *) open Ppx_yojson_conv_lib.Yojson_conv.Primitives module R = Polymarket_rate_limiter.Rate_limiter let src = Logs.Src.create "polymarket.http" ~doc:"HTTP client" module Log = (val Logs.src_log src : Logs.LOG) (** {1 Client Configuration} *) type t = { base_url : string; sw : Eio.Switch.t; rate_limiter : R.t; client : Cohttp_eio.Client.t; } let make_https_handler _net = let authenticator = match Ca_certs.authenticator () with | Ok auth -> auth | Error (`Msg msg) -> failwith ("CA certs error: " ^ msg) in let tls_config = match Tls.Config.client ~authenticator () with | Ok cfg -> cfg | Error (`Msg msg) -> failwith ("TLS config error: " ^ msg) in fun uri socket -> let host = (* Extract host for SNI *) match Uri.host uri with | Some h -> h | None -> "localhost" in Tls_eio.client_of_flow tls_config ~host:(Domain_name.of_string_exn host |> Domain_name.host_exn) socket let create ~base_url ~sw ~net ~rate_limiter () = let client = Cohttp_eio.Client.make ~https:(Some (make_https_handler net)) net in { base_url; sw; rate_limiter; client } let base_url t = t.base_url (** {1 HTTP Request Functions} *) type params = (string * string list) list let build_uri base_url path params = let uri = Uri.of_string (base_url ^ path) in Uri.add_query_params uri params type status_code = int let apply_rate_limit t ~method_ ~uri = R.before_request t.rate_limiter ~method_ ~uri (** Helper to convert header list to Http.Header.t *) let make_headers headers = Http.Header.of_list headers (** Helper to read body string from cohttp response *) let body_to_string body = Eio.Buf_read.(parse_exn take_all) body ~max_size:max_int let log_request ~method_ uri = Log.debug (fun m -> m "%s %s" method_ (Uri.to_string uri)) let log_response ~method_ uri status = Log.debug (fun m -> m "%s %s -> %d" method_ (Uri.to_string uri) status) let log_error ~method_ uri error = Log.err (fun m -> m "%s %s failed: %s" method_ (Uri.to_string uri) error) let do_get ?(headers = []) t uri = apply_rate_limit t ~method_:"GET" ~uri; let headers = make_headers headers in log_request ~method_:"GET" uri; try let resp, body = Cohttp_eio.Client.get ~sw:t.sw ~headers t.client uri in let status = Http.Response.status resp |> Http.Status.to_int in let body_str = body_to_string body in log_response ~method_:"GET" uri status; (status, body_str) with exn -> let msg = Printexc.to_string exn in log_error ~method_:"GET" uri msg; (500, Printf.sprintf {|{"error": "Request failed: %s"}|} msg) let do_post ?(headers = []) t uri ~body:request_body = apply_rate_limit t ~method_:"POST" ~uri; let headers = make_headers (("Content-Type", "application/json") :: headers) in let body = Cohttp_eio.Body.of_string request_body in log_request ~method_:"POST" uri; try let resp, resp_body = Cohttp_eio.Client.post ~sw:t.sw ~headers ~body t.client uri in let status = Http.Response.status resp |> Http.Status.to_int in let body_str = body_to_string resp_body in log_response ~method_:"POST" uri status; (status, body_str) with exn -> let msg = Printexc.to_string exn in log_error ~method_:"POST" uri msg; (500, Printf.sprintf {|{"error": "Request failed: %s"}|} msg) let do_delete ?(headers = []) t uri = apply_rate_limit t ~method_:"DELETE" ~uri; let headers = make_headers headers in log_request ~method_:"DELETE" uri; try let resp, body = Cohttp_eio.Client.delete ~sw:t.sw ~headers t.client uri in let status = Http.Response.status resp |> Http.Status.to_int in let body_str = body_to_string body in log_response ~method_:"DELETE" uri status; (status, body_str) with exn -> let msg = Printexc.to_string exn in log_error ~method_:"DELETE" uri msg; (500, Printf.sprintf {|{"error": "Request failed: %s"}|} msg) let do_delete_with_body ?(headers = []) t uri ~body:request_body = apply_rate_limit t ~method_:"DELETE" ~uri; let headers = make_headers (("Content-Type", "application/json") :: headers) in let body = Cohttp_eio.Body.of_string request_body in log_request ~method_:"DELETE" uri; try let resp, resp_body = Cohttp_eio.Client.delete ~sw:t.sw ~headers ~body t.client uri in let status = Http.Response.status resp |> Http.Status.to_int in let body_str = body_to_string resp_body in log_response ~method_:"DELETE" uri status; (status, body_str) with exn -> let msg = Printexc.to_string exn in log_error ~method_:"DELETE" uri msg; (500, Printf.sprintf {|{"error": "Request failed: %s"}|} msg) (** {1 Error Handling} *) type http_error = { status : int; body : string; message : string } type parse_error = { context : string; message : string } type network_error = { message : string } type error = | Http_error of http_error | Parse_error of parse_error | Network_error of network_error let error_to_string = function | Http_error { status; message; _ } -> Printf.sprintf "HTTP %d: %s" status message | Parse_error { context; message } -> Printf.sprintf "Parse error in %s: %s" context message | Network_error { message } -> Printf.sprintf "Network error: %s" message let pp_error fmt e = Format.fprintf fmt "%s" (error_to_string e) type error_response = { error : string } [@@deriving yojson] (** Type for parsing JSON error responses from APIs. *) let to_error msg = Parse_error { context = "json"; message = msg } let parse_error ~status body = let message = (* Check for HTML error pages (from proxies like Cloudflare) *) if String.length body > 0 && body.[0] = '<' then match status with | 502 -> "Bad Gateway" | 503 -> "Service Unavailable" | 504 -> "Gateway Timeout" | 429 -> "Too Many Requests" | _ -> Printf.sprintf "HTTP error (status %d)" status else try let json = Yojson.Safe.from_string body in match json with | `Assoc fields -> ( match List.assoc_opt "error" fields with | Some (`String msg) -> msg | _ -> body) | _ -> body with _ -> body in Http_error { status; body; message } (** {1 Response Handling} *) let handle_response status body parse_fn = match status with | 200 -> parse_fn body | _ -> Error (parse_error ~status body) (** {1 JSON Field Checking} *) let check_extra_fields ~expected_fields ~context json = match json with | `Assoc fields -> let extra_fields = List.filter (fun (name, _) -> not (List.mem name expected_fields)) fields in if extra_fields <> [] then let format_field (name, value) = Printf.sprintf "%s: %s" name (Yojson.Safe.to_string value) in let formatted = List.map format_field extra_fields |> String.concat ", " in Log.warn (fun m -> m "Extra fields in %s: %s" context formatted) | _ -> () let parse_with_field_check ~expected_fields ~context body of_yojson = try let json = Yojson.Safe.from_string body in check_extra_fields ~expected_fields ~context json; Ok (of_yojson json) with | Ppx_yojson_conv_lib.Yojson_conv.Of_yojson_error (exn, _) -> Error (to_error (Printexc.to_string exn)) | exn -> Error (to_error (Printexc.to_string exn)) let parse_list_with_field_check ~expected_fields ~context body of_yojson = try let json = Yojson.Safe.from_string body in (match json with | `List items -> List.iter (check_extra_fields ~expected_fields ~context) items | _ -> ()); Ok (of_yojson json) with | Ppx_yojson_conv_lib.Yojson_conv.Of_yojson_error (exn, _) -> Error (to_error (Printexc.to_string exn)) | exn -> Error (to_error (Printexc.to_string exn))
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>