My agentic slop goes here. Not intended for anyone else!
1(** JSON API Helpers for Requests *)
2
3(** {1 Response Helpers} *)
4
5let read_body response = Requests.Response.body response |> Eio.Flow.read_all
6
7let check_2xx response =
8 let status = Requests.Response.status_code response in
9 let body = read_body response in
10 if status >= 200 && status < 300 then Ok body
11 else Error (status, body)
12
13let check_ok response =
14 let status = Requests.Response.status_code response in
15 let body = read_body response in
16 if status = 200 then Ok body
17 else Error (status, body)
18
19(** {1 Request Helpers with Status Checking} *)
20
21let get_result session url =
22 try
23 let response = Requests.get session url in
24 check_2xx response
25 with exn ->
26 Error (0, Printexc.to_string exn)
27
28(** {1 JSON Helpers} *)
29
30let parse_json parser body_str =
31 Ezjsonm.from_string body_str |> parser
32
33let parse_json_result parser body_str =
34 try Ok (parse_json parser body_str)
35 with exn -> Error (Printexc.to_string exn)
36
37let get_json_exn session url parser =
38 let response = Requests.get session url in
39 let status = Requests.Response.status_code response in
40 if status < 200 || status >= 300 then
41 failwith (Printf.sprintf "HTTP %d" status);
42 read_body response |> parse_json parser
43
44let get_json session url parser =
45 match get_result session url with
46 | Ok body ->
47 (match parse_json_result parser body with
48 | Ok result -> Ok result
49 | Error msg -> Error (`Json_error msg))
50 | Error (status, body) -> Error (`Http (status, body))
51
52let post_json session url json_value =
53 let body_str = Ezjsonm.value_to_string json_value in
54 let body = Requests.Body.of_string Requests.Mime.json body_str in
55 Requests.post session url ~body
56
57let post_json_exn session url json_value =
58 let response = post_json session url json_value in
59 let status = Requests.Response.status_code response in
60 if status < 200 || status >= 300 then
61 failwith (Printf.sprintf "HTTP %d" status);
62 read_body response
63
64let post_json_result session url json_value =
65 try
66 let response = post_json session url json_value in
67 check_2xx response
68 with exn ->
69 Error (0, Printexc.to_string exn)
70
71(** {1 URL Helpers} *)
72
73let ( / ) base path =
74 (* Handle trailing slash in base and leading slash in path *)
75 let base = if String.ends_with ~suffix:"/" base then String.sub base 0 (String.length base - 1) else base in
76 let path = if String.starts_with ~prefix:"/" path then String.sub path 1 (String.length path - 1) else path in
77 base ^ "/" ^ path
78
79let make_url = ( / )
80
81(** {1 Let Syntax for Composition} *)
82
83module Syntax = struct
84 let ( let* ) x f = Result.bind x f
85 let ( let+ ) x f = Result.map f x
86 let ( and* ) a b = match a, b with
87 | Ok a, Ok b -> Ok (a, b)
88 | Error e, _ | _, Error e -> Error e
89 let ( and+ ) = ( and* )
90end
91
92(** {1 Convenience Combinators} *)
93
94let or_fail = function
95 | Ok x -> x
96 | Error (`Http (code, msg)) ->
97 failwith (Printf.sprintf "HTTP %d: %s" code msg)
98 | Error (`Json_error msg) ->
99 failwith (Printf.sprintf "JSON error: %s" msg)
100
101let or_fail_with prefix = function
102 | Ok x -> x
103 | Error (`Http (code, msg)) ->
104 failwith (Printf.sprintf "%s: HTTP %d: %s" prefix code msg)
105 | Error (`Json_error msg) ->
106 failwith (Printf.sprintf "%s: JSON error: %s" prefix msg)