diff --git a/Cargo.lock b/Cargo.lock index 932052b..7fdd35c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "0.7.20" @@ -71,9 +86,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "async-attributes" @@ -151,7 +166,7 @@ dependencies = [ "polling", "rustix", "slab", - "socket2", + "socket2 0.4.9", "waker-fn", ] @@ -260,6 +275,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.11.0" @@ -272,6 +302,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "beef" version = "0.5.2" @@ -429,6 +465,22 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpufeatures" version = "0.2.7" @@ -529,6 +581,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -771,6 +832,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "globset" version = "0.4.10" @@ -920,6 +987,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -966,6 +1034,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "is-terminal" version = "0.4.7" @@ -993,6 +1067,30 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpc_client" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c1ec33c537dc1d5a8b597313db6d213fee54320f81ea0d19b0c3869b282e1a" +dependencies = [ + "async-trait", + "jsonrpc_client_macro", + "reqwest", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "jsonrpc_client_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97c11e429f0eaa41fe659013680b459d2368d8f0a3e69dccfb7a35800b0dc27b" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "jsonrpsee" version = "0.18.2" @@ -1118,9 +1216,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.144" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libsodium-sys" @@ -1174,11 +1272,26 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + [[package]] name = "mio" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", @@ -1207,6 +1320,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.2" @@ -1281,9 +1403,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1321,9 +1443,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -1450,6 +1572,48 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "reqwest" +version = "0.11.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1542,6 +1706,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -1626,6 +1802,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "soketto" version = "0.7.1" @@ -1685,6 +1871,17 @@ dependencies = [ "url", ] +[[package]] +name = "solar_client" +version = "0.1.0" +dependencies = [ + "anyhow", + "jsonrpc_client", + "reqwest", + "serde_json", + "tokio", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1719,6 +1916,33 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempdir" version = "0.3.7" @@ -1775,26 +1999,26 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.2" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", - "socket2", + "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", @@ -2265,6 +2489,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if 1.0.0", + "windows-sys 0.48.0", +] + [[package]] name = "xdg" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index 12ff259..7f2d11d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,4 +2,6 @@ members = [ "solar", "solar_cli", + "solar_client", ] +resolver = "2" diff --git a/README.md b/README.md index 43725ee..518f506 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ or embedded into another Rust application as a [library](https://github.com/myco :warning: **Solar is alpha software; expect breaking changes** :construction: -[Background](#background) | [Features](#features) | [Installation](#installation) | [Usage](#usage) | [Examples](#examples) | [CLI Options](#options) | [Configuration](#configuration) | [JSON-RPC API](#json-rpc) | [License](#license) +[Background](#background) | [Features](#features) | [Installation](#installation) | [Usage](#usage) | [Examples](#examples) | [CLI Options](#options) | [Configuration](#configuration) | [License](#license) ## Background @@ -126,65 +126,6 @@ Alternatively, peers can be added to the replication configuration via CLI optio Log-level can be defined by setting the `RUST_LOG` environment variable. -## JSON-RPC API - -While running, a solar node can be queried using JSON-RPC over HTTP. - -| Method | Parameters | Response | Description | -| --- | --- | --- | --- | -| `blocks` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `blockers` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `descriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[]` | Returns an array of descriptions | -| `self_descriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[]` | Returns an array of descriptions | -| `latest_description` | `{ "pub_key": "<@...=.ed25519>" }` | `` | Returns a single description | -| `latest_self_description` | `{ "pub_key": "<@...=.ed25519>" }` | `` | Returns a single description | -| `feed` | `{ "pub_key": "<@...=.ed25519>" }` | `[{ "key": "<%...=.sha256>", "value": , "timestamp": , "rts": null }]` | Returns an array of message KVTs (key, value, timestamp) from the local database | -| `follows` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `followers` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `is_following` | `{ "peer_a": "<@...=.ed25519>", "peer_b": "<@...=.ed25519>" }` | `` | Returns a boolean | -| `friends` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `images` | `{ "pub_key": "<@...=.ed25519>" }` | `[<&...=.sha256>]` | Returns an array of image references | -| `self_images` | `{ "pub_key": "<@...=.ed25519>" }` | `[<&...=.sha256>]` | Returns an array of image references | -| `latest_image` | `{ "pub_key": "<@...=.ed25519>" }` | `<&...=.sha256>` | Returns a single image reference | -| `latest_self_image` | `{ "pub_key": "<@...=.ed25519>" }` | `<&...=.sha256>` | Returns a single image reference | -| `message` | `{ "msg_ref": "<%...=.sha256>" }` | `{ "key": "<%...=.sha256>", "value": , "timestamp": , "rts": null }` | Returns a single message KVT (key, value, timestamp) from the local database | -| `names` | `{ "pub_key": "<@...=.ed25519>" }` | `[]` | Returns an array of names | -| `self_names` | `{ "pub_key": "<@...=.ed25519>" }` | `[]` | Returns an array of names | -| `latest_name` | `{ "pub_key": "<@...=.ed25519>" }` | `` | Returns a single name | -| `latest_self_name` | `{ "pub_key": "<@...=.ed25519>" }` | `` | Returns a single name | -| `peers` | | `[{ "pub_key": "<@...=.ed25519>", "seq_num": }` | Returns an array of public key and latest sequence number for each peer in the local database | -| `ping` | | `pong!` | Responds if the JSON-RPC server is running | -| `publish` | `` | `{ "msg_ref": "<%...=.sha256>", "seq_num": }` | Publishes a message and returns the reference (message hash) and sequence number | -| `subscribers` | `{ "channel": "" }` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `subscriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[]` | Returns an array of channel names | -| `whoami` | | `<@...=.ed25519>` | Returns the public key of the local node | - -### Examples - -`curl` can be used to invoke the available methods from the commandline. - -Request: - -`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "ping", "id":1 }' 127.0.0.1:3030` - -Response: - -`{"jsonrpc":"2.0","result":"pong!","id":1}` - -Request: - -`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "publish", "params": {"type": "about", "about": "@o8lWpyLeSqV/BJV9pbxFhKpwm6Lw5k+sqexYK+zT9Tc=.ed25519", "name": "solar_glyph", "description": "glyph's experimental solar (rust) node"}, "id":1 }' 127.0.0.1:3030` - -Response: - -`{"jsonrpc":"2.0","result":{"msg_ref":"%ZwYwLxMHgU8eC43HOziJvYURjZzAzwFk3v5RYS/NbQY=.sha256","seq": 3,"id":1}` - -_Note: You might find it easier to save your JSON to file and pass that to `curl` instead._ - -``` -curl -X POST -H "Content-Type: application/json" --data @publish.json 127.0.0.1:3030 -``` - ## License AGPL-3.0 diff --git a/solar/README.md b/solar/README.md index f89d409..872d05f 100644 --- a/solar/README.md +++ b/solar/README.md @@ -67,33 +67,34 @@ While running, a solar node can be queried using JSON-RPC over HTTP. | Method | Parameters | Response | Description | | --- | --- | --- | --- | -| `blocks` | `"<@...=.ed25519>"` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `blockers` | `"<@...=.ed25519>"` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `descriptions` | `"<@...=.ed25519>"` | `[]` | Returns an array of descriptions | -| `self_descriptions` | `"<@...=.ed25519>"` | `[]` | Returns an array of descriptions | -| `latest_description` | `"<@...=.ed25519>"` | `` | Returns a single description | -| `latest_self_description` | `"<@...=.ed25519>"` | `` | Returns a single description | -| `feed` | `"<@...=.ed25519>"` | `[{ "key": "<%...=.sha256>", "value": , "timestamp": , "rts": null }]` | Returns an array of message KVTs (key, value, timestamp) from the local database | -| `follows` | `"<@...=.ed25519>"` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `followers` | `"<@...=.ed25519>"` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `is_following` | `{ "peer_a": "<@...=.ed25519>", "peer_b": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns a boolean | -| `friends` | `"<@...=.ed25519>"` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `images` | `"<@...=.ed25519>"` | `[<&...=.sha256>]` | Returns an array of image references | -| `self_images` | `"<@...=.ed25519>"` | `[<&...=.sha256>]` | Returns an array of image references | -| `latest_image` | `"<@...=.ed25519>"` | `<&...=.sha256>` | Returns a single image reference | -| `latest_self_image` | `"<@...=.ed25519>"` | `<&...=.sha256>` | Returns a single image reference | -| `message` | `"<%...=.sha256>"` | `{ "key": "<%...=.sha256>", "value": , "timestamp": , "rts": null }` | Returns a single message KVT (key, value, timestamp) from the local database | -| `names` | `"<@...=.ed25519>"` | `[]` | Returns an array of names | -| `self_names` | `"<@...=.ed25519>"` | `[]` | Returns an array of names | -| `latest_name` | `"<@...=.ed25519>"` | `` | Returns a single name | -| `latest_self_name` | `"<@...=.ed25519>"` | `` | Returns a single name | +| `blocks` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys | +| `blockers` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys | +| `descriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[(<@...=.ed25519>, )]` | Returns an array of tuples, each containing a public key and a description | +| `self_descriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[]` | Returns an array of descriptions | +| `latest_description` | `{ "pub_key": "<@...=.ed25519>" }` | `` | Returns a single description | +| `latest_self_description` | `{ "pub_key": "<@...=.ed25519>" }` | `` | Returns a single description | +| `feed` | `{ "pub_key": "<@...=.ed25519>" }` | `[{ "key": "<%...=.sha256>", "value": , "timestamp": , "rts": null }]` | Returns an array of message KVTs (key, value, timestamp) from the local database | +| `follows` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys | +| `followers` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys | +| `is_following` | `{ "peer_a": "<@...=.ed25519>", "peer_b": "<@...=.ed25519>" }` | `` | Returns a boolean | +| `friends` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys | +| `images` | `{ "pub_key": "<@...=.ed25519>" }` | `[(<@...=.ed25519>, <&...=.sha256>)]` | Returns an array of tuples, each containing a public key and an image reference | +| `self_images` | `{ "pub_key": "<@...=.ed25519>" }` | `[<&...=.sha256>]` | Returns an array of image references | +| `latest_image` | `{ "pub_key": "<@...=.ed25519>" }` | `<&...=.sha256>` | Returns a single image reference | +| `latest_self_image` | `{ "pub_key": "<@...=.ed25519>" }` | `<&...=.sha256>` | Returns a single image reference | +| `message` | `{ "msg_ref": "<%...=.sha256>" }` | `{ "key": "<%...=.sha256>", "value": , "timestamp": , "rts": null }` | Returns a single message KVT (key, value, timestamp) from the local database | +| `names` | `{ "pub_key": "<@...=.ed25519>" }` | `[]` | Returns an array of names | +| `self_names` | `{ "pub_key": "<@...=.ed25519>" }` | `[]` | Returns an array of names | +| `latest_name` | `{ "pub_key": "<@...=.ed25519>" }` | `` | Returns a single name | +| `latest_self_name` | `{ "pub_key": "<@...=.ed25519>" }` | `` | Returns a single name | | `peers` | | `[{ "pub_key": "<@...=.ed25519>", "seq_num": }` | Returns an array of public key and latest sequence number for each peer in the local database | | `ping` | | `pong!` | Responds if the JSON-RPC server is running | -| `publish` | `` | `{ "msg_ref": "<%...=.sha256>", "seq_num": }` | Publishes a message and returns the reference (message hash) and sequence number | -| `subscribers` | `""` | `[<@...=.ed25519>]` | Returns an array of public keys | -| `subscriptions` | `"<@...=.ed25519>"` | `[]` | Returns an array of channel names | +| `publish` | `{ "msg": {} }` | `("<%...=.sha256>", )` | Returns a tuple of the reference (message hash) and sequence number | +| `subscribers` | `{ "channel": "" }` | `[<@...=.ed25519>]` | Returns an array of public keys | +| `subscriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[]` | Returns an array of channel names | | `whoami` | | `<@...=.ed25519>` | Returns the public key of the local node | + ### Examples `curl` can be used to invoke the available methods from the commandline. @@ -108,7 +109,7 @@ Response: Request: -`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "publish", "params": {"type": "about", "about": "@o8lWpyLeSqV/BJV9pbxFhKpwm6Lw5k+sqexYK+zT9Tc=.ed25519", "name": "solar_glyph", "description": "glyph's experimental solar (rust) node"}, "id":1 }' 127.0.0.1:3030` +`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "publish", "params": {"msg": {"type": "about", "about": "@o8lWpyLeSqV/BJV9pbxFhKpwm6Lw5k+sqexYK+zT9Tc=.ed25519", "name": "solar_glyph", "description": "glyph's experimental solar (rust) node"} }, "id":1 }' 127.0.0.1:3030` Response: diff --git a/solar/src/actors/jsonrpc/server.rs b/solar/src/actors/jsonrpc/server.rs index 698c7e9..3232733 100644 --- a/solar/src/actors/jsonrpc/server.rs +++ b/solar/src/actors/jsonrpc/server.rs @@ -26,6 +26,12 @@ struct IsFollowing { peer_b: String, } +/// The contents of a raw message (of any supported type). +#[derive(Debug, Deserialize)] +struct Msg { + msg: TypedMessage, +} + /// Message reference containing the key (sha256 hash) of a message. /// Used to parse the key from the parameters supplied to the `message` /// endpoint. @@ -474,8 +480,9 @@ pub async fn actor(server_id: OwnedIdentity, server_addr: SocketAddr) -> Result< // Returns the key (hash) and sequence number of the published message. rpc_module.register_method("publish", move |params: Params, _| { task::block_on(async { - // Parse the parameter containing the post content. - let post_content: TypedMessage = params.parse()?; + // Parse the parameter containing the message content. + let msg_object: Msg = params.parse()?; + let msg_content: TypedMessage = msg_object.msg; // Open the primary KV database for writing. let db = KV_STORE.write().await; @@ -484,8 +491,8 @@ pub async fn actor(server_id: OwnedIdentity, server_addr: SocketAddr) -> Result< // Return `None` if no messages have yet been published on the feed. let last_msg = db.get_latest_msg_val(&server_id.id)?; - // Instantiate and cryptographically-sign a new message using `post`. - let msg = Message::sign(last_msg.as_ref(), &server_id, json!(post_content)) + // Instantiate and cryptographically-sign a new message. + let msg = Message::sign(last_msg.as_ref(), &server_id, json!(msg_content)) .map_err(Error::Validation)?; // Append the signed message to the feed. @@ -497,7 +504,8 @@ pub async fn actor(server_id: OwnedIdentity, server_addr: SocketAddr) -> Result< seq ); - let response = json![{ "msg_ref": msg.id().to_string(), "seq_num": seq }]; + // Return a tuple of message reference and sequence number. + let response = json!((msg.id().to_string(), seq)); Ok::(response) }) diff --git a/solar/src/storage/indexes.rs b/solar/src/storage/indexes.rs index 4cd45a9..42857f2 100644 --- a/solar/src/storage/indexes.rs +++ b/solar/src/storage/indexes.rs @@ -299,27 +299,31 @@ impl Indexes { } /// Return all indexed self-assigned descriptions for the given public key. - pub fn get_self_assigned_descriptions(&self, ssb_id: &str) -> Result> { - let mut descriptions = self.get_descriptions(ssb_id)?; - descriptions.retain(|(author, _description)| author == ssb_id); + pub fn get_self_assigned_descriptions(&self, ssb_id: &str) -> Result> { + let descriptions = self + .get_descriptions(ssb_id)? + .into_iter() + .filter(|(author, _description)| author == ssb_id) + .map(|(_ssb_id, description)| description) + .collect(); Ok(descriptions) } /// Return the most recently indexed description for the given public key. - pub fn get_latest_description(&self, ssb_id: &str) -> Result> { - let descriptions = self.get_descriptions(ssb_id)?; - let description = descriptions.last().cloned(); + pub fn get_latest_description(&self, ssb_id: &str) -> Result> { + let description = self + .get_descriptions(ssb_id)? + .last() + .map(|(_ssb_id, description)| description) + .cloned(); Ok(description) } /// Return the most recently indexed self-assigned description for the given /// public key. - pub fn get_latest_self_assigned_description( - &self, - ssb_id: &str, - ) -> Result> { + pub fn get_latest_self_assigned_description(&self, ssb_id: &str) -> Result> { let self_descriptions = self.get_self_assigned_descriptions(ssb_id)?; let description = self_descriptions.last().cloned(); @@ -460,9 +464,13 @@ impl Indexes { /// Return all indexed self-assigned image references for the given public /// key. - pub fn get_self_assigned_images(&self, ssb_id: &str) -> Result> { - let mut images = self.get_images(ssb_id)?; - images.retain(|(author, _image)| author == ssb_id); + pub fn get_self_assigned_images(&self, ssb_id: &str) -> Result> { + let images = self + .get_images(ssb_id)? + .into_iter() + .filter(|(author, _image)| author == ssb_id) + .map(|(_ssb_id, image)| image) + .collect(); Ok(images) } @@ -478,7 +486,7 @@ impl Indexes { /// Return the most recently indexed self-assigned image reference for the /// given public key. - pub fn get_latest_self_assigned_image(&self, ssb_id: &str) -> Result> { + pub fn get_latest_self_assigned_image(&self, ssb_id: &str) -> Result> { let images = self.get_self_assigned_images(ssb_id)?; let image = images.last().cloned(); @@ -595,7 +603,7 @@ mod test { indexes.index_msg(&keypair.id, first_msg)?; - if let Some((_author, description)) = indexes.get_latest_description(&keypair.id)? { + if let Some(description) = indexes.get_latest_description(&keypair.id)? { assert_eq!(description, first_description); } @@ -632,9 +640,7 @@ mod test { assert_eq!(lastest_name, second_name); } - if let Some((_author, latest_description)) = - indexes.get_latest_description(&keypair.id)? - { + if let Some(latest_description) = indexes.get_latest_description(&keypair.id)? { assert_eq!(latest_description, second_description); } } diff --git a/solar/src/storage/kv.rs b/solar/src/storage/kv.rs index d18c6e0..538c9cc 100644 --- a/solar/src/storage/kv.rs +++ b/solar/src/storage/kv.rs @@ -221,7 +221,7 @@ impl KvStorage { /// Return the public key and latest sequence number for all peers in the /// database. - pub async fn get_peers(&self) -> Result> { + pub async fn get_peers(&self) -> Result> { let db = self.db.as_ref().ok_or(Error::OptionIsNone)?; let mut peers = Vec::new(); @@ -235,8 +235,7 @@ impl KvStorage { // Get the latest sequence number for the peer. // Fallback to a value of 0 if a `None` value is returned. let seq_num = self.get_latest_seq(&pub_key)?.unwrap_or(0); - let peer_latest_sequence = PubKeyAndSeqNum { pub_key, seq_num }; - peers.push(peer_latest_sequence) + peers.push((pub_key, seq_num)) } Ok(peers) @@ -479,8 +478,8 @@ mod test { assert_eq!(peers.len(), 1); // Ensure the public key of the peer matches expectations and that // the sequence number is correct. - assert_eq!(peers[0].pub_key, keypair.id); - assert_eq!(peers[0].seq_num, 1); + assert_eq!(peers[0].0, keypair.id); + assert_eq!(peers[0].1, 1); // Create, sign and append a second post-type message. let msg_content_2 = TypedMessage::Post { diff --git a/solar_client/Cargo.toml b/solar_client/Cargo.toml new file mode 100644 index 0000000..c0902cd --- /dev/null +++ b/solar_client/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "solar_client" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.79" +jsonrpc_client = { version = "0.7.1", features = ["macros", "reqwest"] } +reqwest = { version = "0.11", default-features = false, features = [ "json" ] } +serde_json = { version = "1", features = ["preserve_order", "arbitrary_precision"] } + +[dev-dependencies] +tokio = { version = "1.36.0", features = [ "macros", "rt-multi-thread" ] } diff --git a/solar_client/README.md b/solar_client/README.md new file mode 100644 index 0000000..b23ed95 --- /dev/null +++ b/solar_client/README.md @@ -0,0 +1,9 @@ +# 🌞 Solar JSON-RPC Client + +An HTTP JSON-RPC client for the Solar node. + +See `src/lib.rs` and `examples/` for API details and usage examples. + +## License + +AGPL-3.0 diff --git a/solar_client/examples/blocks.rs b/solar_client/examples/blocks.rs new file mode 100644 index 0000000..c645b1e --- /dev/null +++ b/solar_client/examples/blocks.rs @@ -0,0 +1,21 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let blocks = client.blocks(PUB_KEY).await?; + println!("{:#?}", blocks); + // [ + // "@dW5ch5miTnxLJDVDtB4ZCvrVxh+S8kGCQIBbd5paLhw=.ed25519", + // "@QIlKZ8DMw9XpjpRZ96RBLpfkLnOUZSqamC6WMddGh3I=.ed25519", + // ... + // "@+rMXLy1md42gvbBq+6l6rp95/drh6QyACO1ZZMMnWI0=.ed25519", + // ] + + Ok(()) +} diff --git a/solar_client/examples/descriptions.rs b/solar_client/examples/descriptions.rs new file mode 100644 index 0000000..3119bed --- /dev/null +++ b/solar_client/examples/descriptions.rs @@ -0,0 +1,21 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let descriptions = client.descriptions(PUB_KEY).await?; + println!("{:#?}", descriptions); + // [ + // ( + // "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519", + // "[ sowing seeds of symbiosis | weaving webs of wu wei ]", + // ) + // ] + + Ok(()) +} diff --git a/solar_client/examples/feed.rs b/solar_client/examples/feed.rs new file mode 100644 index 0000000..e392ae0 --- /dev/null +++ b/solar_client/examples/feed.rs @@ -0,0 +1,16 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@qK93G/R9R5J2fiqK+kxV72HqqPUcss+rth8rACcYr4s=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let feed = client.feed(PUB_KEY).await?; + println!("{:#?}", feed); + // TODO + + Ok(()) +} diff --git a/solar_client/examples/follows.rs b/solar_client/examples/follows.rs new file mode 100644 index 0000000..31a744b --- /dev/null +++ b/solar_client/examples/follows.rs @@ -0,0 +1,21 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let follows = client.follows(PUB_KEY).await?; + println!("{:#?}", follows); + // [ + // "@XeP2o+FcSdb5YdTvt/nuYPU2PHJSSwRt+X2y8B2dK5U=.ed25519", + // "@2il4IGrTUzMyInSjCyv6vx6tftELOCxh/FdUuzvj7tE=.ed25519", + // ... + // "@OKRij/n7Uu42A0Z75ty0JI0cZxcieD2NyjXrRdYKNOQ=.ed25519", + // ] + + Ok(()) +} diff --git a/solar_client/examples/images.rs b/solar_client/examples/images.rs new file mode 100644 index 0000000..0f8a646 --- /dev/null +++ b/solar_client/examples/images.rs @@ -0,0 +1,22 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let images = client.images(PUB_KEY).await?; + println!("{:#?}", images); + // [ + // ( + // "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519", + // "&dmLPhR0npN7tHiICGfM1WLBMHhhzh5I5VR3rEKvmOXw=.sha256", + // ), + // ... + // ] + + Ok(()) +} diff --git a/solar_client/examples/is_following.rs b/solar_client/examples/is_following.rs new file mode 100644 index 0000000..062dc0e --- /dev/null +++ b/solar_client/examples/is_following.rs @@ -0,0 +1,17 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PEER_A: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; +const PEER_B: &str = "@2il4IGrTUzMyInSjCyv6vx6tftELOCxh/FdUuzvj7tE=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let is_following = client.is_following(PEER_A, PEER_B).await?; + println!("{}", is_following); + // true + + Ok(()) +} diff --git a/solar_client/examples/latest_description.rs b/solar_client/examples/latest_description.rs new file mode 100644 index 0000000..e5b6c6a --- /dev/null +++ b/solar_client/examples/latest_description.rs @@ -0,0 +1,16 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let latest_description = client.latest_description(PUB_KEY).await?; + println!("{:#?}", latest_description); + // "[ sowing seeds of symbiosis | weaving webs of wu wei ]" + + Ok(()) +} diff --git a/solar_client/examples/latest_image.rs b/solar_client/examples/latest_image.rs new file mode 100644 index 0000000..94ea87d --- /dev/null +++ b/solar_client/examples/latest_image.rs @@ -0,0 +1,19 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let latest_image = client.latest_image(PUB_KEY).await?; + println!("{:#?}", latest_image); + // ( + // "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519", + // "&8M2JFEFHlxJ5q8Lmu3P4bDdCHg0SLB27Q321cy9Upx4=.sha256", + // ) + + Ok(()) +} diff --git a/solar_client/examples/latest_self_description.rs b/solar_client/examples/latest_self_description.rs new file mode 100644 index 0000000..66202d3 --- /dev/null +++ b/solar_client/examples/latest_self_description.rs @@ -0,0 +1,16 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let latest_self_description = client.latest_self_description(PUB_KEY).await?; + println!("{:#?}", latest_self_description); + // "[ sowing seeds of symbiosis | weaving webs of wu wei ]" + + Ok(()) +} diff --git a/solar_client/examples/message.rs b/solar_client/examples/message.rs new file mode 100644 index 0000000..2ebdae2 --- /dev/null +++ b/solar_client/examples/message.rs @@ -0,0 +1,34 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const MSG_REF: &str = "%RCb++/ZhqV1lJNIcoNrk4yM3AfBobT7u8seObZgcEbA=.sha256"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let message = client.message(MSG_REF).await?; + println!("{:#?}", message); + /* + Object { + "key": String("%RCb++/ZhqV1lJNIcoNrk4yM3AfBobT7u8seObZgcEbA=.sha256"), + "value": Object { + "previous": String("%uhtIvUJeHicd+i/biMI/IlLiLkN4pAYgrVq4CA2rSYA=.sha256"), + "sequence": Number(227), + "author": String("@qK93G/R9R5J2fiqK+kxV72HqqPUcss+rth8rACcYr4s=.ed25519"), + "timestamp": Number(1707730214482), + "hash": String("sha256"), + "content": Object { + "type": String("post"), + "text": String("Testing out the solar JSON-RPC client"), + }, + "signature": String("fsDScOQ3Zbm9sRpcUfKV+Rtf/I70vKpRW3BTIuK3IoZGhYj9Z/pdHDGAWUh+9ixToAevdKltgJZWa7DUWdAuCw==.sig.ed25519"), + }, + "timestamp": Number(1707730214.4837818), + "rts": Null, + } + */ + + Ok(()) +} diff --git a/solar_client/examples/names.rs b/solar_client/examples/names.rs new file mode 100644 index 0000000..4d23ad3 --- /dev/null +++ b/solar_client/examples/names.rs @@ -0,0 +1,25 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let names = client.names(PUB_KEY).await?; + println!("{:#?}", names); + // [ + // ( + // "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519", + // "mycognosist", + // ), + // ( + // "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519", + // "glyph", + // ), + // ] + + Ok(()) +} diff --git a/solar_client/examples/peers.rs b/solar_client/examples/peers.rs new file mode 100644 index 0000000..e7d5ebd --- /dev/null +++ b/solar_client/examples/peers.rs @@ -0,0 +1,34 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + // Get the public key and latest sequence number for all peers in the + // local database. + let peers = client.peers().await?; + println!("{:#?}", peers); + // [ + // ( + // "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519", + // 26681, + // ), + // ( + // "@L/g6qZQE/2FdO2UhSJ0uyDiZb5LjJLatM/d8MN+INSM=.ed25519", + // 46, + // ), + // ( + // "@bMUudXOb9+FrVXKIxyn6ro+jo4drbrKXdSoZ9yXp8rc=.ed25519", + // 3, + // ), + // ( + // "@qK93G/R9R5J2fiqK+kxV72HqqPUcss+rth8rACcYr4s=.ed25519", + // 227, + // ), + // ] + + Ok(()) +} diff --git a/solar_client/examples/ping.rs b/solar_client/examples/ping.rs new file mode 100644 index 0000000..216e4d1 --- /dev/null +++ b/solar_client/examples/ping.rs @@ -0,0 +1,15 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let ping = client.ping().await?; + println!("{}", ping); + // pong! + + Ok(()) +} diff --git a/solar_client/examples/publish.rs b/solar_client/examples/publish.rs new file mode 100644 index 0000000..f938bd9 --- /dev/null +++ b/solar_client/examples/publish.rs @@ -0,0 +1,32 @@ +use anyhow::Result; +use serde_json::json; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const TEXT: &str = "Testing message publishing via the solar JSON-RPC client"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + // Construct a JSON post. + // + // This can take any shape conforming to + // `kuska_ssb::api::dto::content::TypedMessage`. + // + // Consult the `kuska_ssb` repo for more details: + // https://github.com/Kuska-ssb/ssb + let post = json!({ + "type": "post", + "text": TEXT, + }); + + let msg_ref_and_seq_num = client.publish(post).await?; + println!("{:?}", msg_ref_and_seq_num); + // ("%J0k3hbXE6CjoA2yFpUsy+A1K+GRcTc5Eh6LH7OuzzUE=.sha256", 231) + + // Or destructure the tuple into individual components: + // let (msg_ref, seq_num) = client.publish(post).await?; + + Ok(()) +} diff --git a/solar_client/examples/self_descriptions.rs b/solar_client/examples/self_descriptions.rs new file mode 100644 index 0000000..a236427 --- /dev/null +++ b/solar_client/examples/self_descriptions.rs @@ -0,0 +1,21 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let self_descriptions = client.self_descriptions(PUB_KEY).await?; + println!("{:#?}", self_descriptions); + // [ + // ( + // "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519", + // "[ sowing seeds of symbiosis | weaving webs of wu wei ]", + // ) + // ] + + Ok(()) +} diff --git a/solar_client/examples/self_images.rs b/solar_client/examples/self_images.rs new file mode 100644 index 0000000..0e03f75 --- /dev/null +++ b/solar_client/examples/self_images.rs @@ -0,0 +1,23 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let self_images = client.self_images(PUB_KEY).await?; + println!("{:#?}", self_images); + // [ + // "&dmLPhR0npN7tHiICGfM1WLBMHhhzh5I5VR3rEKvmOXw=.sha256", + // "&f4YiGjvj3ClOc3VA0XYQnPZAflQm/GROfTkHBjAb3a0=.sha256", + // "&dmLPhR0npN7tHiICGfM1WLBMHhhzh5I5VR3rEKvmOXw=.sha256", + // "&Z8X3/bszQTRZ3cmyEM6sUoa8hlDbS+qfecziI5gRXpY=.sha256", + // "&LesplCjgV8Q355b93QOFCoL5G1KdyXTqIsm4EL9R2Fw=.sha256", + // "&8M2JFEFHlxJ5q8Lmu3P4bDdCHg0SLB27Q321cy9Upx4=.sha256", + // ] + + Ok(()) +} diff --git a/solar_client/examples/subscribers.rs b/solar_client/examples/subscribers.rs new file mode 100644 index 0000000..0f16d14 --- /dev/null +++ b/solar_client/examples/subscribers.rs @@ -0,0 +1,18 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const CHANNEL: &str = "myco"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let subscribers = client.subscribers(CHANNEL).await?; + println!("{:#?}", subscribers); + // [ + // "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519", + // ] + + Ok(()) +} diff --git a/solar_client/examples/subscriptions.rs b/solar_client/examples/subscriptions.rs new file mode 100644 index 0000000..f2ad836 --- /dev/null +++ b/solar_client/examples/subscriptions.rs @@ -0,0 +1,27 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; +const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let subscriptions = client.subscriptions(PUB_KEY).await?; + println!("{:#?}", subscriptions); + // [ + // "falconry", + // "solarfest", + // "compendiumofmadscience", + // "inktober2018", + // "butt-summaries", + // "squatchcam", + // ... + // "arduino", + // "savingspools", + // "meditation", + // ] + + Ok(()) +} diff --git a/solar_client/examples/whoami.rs b/solar_client/examples/whoami.rs new file mode 100644 index 0000000..ef01bea --- /dev/null +++ b/solar_client/examples/whoami.rs @@ -0,0 +1,15 @@ +use anyhow::Result; +use solar_client::{Client, SolarClient}; + +const SERVER_ADDR: &str = "http://127.0.0.1:3030"; + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new(SERVER_ADDR.to_owned())?; + + let whoami = client.whoami().await?; + println!("{}", whoami); + // @qK93G/R9R5J2fiqK+kxV72HqqPUcss+rth8rACcYr4s=.ed25519 + + Ok(()) +} diff --git a/solar_client/src/lib.rs b/solar_client/src/lib.rs new file mode 100644 index 0000000..29f903d --- /dev/null +++ b/solar_client/src/lib.rs @@ -0,0 +1,72 @@ +use anyhow::Result; +use serde_json::Value; + +#[jsonrpc_client::api] +pub trait SolarClient { + async fn blocks(&self, pub_key: &str) -> Vec; + + async fn blockers(&self, pub_key: &str) -> Vec; + + async fn descriptions(&self, pub_key: &str) -> Vec<(String, String)>; + + async fn self_descriptions(&self, pub_key: &str) -> Vec; + + async fn latest_description(&self, pub_key: &str) -> String; + + async fn latest_self_description(&self, pub_key: &str) -> String; + + async fn feed(&self, pub_key: &str) -> Vec; + + async fn follows(&self, pub_key: &str) -> Vec; + + async fn followers(&self, pub_key: &str) -> Vec; + + async fn is_following(&self, peer_a: &str, peer_b: &str) -> bool; + + async fn friends(&self, pub_key: &str) -> Vec; + + async fn images(&self, pub_key: &str) -> Vec<(String, String)>; + + async fn self_images(&self, pub_key: &str) -> Vec; + + async fn latest_image(&self, pub_key: &str) -> (String, String); + + async fn latest_self_image(&self, pub_key: &str) -> String; + + async fn message(&self, msg_ref: &str) -> Value; + + async fn names(&self, pub_key: &str) -> Vec<(String, String)>; + + async fn self_names(&self, pub_key: &str) -> Vec; + + async fn latest_name(&self, pub_key: &str) -> String; + + async fn latest_self_name(&self, pub_key: &str) -> String; + + async fn peers(&self) -> Vec<(String, u64)>; + + async fn ping(&self) -> String; + + async fn publish(&self, msg: Value) -> (String, u64); + + async fn subscribers(&self, channel: &str) -> Vec; + + async fn subscriptions(&self, pub_key: &str) -> Vec; + + async fn whoami(&self) -> String; +} + +#[jsonrpc_client::implement(SolarClient)] +pub struct Client { + inner: reqwest::Client, + base_url: reqwest::Url, +} + +impl Client { + pub fn new(base_url: String) -> Result { + Ok(Self { + inner: reqwest::Client::new(), + base_url: base_url.parse()?, + }) + } +}