Skip to content

Commit

Permalink
Add /children with pagination (ordinals#2617)
Browse files Browse the repository at this point in the history
  • Loading branch information
lifofifoX committed Nov 2, 2023
1 parent fdbe191 commit 8e8449b
Show file tree
Hide file tree
Showing 10 changed files with 424 additions and 27 deletions.
30 changes: 30 additions & 0 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,7 @@ impl Index {
self.client.get_block(&hash).into_option()
}

#[cfg(test)]
pub(crate) fn get_children_by_inscription_id(
&self,
inscription_id: InscriptionId,
Expand All @@ -795,6 +796,35 @@ impl Index {
.collect()
}

pub(crate) fn get_children_by_inscription_id_paginated(
&self,
inscription_id: InscriptionId,
page_size: usize,
page_index: usize,
) -> Result<(Vec<InscriptionId>, bool)> {
let mut children = self
.database
.begin_read()?
.open_multimap_table(INSCRIPTION_ID_TO_CHILDREN)?
.get(&inscription_id.store())?
.skip(page_index * page_size)
.take(page_size + 1)
.map(|result| {
result
.map(|inscription_id| InscriptionId::load(*inscription_id.value()))
.map_err(|err| err.into())
})
.collect::<Result<Vec<InscriptionId>>>()?;

let more = children.len() > page_size;

if more {
children.pop();
}

Ok((children, more))
}

pub(crate) fn get_etching(&self, txid: Txid) -> Result<Option<Rune>> {
Ok(
self
Expand Down
224 changes: 214 additions & 10 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use {
page_config::PageConfig,
runes::Rune,
templates::{
BlockHtml, BlockJson, ClockSvg, HomeHtml, InputHtml, InscriptionHtml, InscriptionJson,
InscriptionsBlockHtml, InscriptionsHtml, InscriptionsJson, OutputHtml, OutputJson,
PageContent, PageHtml, PreviewAudioHtml, PreviewCodeHtml, PreviewImageHtml,
BlockHtml, BlockJson, ChildrenHtml, ClockSvg, HomeHtml, InputHtml, InscriptionHtml,
InscriptionJson, InscriptionsBlockHtml, InscriptionsHtml, InscriptionsJson, OutputHtml,
OutputJson, PageContent, PageHtml, PreviewAudioHtml, PreviewCodeHtml, PreviewImageHtml,
PreviewMarkdownHtml, PreviewModelHtml, PreviewPdfHtml, PreviewTextHtml, PreviewUnknownHtml,
PreviewVideoHtml, RangeHtml, RareTxt, RuneHtml, RunesHtml, SatHtml, SatJson, TransactionHtml,
},
Expand Down Expand Up @@ -199,6 +199,11 @@ impl Server {
.route("/feed.xml", get(Self::feed))
.route("/input/:block/:transaction/:input", get(Self::input))
.route("/inscription/:inscription_query", get(Self::inscription))
.route("/children/:inscription_id", get(Self::children))
.route(
"/children/:inscription_id/:page",
get(Self::children_paginated),
)
.route("/inscriptions", get(Self::inscriptions))
.route("/inscriptions/:from", get(Self::inscriptions_from))
.route("/inscriptions/:from/:n", get(Self::inscriptions_from_n))
Expand All @@ -207,7 +212,7 @@ impl Server {
get(Self::inscriptions_in_block),
)
.route(
"/inscriptions/block/:height/:page_index",
"/inscriptions/block/:height/:page",
get(Self::inscriptions_in_block_from_page),
)
.route("/install.sh", get(Self::install_script))
Expand Down Expand Up @@ -1140,7 +1145,8 @@ impl Server {

let next = index.get_inscription_id_by_sequence_number(entry.sequence_number + 1)?;

let children = index.get_children_by_inscription_id(inscription_id)?;
let (children, _more_children) =
index.get_children_by_inscription_id_paginated(inscription_id, 4, 0)?;

let rune = index.get_rune_by_inscription_id(inscription_id)?;

Expand All @@ -1166,26 +1172,69 @@ impl Server {
} else {
InscriptionHtml {
chain: page_config.chain,
children,
genesis_fee: entry.fee,
genesis_height: entry.height,
children,
inscription,
inscription_id,
next,
inscription_number: entry.inscription_number,
next,
output,
parent: entry.parent,
previous,
rune,
sat: entry.sat,
satpoint,
timestamp: timestamp(entry.timestamp),
rune,
}
.page(page_config)
.into_response()
})
}

async fn children(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Path(inscription_id): Path<InscriptionId>,
) -> ServerResult<Response> {
Self::children_paginated(
Extension(page_config),
Extension(index),
Path((inscription_id, 0)),
)
.await
}

async fn children_paginated(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Path((parent, page)): Path<(InscriptionId, usize)>,
) -> ServerResult<Response> {
let parent_number = index
.get_inscription_entry(parent)?
.ok_or_not_found(|| format!("inscription {parent}"))?
.inscription_number;

let (children, more_children) =
index.get_children_by_inscription_id_paginated(parent, 100, page)?;

let prev_page = page.checked_sub(1);

let next_page = more_children.then_some(page + 1);

Ok(
ChildrenHtml {
parent,
parent_number,
children,
prev_page,
next_page,
}
.page(page_config)
.into_response(),
)
}

async fn inscriptions(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Expand All @@ -1212,7 +1261,7 @@ impl Server {
async fn inscriptions_in_block_from_page(
Extension(page_config): Extension<Arc<PageConfig>>,
Extension(index): Extension<Arc<Index>>,
Path((block_height, page_index)): Path<(u64, usize)>,
Path((block_height, page)): Path<(u64, usize)>,
accept_json: AcceptJson,
) -> ServerResult<Response> {
let inscriptions = index.get_inscriptions_in_block(block_height)?;
Expand All @@ -1224,7 +1273,7 @@ impl Server {
block_height,
index.block_height()?.unwrap_or(Height(0)).n(),
inscriptions,
page_index,
page,
)?
.page(page_config)
.into_response()
Expand Down Expand Up @@ -3238,6 +3287,161 @@ mod tests {
);
}

#[test]
fn inscription_with_and_without_children_page() {
let server = TestServer::new_with_regtest();
server.mine_blocks(1);

let parent_txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, inscription("text/plain", "hello").to_witness())],
..Default::default()
});

server.mine_blocks(1);

let parent_inscription_id = InscriptionId {
txid: parent_txid,
index: 0,
};

server.assert_response_regex(
format!("/children/{parent_inscription_id}"),
StatusCode::OK,
".*<h3>No children</h3>.*",
);

let txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[
(
2,
0,
0,
Inscription {
content_type: Some("text/plain".into()),
body: Some("hello".into()),
parent: Some(parent_inscription_id.parent_value()),
..Default::default()
}
.to_witness(),
),
(2, 1, 0, Default::default()),
],
..Default::default()
});

server.mine_blocks(1);

let inscription_id = InscriptionId { txid, index: 0 };

server.assert_response_regex(
format!("/children/{parent_inscription_id}"),
StatusCode::OK,
format!(".*<title>Inscription 0 Children</title>.*<h1><a href=/inscription/{parent_inscription_id}>Inscription 0</a> Children</h1>.*<div class=thumbnails>.*<a href=/inscription/{inscription_id}><iframe .* src=/preview/{inscription_id}></iframe></a>.*"),
);
}

#[test]
fn inscriptions_page_shows_max_four_children() {
let server = TestServer::new_with_regtest();
server.mine_blocks(1);

let parent_txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, inscription("text/plain", "hello").to_witness())],
..Default::default()
});

server.mine_blocks(6);

let parent_inscription_id = InscriptionId {
txid: parent_txid,
index: 0,
};

let _txid = server.bitcoin_rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[
(
2,
0,
0,
Inscription {
content_type: Some("text/plain".into()),
body: Some("hello".into()),
parent: Some(parent_inscription_id.parent_value()),
..Default::default()
}
.to_witness(),
),
(
3,
0,
0,
Inscription {
content_type: Some("text/plain".into()),
body: Some("hello".into()),
parent: Some(parent_inscription_id.parent_value()),
..Default::default()
}
.to_witness(),
),
(
4,
0,
0,
Inscription {
content_type: Some("text/plain".into()),
body: Some("hello".into()),
parent: Some(parent_inscription_id.parent_value()),
..Default::default()
}
.to_witness(),
),
(
5,
0,
0,
Inscription {
content_type: Some("text/plain".into()),
body: Some("hello".into()),
parent: Some(parent_inscription_id.parent_value()),
..Default::default()
}
.to_witness(),
),
(
6,
0,
0,
Inscription {
content_type: Some("text/plain".into()),
body: Some("hello".into()),
parent: Some(parent_inscription_id.parent_value()),
..Default::default()
}
.to_witness(),
),
(2, 1, 0, Default::default()),
],
..Default::default()
});

server.mine_blocks(1);

server.assert_response_regex(
format!("/inscription/{parent_inscription_id}"),
StatusCode::OK,
format!(
".*<title>Inscription 0</title>.*
.*<a href=/inscription/.*><iframe .* src=/preview/.*></iframe></a>.*
.*<a href=/inscription/.*><iframe .* src=/preview/.*></iframe></a>.*
.*<a href=/inscription/.*><iframe .* src=/preview/.*></iframe></a>.*
.*<a href=/inscription/.*><iframe .* src=/preview/.*></iframe></a>.*
<div class=center>
<a href=/children/{parent_inscription_id}>all</a>
</div>.*"
),
);
}

#[test]
fn runes_are_displayed_on_runes_page() {
let server = TestServer::new_with_regtest_with_index_runes();
Expand Down
2 changes: 2 additions & 0 deletions src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use {super::*, boilerplate::Boilerplate};

pub(crate) use {
block::{BlockHtml, BlockJson},
children::ChildrenHtml,
clock::ClockSvg,
home::HomeHtml,
iframe::Iframe,
Expand All @@ -25,6 +26,7 @@ pub(crate) use {
};

pub mod block;
mod children;
mod clock;
mod home;
mod iframe;
Expand Down
Loading

0 comments on commit 8e8449b

Please sign in to comment.