Skip to content

Commit

Permalink
Add status endpoint to V2 API.
Browse files Browse the repository at this point in the history
  • Loading branch information
aterentic-ethernal committed Aug 4, 2023
1 parent 0900dd2 commit d44716a
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 5 deletions.
8 changes: 6 additions & 2 deletions src/api/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,19 @@ impl Server {
http_server_port: port,
app_id,
..
} = self.cfg;
} = self.cfg.clone();

let port = (port.1 > 0)
.then(|| thread_rng().gen_range(port.0..=port.1))
.unwrap_or(port.0);

let v1_api = v1::routes(self.db.clone(), app_id, self.counter.clone());
#[cfg(feature = "api-v2")]
let v2_api = v2::routes(self.version.clone(), self.network_version.clone());
let v2_api = v2::routes(
self.version.clone(),
self.network_version.clone(),
self.cfg,
);

let cors = warp::cors()
.allow_any_origin()
Expand Down
42 changes: 42 additions & 0 deletions src/api/v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,48 @@ Content-Type: application/json
- **version** - the Avail Light Client version
- **network_version** - Avail network version supported by the Avail Light Client

## **GET** `/v2/status`

Gets current status and active modes of the light client.

- Use cases
- Monitoring of the active light clients
- Reconfiguration verification
- Development tooling

Response:

```yaml
HTTP/1.1 200 OK
Content-Type: application/json

{
"modes": ["light", "app", "partition"],
"app_id": "{app-id}", // Optional
"genesis_hash": "{genesis-hash}",
"network": "{network}",
"latest_block": {latest-block}, // Optional
"latest_synced_block": {sync-block}, // Optional
"sync_depth": {sync-depth}, // Optional
"partition": "{partition}" // Optional
}
```

- **modes** - active modes
- **app_id** - if **app** mode is active, this field contains configured application ID
- **genesis_hash** - genesis hash of the network to which the light client is connected
- **network** - network host, version and spec version light client is currently con
- **latest_block** - latest processed block
- **latest_synced_block** - the latest processed block in the sync range
- **sync_depth** - number of blocks before the latest to sync on light client start
- **partition** - if configured, displays partition which light client distributes to the peer to peer network

### Modes

- **light** - data availability sampling mode, the light client performs random sampling and calculates confidence
- **app** - light client fetches, verifies, and stores application-related data
- **partition** - light client fetches configured block partition and publishes it to the DHT

# WebSocket API

The Avail Light Client WebSocket API allows real-time communication between a client and a server over a persistent connection, enabling push notifications as an alternative to polling. Web socket API can be used on its own or in combination with HTTP API to enable different pull/push use cases.
Expand Down
10 changes: 9 additions & 1 deletion src/api/v2/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::types::RuntimeConfig;

use super::{
types::{Client, Clients, Subscription, SubscriptionId, Version},
types::{Client, Clients, Status, Subscription, SubscriptionId, Version},
ws,
};
use std::convert::Infallible;
Expand Down Expand Up @@ -28,3 +30,9 @@ pub async fn ws(
// Multiple connections to the same client are currently allowed
Ok(ws.on_upgrade(move |web_socket| ws::connect(subscription_id, web_socket, clients, version)))
}

pub async fn status(config: RuntimeConfig) -> Result<Status, Infallible> {
Ok(Status {
modes: config.into(),
})
}
33 changes: 31 additions & 2 deletions src/api/v2/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::types::RuntimeConfig;

use self::types::{Clients, Version};
use std::{collections::HashMap, convert::Infallible, sync::Arc};
use tokio::sync::RwLock;
Expand All @@ -19,6 +21,15 @@ fn version_route(
.map(move || version.clone())
}

fn status_route(
config: RuntimeConfig,
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone {
warp::path!("v2" / "status")
.and(warp::get())
.and(warp::any().map(move || config.clone()))
.and_then(handlers::status)
}

fn subscriptions_route(
clients: Clients,
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone {
Expand All @@ -43,21 +54,24 @@ fn ws_route(
pub fn routes(
version: String,
network_version: String,
config: RuntimeConfig,
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone {
let clients: Clients = Arc::new(RwLock::new(HashMap::new()));
let version = Version {
version,
network_version,
};
version_route(version.clone())
.or(status_route(config))
.or(subscriptions_route(clients.clone()))
.or(ws_route(clients, version))
}

#[cfg(test)]
mod tests {
use crate::api::v2::types::{
Clients, DataFields, Subscription, SubscriptionId, Topics, Version,
use crate::{
api::v2::types::{Clients, DataFields, Subscription, SubscriptionId, Topics, Version},
types::RuntimeConfig,
};
use std::{
collections::{HashMap, HashSet},
Expand Down Expand Up @@ -91,6 +105,21 @@ mod tests {
);
}

#[tokio::test]
async fn status_route() {
let route = super::status_route(RuntimeConfig::default());
let response = warp::test::request()
.method("GET")
.path("/v2/status")
.reply(&route)
.await;

assert_eq!(
response.body(),
r#"{"modes":["light","app","partition"],"app_id": "1","genesis_hash":"{genesis-hash}","network":"{network}","latest_block":0,"latest_synced_block":0,"sync_depth":1,"partition":"1/10"}"#
);
}

fn all_topics() -> HashSet<Topics> {
vec![
Topics::HeaderVerified,
Expand Down
35 changes: 35 additions & 0 deletions src/api/v2/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use std::{
use tokio::sync::{mpsc::UnboundedSender, RwLock};
use warp::{ws, Reply};

use crate::types::RuntimeConfig;

#[derive(Serialize, Clone)]
pub struct Version {
pub version: String,
Expand All @@ -19,6 +21,39 @@ impl Reply for Version {
}
}

#[derive(Serialize)]
pub struct Status {
pub modes: Vec<Mode>,
}

#[derive(Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum Mode {
Light,
App,
Partition,
}

impl From<RuntimeConfig> for Vec<Mode> {
fn from(value: RuntimeConfig) -> Self {
let mut result: Vec<Mode> = vec![];
result.push(Mode::Light);
if value.app_id.is_some() {
result.push(Mode::App);
}
if value.block_matrix_partition.is_some() {
result.push(Mode::Partition)
}
result
}
}

impl Reply for Status {
fn into_response(self) -> warp::reply::Response {
warp::reply::json(&self).into_response()
}
}

#[derive(Serialize, Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "kebab-case")]
pub enum Topics {
Expand Down

0 comments on commit d44716a

Please sign in to comment.