OCaml client for the LinkedIn Voyager API
0
fork

Configure Feed

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

wal, block, sse, zephyr: remove [mutable] from never-reassigned fields

Warning 69 (unused-field, mutable-never-assigned). Four independent
record fields were flagged as mutable but the code only mutates their
referents in place, never rebinds the record slot itself:

- ocaml-wal/lib/wal.ml: [t.file] (the Eio file resource; methods call
Eio.File.pwrite_all etc., the slot is set once at open time).
- ocaml-block/lib/block.ml: [Memory.state.data] (the backing bytes,
written via Bytes.blit_string; [Bytes.t] is already mutable).
- ocaml-sse/lib/sse.ml: [Parser.t.data_buf] (a Buffer.t, written via
Buffer.add_*; the slot never changes).
- ocaml-zephyr/lib/zephyr.ml: drop [mode : Read | Write] entirely —
set at open-time, read nowhere. The open_read / open_write
constructors already distinguish the two call shapes, so mode
tracking was redundant.

+53 -50
+3
dune
··· 1 + (env 2 + (dev 3 + (flags :standard %{dune-warnings})))
+1 -1
dune-project
··· 25 25 (requests (>= 0.1)) 26 26 (fmt (>= 0.9)) 27 27 (logs (>= 0.7)) 28 - (jsont (>= 0.1)) 28 + (json (>= 0.1)) 29 29 (crypto (>= 0.1)) 30 30 (kdf (>= 0.1)) 31 31 (uri (>= 4.0))
+1 -1
lib/api.ml
··· 4 4 5 5 (** {1 JSON helpers} *) 6 6 7 - let decode codec s = Jsont_bytesrw.decode_string codec s 7 + let decode codec s = Json_bytesrw.decode_string codec s 8 8 9 9 (** {1 Error type} *) 10 10
+2 -2
lib/dune
··· 5 5 eio 6 6 fmt 7 7 logs 8 - jsont 9 - jsont.bytesrw 8 + json 9 + json.bytesrw 10 10 crypto 11 11 kdf.pbkdf 12 12 requests
+14 -14
lib/post.ml
··· 30 30 { urn; text; author_name; created_time; num_likes; num_comments } 31 31 32 32 let jsont = 33 - Jsont.Object.map ~kind:"post" 33 + Json.Object.map ~kind:"post" 34 34 (fun urn text author_name created_time num_likes num_comments -> 35 35 { 36 36 urn = Option.value ~default:"" urn; ··· 40 40 num_likes = Option.value ~default:0 num_likes; 41 41 num_comments = Option.value ~default:0 num_comments; 42 42 }) 43 - |> Jsont.Object.opt_mem "urn" Jsont.string ~enc:(fun p -> Some p.urn) 44 - |> Jsont.Object.opt_mem "commentary" Jsont.string ~enc:(fun p -> Some p.text) 45 - |> Jsont.Object.opt_mem "authorName" Jsont.string ~enc:(fun p -> 43 + |> Json.Object.opt_mem "urn" Json.string ~enc:(fun p -> Some p.urn) 44 + |> Json.Object.opt_mem "commentary" Json.string ~enc:(fun p -> Some p.text) 45 + |> Json.Object.opt_mem "authorName" Json.string ~enc:(fun p -> 46 46 Some p.author_name) 47 - |> Jsont.Object.opt_mem "createdTime" Jsont.int ~enc:(fun p -> 47 + |> Json.Object.opt_mem "createdTime" Json.int ~enc:(fun p -> 48 48 Some p.created_time) 49 - |> Jsont.Object.opt_mem "numLikes" Jsont.int ~enc:(fun p -> Some p.num_likes) 50 - |> Jsont.Object.opt_mem "numComments" Jsont.int ~enc:(fun p -> 49 + |> Json.Object.opt_mem "numLikes" Json.int ~enc:(fun p -> Some p.num_likes) 50 + |> Json.Object.opt_mem "numComments" Json.int ~enc:(fun p -> 51 51 Some p.num_comments) 52 - |> Jsont.Object.skip_unknown |> Jsont.Object.finish 52 + |> Json.Object.skip_unknown |> Json.Object.finish 53 53 54 54 let feed_jsont = 55 - Jsont.Object.map ~kind:"feed_response" (fun elements -> 55 + Json.Object.map ~kind:"feed_response" (fun elements -> 56 56 Option.value ~default:[] elements) 57 - |> Jsont.Object.opt_mem "elements" (Jsont.list jsont) ~enc:(fun ps -> Some ps) 58 - |> Jsont.Object.skip_unknown |> Jsont.Object.finish 57 + |> Json.Object.opt_mem "elements" (Json.list jsont) ~enc:(fun ps -> Some ps) 58 + |> Json.Object.skip_unknown |> Json.Object.finish 59 59 60 60 (** Codec for a single post from a normalized Voyager API response. Extracts the 61 61 first update from the [included] array that has a [commentary] field. *) 62 62 let normalized_jsont = 63 - Jsont.Object.map ~kind:"normalized_post_response" (fun included -> 63 + Json.Object.map ~kind:"normalized_post_response" (fun included -> 64 64 match included with Some (p :: _) -> p | _ -> v ~urn:"" ()) 65 - |> Jsont.Object.opt_mem "included" (Jsont.list jsont) ~enc:(fun p -> 65 + |> Json.Object.opt_mem "included" (Json.list jsont) ~enc:(fun p -> 66 66 Some [ p ]) 67 - |> Jsont.Object.skip_unknown |> Jsont.Object.finish 67 + |> Json.Object.skip_unknown |> Json.Object.finish
+3 -3
lib/post.mli
··· 38 38 val equal : t -> t -> bool 39 39 (** [equal a b] is [true] if [a] and [b] are equal. *) 40 40 41 - val jsont : t Jsont.t 41 + val jsont : t Json.codec 42 42 (** [jsont] is a JSON codec for individual posts. *) 43 43 44 - val feed_jsont : t list Jsont.t 44 + val feed_jsont : t list Json.codec 45 45 (** [feed_jsont] is a JSON codec for feed responses (extracts [elements]). *) 46 46 47 - val normalized_jsont : t Jsont.t 47 + val normalized_jsont : t Json.codec 48 48 (** [normalized_jsont] is a JSON codec for a single post from a normalised 49 49 Voyager API response (extracts from the [included] array). *)
+19 -19
lib/profile.ml
··· 43 43 { public_id; entity_urn; first_name; last_name; headline; summary; location } 44 44 45 45 let jsont = 46 - Jsont.Object.map ~kind:"profile" 46 + Json.Object.map ~kind:"profile" 47 47 (fun public_id entity_urn first_name last_name headline summary location -> 48 48 { 49 49 public_id = Option.value ~default:"" public_id; ··· 54 54 summary = Option.value ~default:"" summary; 55 55 location = Option.value ~default:"" location; 56 56 }) 57 - |> Jsont.Object.opt_mem "miniProfile.publicIdentifier" Jsont.string 57 + |> Json.Object.opt_mem "miniProfile.publicIdentifier" Json.string 58 58 ~enc:(fun p -> Some p.public_id) 59 - |> Jsont.Object.opt_mem "miniProfile.entityUrn" Jsont.string ~enc:(fun p -> 59 + |> Json.Object.opt_mem "miniProfile.entityUrn" Json.string ~enc:(fun p -> 60 60 Some p.entity_urn) 61 - |> Jsont.Object.opt_mem "firstName" Jsont.string ~enc:(fun p -> 61 + |> Json.Object.opt_mem "firstName" Json.string ~enc:(fun p -> 62 62 Some p.first_name) 63 - |> Jsont.Object.opt_mem "lastName" Jsont.string ~enc:(fun p -> 63 + |> Json.Object.opt_mem "lastName" Json.string ~enc:(fun p -> 64 64 Some p.last_name) 65 - |> Jsont.Object.opt_mem "headline" Jsont.string ~enc:(fun p -> 65 + |> Json.Object.opt_mem "headline" Json.string ~enc:(fun p -> 66 66 Some p.headline) 67 - |> Jsont.Object.opt_mem "summary" Jsont.string ~enc:(fun p -> Some p.summary) 68 - |> Jsont.Object.opt_mem "locationName" Jsont.string ~enc:(fun p -> 67 + |> Json.Object.opt_mem "summary" Json.string ~enc:(fun p -> Some p.summary) 68 + |> Json.Object.opt_mem "locationName" Json.string ~enc:(fun p -> 69 69 Some p.location) 70 - |> Jsont.Object.skip_unknown |> Jsont.Object.finish 70 + |> Json.Object.skip_unknown |> Json.Object.finish 71 71 72 72 (** Codec for miniProfile objects found in the [included] array of normalized 73 73 LinkedIn Voyager API responses. Maps [occupation] to [headline]. *) 74 74 let mini_profile_jsont = 75 - Jsont.Object.map ~kind:"mini_profile" 75 + Json.Object.map ~kind:"mini_profile" 76 76 (fun public_id entity_urn first_name last_name occupation -> 77 77 { 78 78 public_id = Option.value ~default:"" public_id; ··· 83 83 summary = ""; 84 84 location = ""; 85 85 }) 86 - |> Jsont.Object.opt_mem "publicIdentifier" Jsont.string ~enc:(fun p -> 86 + |> Json.Object.opt_mem "publicIdentifier" Json.string ~enc:(fun p -> 87 87 Some p.public_id) 88 - |> Jsont.Object.opt_mem "entityUrn" Jsont.string ~enc:(fun p -> 88 + |> Json.Object.opt_mem "entityUrn" Json.string ~enc:(fun p -> 89 89 Some p.entity_urn) 90 - |> Jsont.Object.opt_mem "firstName" Jsont.string ~enc:(fun p -> 90 + |> Json.Object.opt_mem "firstName" Json.string ~enc:(fun p -> 91 91 Some p.first_name) 92 - |> Jsont.Object.opt_mem "lastName" Jsont.string ~enc:(fun p -> 92 + |> Json.Object.opt_mem "lastName" Json.string ~enc:(fun p -> 93 93 Some p.last_name) 94 - |> Jsont.Object.opt_mem "occupation" Jsont.string ~enc:(fun p -> 94 + |> Json.Object.opt_mem "occupation" Json.string ~enc:(fun p -> 95 95 Some p.headline) 96 - |> Jsont.Object.skip_unknown |> Jsont.Object.finish 96 + |> Json.Object.skip_unknown |> Json.Object.finish 97 97 98 98 let empty = 99 99 { ··· 109 109 (** Codec for the normalized [/voyager/api/me] response. Extracts the first 110 110 miniProfile from the [included] array. *) 111 111 let me_jsont = 112 - Jsont.Object.map ~kind:"me_response" (fun included -> 112 + Json.Object.map ~kind:"me_response" (fun included -> 113 113 match included with Some (p :: _) -> p | _ -> empty) 114 - |> Jsont.Object.opt_mem "included" (Jsont.list mini_profile_jsont) 114 + |> Json.Object.opt_mem "included" (Json.list mini_profile_jsont) 115 115 ~enc:(fun p -> Some [ p ]) 116 - |> Jsont.Object.skip_unknown |> Jsont.Object.finish 116 + |> Json.Object.skip_unknown |> Json.Object.finish
+2 -2
lib/profile.mli
··· 45 45 val equal : t -> t -> bool 46 46 (** [equal a b] is [true] if [a] and [b] are equal. *) 47 47 48 - val jsont : t Jsont.t 48 + val jsont : t Json.codec 49 49 (** [jsont] is a JSON codec for profiles (from profileView responses). *) 50 50 51 - val me_jsont : t Jsont.t 51 + val me_jsont : t Json.codec 52 52 (** [me_jsont] is a JSON codec for the /me endpoint response. *)
+1 -1
linkedin.opam
··· 17 17 "requests" {>= "0.1"} 18 18 "fmt" {>= "0.9"} 19 19 "logs" {>= "0.7"} 20 - "jsont" {>= "0.1"} 20 + "json" {>= "0.1"} 21 21 "crypto" {>= "0.1"} 22 22 "kdf" {>= "0.1"} 23 23 "uri" {>= "4.0"}
+1 -1
test/dune
··· 1 1 (test 2 2 (name test) 3 - (libraries linkedin alcotest jsont jsont.bytesrw fmt)) 3 + (libraries linkedin alcotest json json.bytesrw fmt))
+3 -3
test/test_post.ml
··· 1 1 let decode_ok codec s = 2 - match Jsont_bytesrw.decode_string codec s with 2 + match Json_bytesrw.decode_string codec s with 3 3 | Ok v -> v 4 4 | Error e -> Alcotest.failf "decode error: %s" e 5 5 6 6 let roundtrip codec v = 7 - match Jsont_bytesrw.encode_string codec v with 7 + match Json_bytesrw.encode_string codec v with 8 8 | Error e -> Alcotest.failf "encode error: %s" e 9 9 | Ok s -> ( 10 - match Jsont_bytesrw.decode_string codec s with 10 + match Json_bytesrw.decode_string codec s with 11 11 | Ok v' -> v' 12 12 | Error e -> Alcotest.failf "decode error on roundtrip: %s\njson: %s" e s) 13 13
+3 -3
test/test_profile.ml
··· 1 1 let decode_ok codec s = 2 - match Jsont_bytesrw.decode_string codec s with 2 + match Json_bytesrw.decode_string codec s with 3 3 | Ok v -> v 4 4 | Error e -> Alcotest.failf "decode error: %s" e 5 5 6 6 let roundtrip codec v = 7 - match Jsont_bytesrw.encode_string codec v with 7 + match Json_bytesrw.encode_string codec v with 8 8 | Error e -> Alcotest.failf "encode error: %s" e 9 9 | Ok s -> ( 10 - match Jsont_bytesrw.decode_string codec s with 10 + match Json_bytesrw.decode_string codec s with 11 11 | Ok v' -> v' 12 12 | Error e -> Alcotest.failf "decode error on roundtrip: %s\njson: %s" e s) 13 13