Google Docs API client for OCaml
1# gdocs
2
3Google Docs API client for OCaml.
4
5`gdocs` implements a small, opinionated slice of the [Google Docs
6REST API][gdocs-api]: fetching documents by ID and extracting their
7content as plain text or Markdown. Authentication is delegated to
8[`gauth`][gauth]; reading requires the
9`.../auth/documents.readonly` OAuth scope.
10
11[gdocs-api]: https://developers.google.com/docs/api/reference/rest
12[gauth]: https://tangled.org/gazagnaire.org/ocaml-gauth
13
14## Installation
15
16Install with opam:
17
18<!-- $MDX skip -->
19```sh
20$ opam install gdocs
21```
22
23If opam cannot find the package, it may not yet be released in the
24public `opam-repository`. Add the overlay repository, then install
25it:
26
27<!-- $MDX skip -->
28```sh
29$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
30$ opam update
31$ opam install gdocs
32```
33
34## Usage
35
36The main entry points are `Gdocs.get`, `Document.title`,
37`Document.to_text`, and `Markdown.of_document`:
38
39```ocaml
40let fetch_markdown http ~token ~document_id =
41 match Gdocs.get http ~token document_id with
42 | Error (`Msg m) -> Error m
43 | Ok doc ->
44 let body = Gdocs.Markdown.of_document doc in
45 Ok (Gdocs.Document.title doc, body)
46```
47
48Wire that up with `gauth` and Eio for a full flow:
49
50```ocaml
51let run env ~key_path ~document_id =
52 Eio.Switch.run @@ fun sw ->
53 let http = Requests.v ~sw env in
54 let clock = Eio.Stdenv.clock env in
55 match Gauth.Service_account.of_file key_path with
56 | Error (`Msg m) -> failwith m
57 | Ok key ->
58 match
59 Gauth.Service_account.token http ~clock
60 ~scopes:[ Gdocs.scope_readonly ] key
61 with
62 | Error (`Msg m) -> failwith m
63 | Ok token ->
64 match Gdocs.get http ~token document_id with
65 | Ok doc ->
66 Fmt.pr "# %s@.@.%s@."
67 (Gdocs.Document.title doc)
68 (Gdocs.Markdown.of_document doc)
69 | Error (`Msg m) -> Fmt.pr "fetch failed: %s@." m
70```
71
72### Plain text
73
74`Document.to_text` concatenates every `textRun` in paragraph order;
75the API already emits trailing newlines, so the result is
76paragraph-terminated:
77
78```ocaml
79let title_and_text doc =
80 Gdocs.Document.title doc, Gdocs.Document.to_text doc
81```
82
83### Scopes
84
85```ocaml
86let readonly = Gdocs.scope_readonly
87let readwrite = Gdocs.scope_readwrite
88```
89
90HTTP errors surface with the response status in the `Msg` payload:
91`401` for an invalid or expired token, `403` for missing scopes or
92no access, `404` for an unknown document id.
93
94## Licence
95
96MIT