lanonna/pam/bin/httpclient.ml
2024-05-01 12:03:26 +02:00

104 lines
3.4 KiB
OCaml

open Pyops
open Pytypes
open Batteries
open Yojson.Safe
open Config
open Utils
open Pam
let issue_of_json (m_room: Datatypes.MatrixRoom.t) (json): Datatypes.forgejo_issue_data =
let open Yojson.Safe.Util in
let due_date = json |> member "due_date" |> to_option to_string in
let record: Datatypes.forgejo_issue_data = {
url = json |> member "url" |> to_string;
title = json |> member "title" |> to_string;
body = json |> member "body" |> to_string;
matrix_target = m_room;
due_date = due_date
}
in record
let issues_of_json matrix_room json_str =
let open Yojson.Safe.Util in
try
json_str |> from_string |> to_list |> List.map (issue_of_json matrix_room) |> Result.ok
with
| Yojson.Json_error msg -> Error [%string "JSON parsing error: %{msg}"]
module ForgejoUrl = struct
type t = {url: string; page: int}
let from_id repo_id =
{ url = [%string "https://salsa.lezzo.org/api/v1/repos/%{repo_id}/issues?state=open&type=issues&limit=50"];
page = 0 }
let next_page t = {t with page=t.page+1}
let to_string t = [%string "%{t.url}&page=%{t.page#Int}"]
end
type repo_pytuple = {url: ForgejoUrl.t; headers: pyobject}
type http_actor = {requests: pyobject; repos: repo_pytuple StringMap.t}
let make_headers base64_password =
let headers =
[("accept", "application/json");
("authorization", [%string "Basic %{base64_password}"])] in
headers
|> List.map (fun (k, v) -> (k, Py.String.of_string v))
|> Py.Dict.of_bindings_string
let init (repos: Config.repo_data list) =
let _ = Py.initialize () in
let requests = Py.import "requests" in
let urls =
repos
|> List.map (fun {forgejo_id; base64_password; matrix_room} ->
(matrix_room, {url=ForgejoUrl.from_id forgejo_id; headers=make_headers base64_password}))
|> StringMap.of_list
in
{requests=requests; repos=urls}
let pyprint () =
let builtins = Py.Eval.get_builtins () in
let p = Py.Dict.find_string builtins "print" in
Py.Callable.to_function p
let extract_pagination resp =
let key = Py.String.of_string "X-Total-Count" in
resp
.@$("headers")
.&("get") [|key|] (* not really a pydict, so need to use `get` *)
|> Py.Object.to_string
|> int_of_string_opt
let make_get_request {requests; repos} =
let get = Py.Module.get_function_with_keywords requests "get" in
let rec fold_fn (accum: (Datatypes.forgejo_issue_data list)) = function
| [] -> Ok accum
| (m_room_string, {url; headers})::rest ->
let pyurl = url |> ForgejoUrl.to_string |> Py.String.of_string in
let resp = get [|pyurl|] [("headers", headers)] in
let jsontext =
resp.@$("text")
|> Py.String.to_string
in
let target_items_total = extract_pagination resp in
let matrix_room = Datatypes.MatrixRoom.make m_room_string in
let issues = issues_of_json matrix_room jsontext in
match target_items_total, issues with
| None, _ -> Error "Can't extract pagination"
| _, Error err ->
Error err
| Some target_items_total, Ok target_issues ->
let n_received = List.length target_issues in
let accum' = target_issues@accum in
let urls =
if n_received <> target_items_total then
(m_room_string, {url=ForgejoUrl.next_page url; headers=headers})::rest
else
rest
in fold_fn accum' urls
in
StringMap.to_list repos
|> fold_fn []