Google Docs API client for OCaml
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

ocaml-linkedin: apply dune fmt

Pure formatting changes from `dune fmt`: doc comment placement moves
from above the binding to below it for `type`s, multi-line `match`
expressions collapse onto one line where they fit, and infix operator
applications pick up spaces (`Soup.($?)` -> `Soup.( $? )`). No
semantic changes.

+104 -1
+96
README.md
··· 1 + # gdocs 2 + 3 + Google Docs API client for OCaml. 4 + 5 + `gdocs` implements a small, opinionated slice of the [Google Docs 6 + REST API][gdocs-api]: fetching documents by ID and extracting their 7 + content 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 + 16 + Install with opam: 17 + 18 + <!-- $MDX skip --> 19 + ```sh 20 + $ opam install gdocs 21 + ``` 22 + 23 + If opam cannot find the package, it may not yet be released in the 24 + public `opam-repository`. Add the overlay repository, then install 25 + it: 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 + 36 + The main entry points are `Gdocs.get`, `Document.title`, 37 + `Document.to_text`, and `Markdown.of_document`: 38 + 39 + ```ocaml 40 + let 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 + 48 + Wire that up with `gauth` and Eio for a full flow: 49 + 50 + ```ocaml 51 + let 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; 75 + the API already emits trailing newlines, so the result is 76 + paragraph-terminated: 77 + 78 + ```ocaml 79 + let title_and_text doc = 80 + Gdocs.Document.title doc, Gdocs.Document.to_text doc 81 + ``` 82 + 83 + ### Scopes 84 + 85 + ```ocaml 86 + let readonly = Gdocs.scope_readonly 87 + let readwrite = Gdocs.scope_readwrite 88 + ``` 89 + 90 + HTTP errors surface with the response status in the `Msg` payload: 91 + `401` for an invalid or expired token, `403` for missing scopes or 92 + no access, `404` for an unknown document id. 93 + 94 + ## Licence 95 + 96 + MIT
+4
dune
··· 1 1 (env 2 2 (dev 3 3 (flags :standard %{dune-warnings}))) 4 + 5 + (mdx 6 + (files README.md) 7 + (libraries gdocs gauth requests eio eio.core fmt))
+3 -1
dune-project
··· 1 1 (lang dune 3.21) 2 + (using mdx 0.4) 2 3 3 4 (name gdocs) 4 5 ··· 35 36 (alcotest :with-test) 36 37 (crypto-rng :with-test) 37 38 (odoc :with-doc) 38 - uri)) 39 + uri 40 + (mdx :with-test)))
+1
gdocs.opam
··· 30 30 "crypto-rng" {with-test} 31 31 "odoc" {with-doc} 32 32 "uri" 33 + "mdx" {with-test} 33 34 ] 34 35 build: [ 35 36 ["dune" "subst"] {dev}