From 11e797b8f2270585dd449c92f7321a3087d6eae8 Mon Sep 17 00:00:00 2001 From: patate Date: Wed, 5 Jul 2023 06:51:54 +0000 Subject: [PATCH 1/9] add GET sat endpoint --- Cargo.lock | 5 +-- Cargo.toml | 1 + src/epoch.rs | 2 +- src/height.rs | 2 +- src/subcommand/server.rs | 52 +++++++++++++++++++++++++--- src/subcommand/server/accept_json.rs | 24 +++++++++++++ tests/lib.rs | 6 ++-- tests/server_api.rs | 18 ++++++++++ tests/test_server.rs | 34 ++++++++++-------- 9 files changed, 118 insertions(+), 26 deletions(-) create mode 100644 src/subcommand/server/accept_json.rs create mode 100644 tests/server_api.rs diff --git a/Cargo.lock b/Cargo.lock index 6348b4b169..d3704a07f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,9 +352,9 @@ checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7" dependencies = [ "proc-macro2", "quote", @@ -2223,6 +2223,7 @@ name = "ord" version = "0.8.0" dependencies = [ "anyhow", + "async-trait", "axum", "axum-server", "base64 0.21.0", diff --git a/Cargo.toml b/Cargo.toml index 4ffb2e13ce..3ff7c12e68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [".", "test-bitcoincore-rpc"] [dependencies] anyhow = { version = "1.0.56", features = ["backtrace"] } +async-trait = "0.1.69" axum = { version = "0.6.1", features = ["headers"] } axum-server = "0.5.0" base64 = "0.21.0" diff --git a/src/epoch.rs b/src/epoch.rs index 7918191574..328b09af32 100644 --- a/src/epoch.rs +++ b/src/epoch.rs @@ -1,6 +1,6 @@ use super::*; -#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, PartialOrd)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Serialize, PartialOrd)] pub(crate) struct Epoch(pub(crate) u64); impl Epoch { diff --git a/src/height.rs b/src/height.rs index e2a3e20749..75ac7ec8ab 100644 --- a/src/height.rs +++ b/src/height.rs @@ -1,6 +1,6 @@ use super::*; -#[derive(Copy, Clone, Debug, Display, FromStr, Ord, Eq, PartialEq, PartialOrd)] +#[derive(Copy, Clone, Debug, Display, FromStr, Ord, Eq, Serialize, PartialEq, PartialOrd)] pub(crate) struct Height(pub(crate) u64); impl Height { diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index c5049d37f9..ecc2d812cf 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -1,5 +1,6 @@ use { self::{ + accept_json::AcceptJson, deserialize_from_str::DeserializeFromStr, error::{OptionExt, ServerError, ServerResult}, }, @@ -12,7 +13,7 @@ use { }, axum::{ body, - extract::{Extension, Path, Query}, + extract::{Extension, Json, Path, Query}, headers::UserAgent, http::{header, HeaderMap, HeaderValue, StatusCode, Uri}, response::{IntoResponse, Redirect, Response}, @@ -36,6 +37,7 @@ use { }, }; +mod accept_json; mod error; enum BlockQuery { @@ -378,18 +380,58 @@ impl Server { Extension(page_config): Extension>, Extension(index): Extension>, Path(DeserializeFromStr(sat)): Path>, - ) -> ServerResult> { + accept_json: AcceptJson, + ) -> ServerResult { let satpoint = index.rare_sat_satpoint(sat)?; - Ok( + Ok(if accept_json.0 { + Json(serde_json::json!({ + "decimal": sat.decimal().to_string(), + "degree": sat.degree().to_string(), + "percentile": sat.percentile(), + "name": sat.name(), + "cycle": sat.cycle(), + "epoch": sat.epoch(), + "period": sat.period(), + "block": sat.height(), + "offset": sat.third(), + "rarity": sat.rarity(), + "timestamp": index.block_time(sat.height())?.timestamp().to_string(), + "_links": { + "self": { + "href": format!("/sat/{}", sat), + }, + "block": { + "href": format!("/block/{}", sat.height()), + }, + "inscription": (index.get_inscription_id_by_sat(sat)?.is_some()).then(|| { + serde_json::json!({ + "href": format!("/inscription/{:?}", index.get_inscription_id_by_sat(sat)), + }) + }), + "next": (sat < Sat::LAST.0).then(|| { + serde_json::json!({ + "href": format!("/sat/{}", sat.0 + 1), + }) + }), + "prev": (sat > 0).then(|| { + serde_json::json!({ + "href": format!("/sat/{}", sat.0 - 1), + }) + }), + } + })) + .into_response() + } else { SatHtml { sat, satpoint, blocktime: index.block_time(sat.height())?, inscription: index.get_inscription_id_by_sat(sat)?, } - .page(page_config, index.has_sat_index()?), - ) + .page(page_config, index.has_sat_index()?) + .into_response() + }) } async fn ordinal(Path(sat): Path) -> Redirect { diff --git a/src/subcommand/server/accept_json.rs b/src/subcommand/server/accept_json.rs new file mode 100644 index 0000000000..e01edf29c2 --- /dev/null +++ b/src/subcommand/server/accept_json.rs @@ -0,0 +1,24 @@ +use super::*; + +pub(crate) struct AcceptJson(pub(crate) bool); + +#[async_trait::async_trait] +impl axum::extract::FromRequestParts for AcceptJson +where + S: Send + Sync, +{ + type Rejection = (); + + async fn from_request_parts( + parts: &mut http::request::Parts, + _state: &S, + ) -> Result { + Ok(Self( + parts + .headers + .get("accept") + .map(|value| value == "application/json") + .unwrap_or_default(), + )) + } +} diff --git a/tests/lib.rs b/tests/lib.rs index 50a0fbee8c..252f2194d6 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -70,18 +70,20 @@ fn create_wallet(rpc_server: &test_bitcoincore_rpc::Handle) { } mod command_builder; +mod expected; +mod test_server; + mod core; mod epochs; -mod expected; mod find; mod index; mod info; mod list; mod parse; mod server; +mod server_api; mod subsidy; mod supply; -mod test_server; mod traits; mod version; mod wallet; diff --git a/tests/server_api.rs b/tests/server_api.rs new file mode 100644 index 0000000000..1411ba4278 --- /dev/null +++ b/tests/server_api.rs @@ -0,0 +1,18 @@ +use super::*; + +#[test] +fn get_sat() { + let rpc_server = test_bitcoincore_rpc::spawn(); + + let response = + TestServer::spawn_with_args(&rpc_server, &[]).json_request("/sat/2099999997689999"); + + assert_eq!(response.status(), StatusCode::OK); + + let text = response.text().unwrap(); + + assert!(text.contains("\"block\":6929999")); + assert!(text.contains("\"cycle\":5")); + assert!(text.contains("\"epoch\":32")); + assert!(text.contains("\"name\":\"a\"")); +} diff --git a/tests/test_server.rs b/tests/test_server.rs index e068f9b698..a27b85cef4 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -60,19 +60,7 @@ impl TestServer { } pub(crate) fn assert_response_regex(&self, path: impl AsRef, regex: impl AsRef) { - let client = Client::new(&self.rpc_url, Auth::None).unwrap(); - let chain_block_count = client.get_block_count().unwrap() + 1; - - for i in 0.. { - let response = reqwest::blocking::get(self.url().join("/blockcount").unwrap()).unwrap(); - assert_eq!(response.status(), StatusCode::OK); - if response.text().unwrap().parse::().unwrap() == chain_block_count { - break; - } else if i == 20 { - panic!("index failed to synchronize with chain"); - } - thread::sleep(Duration::from_millis(25)); - } + self.check_client_state(); let response = reqwest::blocking::get(self.url().join(path.as_ref()).unwrap()).unwrap(); assert_eq!(response.status(), StatusCode::OK); @@ -80,6 +68,24 @@ impl TestServer { } pub(crate) fn request(&self, path: impl AsRef) -> Response { + self.check_client_state(); + + reqwest::blocking::get(self.url().join(path.as_ref()).unwrap()).unwrap() + } + + pub(crate) fn json_request(&self, path: impl AsRef) -> Response { + self.check_client_state(); + + let client = reqwest::blocking::Client::new(); + + client + .get(self.url().join(path.as_ref()).unwrap()) + .header(reqwest::header::ACCEPT, "application/json") + .send() + .unwrap() + } + + fn check_client_state(&self) { let client = Client::new(&self.rpc_url, Auth::None).unwrap(); let chain_block_count = client.get_block_count().unwrap() + 1; @@ -93,8 +99,6 @@ impl TestServer { } thread::sleep(Duration::from_millis(25)); } - - reqwest::blocking::get(self.url().join(path.as_ref()).unwrap()).unwrap() } } From 9c85dea4537e4e24d5009a7eaf557878bed8cd4b Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 24 Jul 2023 16:52:49 +0200 Subject: [PATCH 2/9] create sat output struct --- src/lib.rs | 2 +- src/sat.rs | 15 ++++++++++++ src/subcommand/server.rs | 49 +++++++++++----------------------------- src/subcommand/traits.rs | 19 +++------------- tests/test_server.rs | 8 +++---- tests/traits.rs | 8 ++++--- 6 files changed, 41 insertions(+), 60 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1f51664923..55cf0f9d3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,7 +115,7 @@ mod outgoing; mod page_config; mod rarity; mod representation; -mod sat; +pub mod sat; mod sat_point; pub mod subcommand; mod tally; diff --git a/src/sat.rs b/src/sat.rs index 1c50f183ed..298fd54c6c 100644 --- a/src/sat.rs +++ b/src/sat.rs @@ -185,6 +185,21 @@ impl Sat { } } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct Output { + pub number: u64, + pub decimal: String, + pub degree: String, + pub name: String, + pub block: u64, + pub cycle: u64, + pub epoch: u64, + pub period: u64, + pub offset: u64, + pub rarity: Rarity, + pub percentile: String, +} + impl PartialEq for Sat { fn eq(&self, other: &u64) -> bool { self.0 == *other diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index ecc2d812cf..3ca7f20b75 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -385,42 +385,19 @@ impl Server { let satpoint = index.rare_sat_satpoint(sat)?; Ok(if accept_json.0 { - Json(serde_json::json!({ - "decimal": sat.decimal().to_string(), - "degree": sat.degree().to_string(), - "percentile": sat.percentile(), - "name": sat.name(), - "cycle": sat.cycle(), - "epoch": sat.epoch(), - "period": sat.period(), - "block": sat.height(), - "offset": sat.third(), - "rarity": sat.rarity(), - "timestamp": index.block_time(sat.height())?.timestamp().to_string(), - "_links": { - "self": { - "href": format!("/sat/{}", sat), - }, - "block": { - "href": format!("/block/{}", sat.height()), - }, - "inscription": (index.get_inscription_id_by_sat(sat)?.is_some()).then(|| { - serde_json::json!({ - "href": format!("/inscription/{:?}", index.get_inscription_id_by_sat(sat)), - }) - }), - "next": (sat < Sat::LAST.0).then(|| { - serde_json::json!({ - "href": format!("/sat/{}", sat.0 + 1), - }) - }), - "prev": (sat > 0).then(|| { - serde_json::json!({ - "href": format!("/sat/{}", sat.0 - 1), - }) - }), - } - })) + Json(sat::Output { + number: sat.0, + decimal: sat.decimal().to_string(), + degree: sat.degree().to_string(), + name: sat.name(), + block: sat.height().0, + cycle: sat.cycle(), + epoch: sat.epoch().0, + period: sat.period(), + offset: sat.third(), + rarity: sat.rarity(), + percentile: sat.percentile(), + }) .into_response() } else { SatHtml { diff --git a/src/subcommand/traits.rs b/src/subcommand/traits.rs index bfb4999be6..d964445056 100644 --- a/src/subcommand/traits.rs +++ b/src/subcommand/traits.rs @@ -6,33 +6,20 @@ pub(crate) struct Traits { sat: Sat, } -#[derive(Debug, PartialEq, Serialize, Deserialize)] -pub struct Output { - pub number: u64, - pub decimal: String, - pub degree: String, - pub name: String, - pub height: u64, - pub cycle: u64, - pub epoch: u64, - pub period: u64, - pub offset: u64, - pub rarity: Rarity, -} - impl Traits { pub(crate) fn run(self) -> Result { - print_json(Output { + print_json(sat::Output { number: self.sat.n(), decimal: self.sat.decimal().to_string(), degree: self.sat.degree().to_string(), name: self.sat.name(), - height: self.sat.height().0, + block: self.sat.height().0, cycle: self.sat.cycle(), epoch: self.sat.epoch().0, period: self.sat.period(), offset: self.sat.third(), rarity: self.sat.rarity(), + percentile: self.sat.percentile(), })?; Ok(()) diff --git a/tests/test_server.rs b/tests/test_server.rs index a27b85cef4..098aa51d17 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -60,7 +60,7 @@ impl TestServer { } pub(crate) fn assert_response_regex(&self, path: impl AsRef, regex: impl AsRef) { - self.check_client_state(); + self.sync_server(); let response = reqwest::blocking::get(self.url().join(path.as_ref()).unwrap()).unwrap(); assert_eq!(response.status(), StatusCode::OK); @@ -68,13 +68,13 @@ impl TestServer { } pub(crate) fn request(&self, path: impl AsRef) -> Response { - self.check_client_state(); + self.sync_server(); reqwest::blocking::get(self.url().join(path.as_ref()).unwrap()).unwrap() } pub(crate) fn json_request(&self, path: impl AsRef) -> Response { - self.check_client_state(); + self.sync_server(); let client = reqwest::blocking::Client::new(); @@ -85,7 +85,7 @@ impl TestServer { .unwrap() } - fn check_client_state(&self) { + fn sync_server(&self) { let client = Client::new(&self.rpc_url, Auth::None).unwrap(); let chain_block_count = client.get_block_count().unwrap() + 1; diff --git a/tests/traits.rs b/tests/traits.rs index da2d1232e8..38c7744dc9 100644 --- a/tests/traits.rs +++ b/tests/traits.rs @@ -1,4 +1,4 @@ -use {super::*, ord::subcommand::traits::Output, ord::Rarity}; +use {super::*, ord::sat::Output, ord::Rarity}; #[test] fn traits_command_prints_sat_traits() { @@ -9,12 +9,13 @@ fn traits_command_prints_sat_traits() { decimal: "0.0".into(), degree: "0°0′0″0‴".into(), name: "nvtdijuwxlp".into(), - height: 0, + block: 0, cycle: 0, epoch: 0, period: 0, offset: 0, rarity: Rarity::Mythic, + percentile: "0%".into(), } ); } @@ -27,12 +28,13 @@ fn traits_command_for_last_sat() { decimal: "6929999.0".into(), degree: "5°209999′1007″0‴".into(), name: "a".into(), - height: 6929999, + block: 6929999, cycle: 5, epoch: 32, period: 3437, offset: 0, rarity: Rarity::Uncommon, + percentile: "100%".into(), } ); } From 6c4da27da67b141cea58383299fdb8e4e6a3cebe Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 24 Jul 2023 18:23:33 +0200 Subject: [PATCH 3/9] quick fix --- src/lib.rs | 4 ++-- src/sat.rs | 15 --------------- src/subcommand/server.rs | 11 ++++++++--- src/subcommand/traits.rs | 19 ++++++++++++++++--- src/templates.rs | 2 +- src/templates/sat.rs | 18 ++++++++++++++++++ tests/traits.rs | 8 +++----- 7 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ede36f0320..ade0f15ded 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,11 +116,11 @@ mod outgoing; mod page_config; mod rarity; mod representation; -pub mod sat; +pub(crate) mod sat; mod sat_point; pub mod subcommand; mod tally; -mod templates; +pub mod templates; mod wallet; type Result = std::result::Result; diff --git a/src/sat.rs b/src/sat.rs index 298fd54c6c..1c50f183ed 100644 --- a/src/sat.rs +++ b/src/sat.rs @@ -185,21 +185,6 @@ impl Sat { } } -#[derive(Debug, PartialEq, Serialize, Deserialize)] -pub struct Output { - pub number: u64, - pub decimal: String, - pub degree: String, - pub name: String, - pub block: u64, - pub cycle: u64, - pub epoch: u64, - pub period: u64, - pub offset: u64, - pub rarity: Rarity, - pub percentile: String, -} - impl PartialEq for Sat { fn eq(&self, other: &u64) -> bool { self.0 == *other diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 0f5f5e0c70..fc4fa5f7ea 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -387,9 +387,11 @@ impl Server { accept_json: AcceptJson, ) -> ServerResult { let satpoint = index.rare_sat_satpoint(sat)?; + let blocktime = index.block_time(sat.height())?; + let inscriptions = index.get_inscription_ids_by_sat(sat)?; Ok(if accept_json.0 { - Json(sat::Output { + Json(templates::sat::SatJson { number: sat.0, decimal: sat.decimal().to_string(), degree: sat.degree().to_string(), @@ -401,14 +403,17 @@ impl Server { offset: sat.third(), rarity: sat.rarity(), percentile: sat.percentile(), + satpoint, + timestamp: blocktime.timestamp().to_string(), + inscriptions, }) .into_response() } else { SatHtml { sat, satpoint, - blocktime: index.block_time(sat.height())?, - inscriptions: index.get_inscription_ids_by_sat(sat)?, + blocktime, + inscriptions, } .page(page_config, index.has_sat_index()?) .into_response() diff --git a/src/subcommand/traits.rs b/src/subcommand/traits.rs index d964445056..bfb4999be6 100644 --- a/src/subcommand/traits.rs +++ b/src/subcommand/traits.rs @@ -6,20 +6,33 @@ pub(crate) struct Traits { sat: Sat, } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct Output { + pub number: u64, + pub decimal: String, + pub degree: String, + pub name: String, + pub height: u64, + pub cycle: u64, + pub epoch: u64, + pub period: u64, + pub offset: u64, + pub rarity: Rarity, +} + impl Traits { pub(crate) fn run(self) -> Result { - print_json(sat::Output { + print_json(Output { number: self.sat.n(), decimal: self.sat.decimal().to_string(), degree: self.sat.degree().to_string(), name: self.sat.name(), - block: self.sat.height().0, + height: self.sat.height().0, cycle: self.sat.cycle(), epoch: self.sat.epoch().0, period: self.sat.period(), offset: self.sat.third(), rarity: self.sat.rarity(), - percentile: self.sat.percentile(), })?; Ok(()) diff --git a/src/templates.rs b/src/templates.rs index 7a8858a869..51fcc8663c 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -31,7 +31,7 @@ mod output; mod preview; mod range; mod rare; -mod sat; +pub mod sat; mod transaction; #[derive(Boilerplate)] diff --git a/src/templates/sat.rs b/src/templates/sat.rs index fd1c5bab7d..c42b839750 100644 --- a/src/templates/sat.rs +++ b/src/templates/sat.rs @@ -8,6 +8,24 @@ pub(crate) struct SatHtml { pub(crate) inscriptions: Vec, } +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct SatJson { + pub number: u64, + pub decimal: String, + pub degree: String, + pub name: String, + pub block: u64, + pub cycle: u64, + pub epoch: u64, + pub period: u64, + pub offset: u64, + pub rarity: Rarity, + pub percentile: String, + pub satpoint: Option, + pub timestamp: String, + pub inscriptions: Vec, +} + impl PageContent for SatHtml { fn title(&self) -> String { format!("Sat {}", self.sat) diff --git a/tests/traits.rs b/tests/traits.rs index 38c7744dc9..da2d1232e8 100644 --- a/tests/traits.rs +++ b/tests/traits.rs @@ -1,4 +1,4 @@ -use {super::*, ord::sat::Output, ord::Rarity}; +use {super::*, ord::subcommand::traits::Output, ord::Rarity}; #[test] fn traits_command_prints_sat_traits() { @@ -9,13 +9,12 @@ fn traits_command_prints_sat_traits() { decimal: "0.0".into(), degree: "0°0′0″0‴".into(), name: "nvtdijuwxlp".into(), - block: 0, + height: 0, cycle: 0, epoch: 0, period: 0, offset: 0, rarity: Rarity::Mythic, - percentile: "0%".into(), } ); } @@ -28,13 +27,12 @@ fn traits_command_for_last_sat() { decimal: "6929999.0".into(), degree: "5°209999′1007″0‴".into(), name: "a".into(), - block: 6929999, + height: 6929999, cycle: 5, epoch: 32, period: 3437, offset: 0, rarity: Rarity::Uncommon, - percentile: "100%".into(), } ); } From 866b9554c4c89f8743053f67f44686c9deaee7c7 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 24 Jul 2023 19:42:57 +0200 Subject: [PATCH 4/9] add more tests --- src/lib.rs | 6 ++-- tests/json_api.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++ tests/lib.rs | 2 +- tests/server_api.rs | 18 ----------- 4 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 tests/json_api.rs delete mode 100644 tests/server_api.rs diff --git a/src/lib.rs b/src/lib.rs index ade0f15ded..1ef8d220ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,15 +108,15 @@ mod fee_rate; mod height; mod index; mod inscription; -mod inscription_id; +pub mod inscription_id; mod media; mod object; mod options; mod outgoing; mod page_config; -mod rarity; +pub mod rarity; mod representation; -pub(crate) mod sat; +pub mod sat; mod sat_point; pub mod subcommand; mod tally; diff --git a/tests/json_api.rs b/tests/json_api.rs new file mode 100644 index 0000000000..f24db40d8d --- /dev/null +++ b/tests/json_api.rs @@ -0,0 +1,76 @@ +use { + super::*, ord::inscription_id::InscriptionId, ord::rarity::Rarity, ord::templates::sat::SatJson, + ord::SatPoint, +}; + +#[test] +fn get_sat_without_sat_index() { + let rpc_server = test_bitcoincore_rpc::spawn(); + + let response = + TestServer::spawn_with_args(&rpc_server, &[]).json_request("/sat/2099999997689999"); + + assert_eq!(response.status(), StatusCode::OK); + + let mut sat_json: SatJson = serde_json::from_str(&response.text().unwrap()).unwrap(); + + // this is a hack to ignore the timestamp, since it changes for every request + sat_json.timestamp = "".into(); + + pretty_assert_eq!( + sat_json, + SatJson { + number: 2099999997689999, + decimal: "6929999.0".into(), + degree: "5°209999′1007″0‴".into(), + name: "a".into(), + block: 6929999, + cycle: 5, + epoch: 32, + period: 3437, + offset: 0, + rarity: Rarity::Uncommon, + percentile: "100%".into(), + satpoint: None, + timestamp: "".into(), + inscriptions: vec![], + } + ) +} + +#[test] +fn get_sat_with_inscription_and_sat_index() { + let rpc_server = test_bitcoincore_rpc::spawn(); + + create_wallet(&rpc_server); + + let Inscribe { reveal, .. } = inscribe(&rpc_server); + let inscription_id = InscriptionId::from(reveal); + + let response = TestServer::spawn_with_args(&rpc_server, &["--index-sats"]) + .json_request(format!("/sat/{}", 50 * COIN_VALUE)); + + assert_eq!(response.status(), StatusCode::OK); + + let sat_json: SatJson = serde_json::from_str(&response.text().unwrap()).unwrap(); + + pretty_assert_eq!( + sat_json, + SatJson { + number: 50 * COIN_VALUE, + decimal: "1.0".into(), + degree: "0°1′1″0‴".into(), + name: "nvtcsezkbth".into(), + block: 1, + cycle: 0, + epoch: 0, + period: 0, + offset: 0, + rarity: Rarity::Uncommon, + percentile: "0.00023809523835714296%".into(), + satpoint: Some(SatPoint::from_str(&format!("{}:{}:{}", reveal, 0, 0)).unwrap()), + timestamp: "1970-01-01 00:00:01 UTC".into(), + inscriptions: vec![inscription_id], + } + ) +} diff --git a/tests/lib.rs b/tests/lib.rs index 4b8db82aa5..6ad150a58e 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -82,10 +82,10 @@ mod epochs; mod find; mod index; mod info; +mod json_api; mod list; mod parse; mod server; -mod server_api; mod subsidy; mod supply; mod traits; diff --git a/tests/server_api.rs b/tests/server_api.rs deleted file mode 100644 index 1411ba4278..0000000000 --- a/tests/server_api.rs +++ /dev/null @@ -1,18 +0,0 @@ -use super::*; - -#[test] -fn get_sat() { - let rpc_server = test_bitcoincore_rpc::spawn(); - - let response = - TestServer::spawn_with_args(&rpc_server, &[]).json_request("/sat/2099999997689999"); - - assert_eq!(response.status(), StatusCode::OK); - - let text = response.text().unwrap(); - - assert!(text.contains("\"block\":6929999")); - assert!(text.contains("\"cycle\":5")); - assert!(text.contains("\"epoch\":32")); - assert!(text.contains("\"name\":\"a\"")); -} From 711fe2502730d77c465d3b92ffc9736f29a836d7 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 24 Jul 2023 21:58:48 +0200 Subject: [PATCH 5/9] quick fix --- src/subcommand/server.rs | 4 ++-- src/templates.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index fc4fa5f7ea..6ce4cc5582 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -9,7 +9,7 @@ use { crate::templates::{ BlockHtml, ClockSvg, HomeHtml, InputHtml, InscriptionHtml, InscriptionsHtml, OutputHtml, PageContent, PageHtml, PreviewAudioHtml, PreviewImageHtml, PreviewPdfHtml, PreviewTextHtml, - PreviewUnknownHtml, PreviewVideoHtml, RangeHtml, RareTxt, SatHtml, TransactionHtml, + PreviewUnknownHtml, PreviewVideoHtml, RangeHtml, RareTxt, SatJson, SatHtml, TransactionHtml, }, axum::{ body, @@ -391,7 +391,7 @@ impl Server { let inscriptions = index.get_inscription_ids_by_sat(sat)?; Ok(if accept_json.0 { - Json(templates::sat::SatJson { + Json(SatJson { number: sat.0, decimal: sat.decimal().to_string(), degree: sat.degree().to_string(), diff --git a/src/templates.rs b/src/templates.rs index 51fcc8663c..e4f008f6a8 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -16,7 +16,7 @@ pub(crate) use { }, range::RangeHtml, rare::RareTxt, - sat::SatHtml, + sat::{SatHtml, SatJson}, transaction::TransactionHtml, }; From 439f7066a0727a6a472f9b864ef0decff9b95f19 Mon Sep 17 00:00:00 2001 From: raphjaph Date: Mon, 24 Jul 2023 22:50:55 +0200 Subject: [PATCH 6/9] fmt --- src/subcommand/server.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 6ce4cc5582..eebf9a84b0 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -9,7 +9,7 @@ use { crate::templates::{ BlockHtml, ClockSvg, HomeHtml, InputHtml, InscriptionHtml, InscriptionsHtml, OutputHtml, PageContent, PageHtml, PreviewAudioHtml, PreviewImageHtml, PreviewPdfHtml, PreviewTextHtml, - PreviewUnknownHtml, PreviewVideoHtml, RangeHtml, RareTxt, SatJson, SatHtml, TransactionHtml, + PreviewUnknownHtml, PreviewVideoHtml, RangeHtml, RareTxt, SatHtml, SatJson, TransactionHtml, }, axum::{ body, From 764a7ece56d3852c2bf44c860c606f5ecc8afbb8 Mon Sep 17 00:00:00 2001 From: Patate Date: Wed, 2 Aug 2023 15:18:28 +0200 Subject: [PATCH 7/9] add option to activate JSON responses --- src/index.rs | 17 +++++++++++++++++ src/options.rs | 6 ++++++ src/subcommand/server.rs | 2 +- tests/json_api.rs | 6 +++--- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/index.rs b/src/index.rs index b2b208c7c7..89b17c442d 100644 --- a/src/index.rs +++ b/src/index.rs @@ -464,6 +464,10 @@ impl Index { Ok(()) } + pub(crate) fn is_json_api_enabled(&self) -> bool { + self.options.enable_json_api + } + pub(crate) fn is_reorged(&self) -> bool { self.reorged.load(atomic::Ordering::Relaxed) } @@ -1163,6 +1167,19 @@ mod tests { } } + #[test] + fn json_api_enabled() { + { + let context = Context::builder().build(); + assert_eq!(context.options.enable_json_api, false); + } + + { + let context = Context::builder().args(["--enable-json-api"]).build(); + assert_eq!(context.options.enable_json_api, true); + } + } + #[test] fn height_limit() { { diff --git a/src/options.rs b/src/options.rs index 20841e911b..3423a7103c 100644 --- a/src/options.rs +++ b/src/options.rs @@ -54,6 +54,12 @@ pub(crate) struct Options { pub(crate) testnet: bool, #[clap(long, default_value = "ord", help = "Use wallet named .")] pub(crate) wallet: String, + #[clap( + long, + short, + help = "Enable JSON API to get JSON responses from the server instead of HTML." + )] + pub(crate) enable_json_api: bool, } impl Options { diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index eebf9a84b0..7015fe2841 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -390,7 +390,7 @@ impl Server { let blocktime = index.block_time(sat.height())?; let inscriptions = index.get_inscription_ids_by_sat(sat)?; - Ok(if accept_json.0 { + Ok(if index.is_json_api_enabled() && accept_json.0 { Json(SatJson { number: sat.0, decimal: sat.decimal().to_string(), diff --git a/tests/json_api.rs b/tests/json_api.rs index f24db40d8d..3cc3ea5867 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -7,8 +7,8 @@ use { fn get_sat_without_sat_index() { let rpc_server = test_bitcoincore_rpc::spawn(); - let response = - TestServer::spawn_with_args(&rpc_server, &[]).json_request("/sat/2099999997689999"); + let response = TestServer::spawn_with_args(&rpc_server, &["--enable-json-api"]) + .json_request("/sat/2099999997689999"); assert_eq!(response.status(), StatusCode::OK); @@ -47,7 +47,7 @@ fn get_sat_with_inscription_and_sat_index() { let Inscribe { reveal, .. } = inscribe(&rpc_server); let inscription_id = InscriptionId::from(reveal); - let response = TestServer::spawn_with_args(&rpc_server, &["--index-sats"]) + let response = TestServer::spawn_with_args(&rpc_server, &["--index-sats", "--enable-json-api"]) .json_request(format!("/sat/{}", 50 * COIN_VALUE)); assert_eq!(response.status(), StatusCode::OK); From 105ded639dc577efccec5ecd2873d90f2d479655 Mon Sep 17 00:00:00 2001 From: raph Date: Thu, 10 Aug 2023 11:54:06 +0200 Subject: [PATCH 8/9] Update src/options.rs --- src/options.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/options.rs b/src/options.rs index 3423a7103c..a5a97191b7 100644 --- a/src/options.rs +++ b/src/options.rs @@ -57,7 +57,7 @@ pub(crate) struct Options { #[clap( long, short, - help = "Enable JSON API to get JSON responses from the server instead of HTML." + help = "Enable JSON API." )] pub(crate) enable_json_api: bool, } From eef8f78ffdf4e737ecf811297323b4d8871b367d Mon Sep 17 00:00:00 2001 From: raphjaph Date: Thu, 10 Aug 2023 12:16:07 +0200 Subject: [PATCH 9/9] set correct http response code --- src/index.rs | 13 ------------- src/options.rs | 6 +----- src/subcommand/server.rs | 41 +++++++++++++++++++++------------------- tests/json_api.rs | 10 ++++++++++ 4 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/index.rs b/src/index.rs index f907126e99..1e1cf70405 100644 --- a/src/index.rs +++ b/src/index.rs @@ -1158,19 +1158,6 @@ mod tests { } } - #[test] - fn json_api_enabled() { - { - let context = Context::builder().build(); - assert_eq!(context.options.enable_json_api, false); - } - - { - let context = Context::builder().args(["--enable-json-api"]).build(); - assert_eq!(context.options.enable_json_api, true); - } - } - #[test] fn height_limit() { { diff --git a/src/options.rs b/src/options.rs index a5a97191b7..7d2e5ef8a2 100644 --- a/src/options.rs +++ b/src/options.rs @@ -54,11 +54,7 @@ pub(crate) struct Options { pub(crate) testnet: bool, #[clap(long, default_value = "ord", help = "Use wallet named .")] pub(crate) wallet: String, - #[clap( - long, - short, - help = "Enable JSON API." - )] + #[clap(long, short, help = "Enable JSON API.")] pub(crate) enable_json_api: bool, } diff --git a/src/subcommand/server.rs b/src/subcommand/server.rs index 7015fe2841..d33290c4e3 100644 --- a/src/subcommand/server.rs +++ b/src/subcommand/server.rs @@ -389,25 +389,28 @@ impl Server { let satpoint = index.rare_sat_satpoint(sat)?; let blocktime = index.block_time(sat.height())?; let inscriptions = index.get_inscription_ids_by_sat(sat)?; - - Ok(if index.is_json_api_enabled() && accept_json.0 { - Json(SatJson { - number: sat.0, - decimal: sat.decimal().to_string(), - degree: sat.degree().to_string(), - name: sat.name(), - block: sat.height().0, - cycle: sat.cycle(), - epoch: sat.epoch().0, - period: sat.period(), - offset: sat.third(), - rarity: sat.rarity(), - percentile: sat.percentile(), - satpoint, - timestamp: blocktime.timestamp().to_string(), - inscriptions, - }) - .into_response() + Ok(if accept_json.0 { + if index.is_json_api_enabled() { + Json(SatJson { + number: sat.0, + decimal: sat.decimal().to_string(), + degree: sat.degree().to_string(), + name: sat.name(), + block: sat.height().0, + cycle: sat.cycle(), + epoch: sat.epoch().0, + period: sat.period(), + offset: sat.third(), + rarity: sat.rarity(), + percentile: sat.percentile(), + satpoint, + timestamp: blocktime.timestamp().to_string(), + inscriptions, + }) + .into_response() + } else { + StatusCode::NOT_ACCEPTABLE.into_response() + } } else { SatHtml { sat, diff --git a/tests/json_api.rs b/tests/json_api.rs index 3cc3ea5867..3cbba92188 100644 --- a/tests/json_api.rs +++ b/tests/json_api.rs @@ -74,3 +74,13 @@ fn get_sat_with_inscription_and_sat_index() { } ) } + +#[test] +fn json_request_fails_when_not_enabled() { + let rpc_server = test_bitcoincore_rpc::spawn(); + + let response = + TestServer::spawn_with_args(&rpc_server, &[]).json_request("/sat/2099999997689999"); + + assert_eq!(response.status(), StatusCode::NOT_ACCEPTABLE); +}