Skip to content
Snippets Groups Projects
indexer.rs 3.31 KiB
Newer Older
use graphql_client::reqwest::post_graphql;
use graphql_client::GraphQLQuery;
use crate::*;

// type used in parameters query
#[allow(non_camel_case_types)]
type jsonb = serde_json::Value;
#[derive(GraphQLQuery)]
#[graphql(
	schema_path = "res/indexer-schema.json",
	query_path = "res/indexer-queries.graphql"
)]
pub struct IdentityNameByPubkey;

#[derive(GraphQLQuery)]
#[graphql(
	schema_path = "res/indexer-schema.json",
	query_path = "res/indexer-queries.graphql"
)]
pub struct IdentityPubkeyByName;

#[derive(GraphQLQuery)]
#[graphql(
	schema_path = "res/indexer-schema.json",
	query_path = "res/indexer-queries.graphql"
)]
pub struct LatestBlock;

#[derive(GraphQLQuery)]
#[graphql(
	schema_path = "res/indexer-schema.json",
	query_path = "res/indexer-queries.graphql"
)]
pub struct GenesisHash;

#[derive(Clone, Debug)]
Hugo Trentesaux's avatar
Hugo Trentesaux committed
pub struct Indexer {
	pub gql_client: reqwest::Client,
Hugo Trentesaux's avatar
Hugo Trentesaux committed
	pub gql_url: String,
Hugo Trentesaux's avatar
Hugo Trentesaux committed
impl Indexer {
	pub async fn username_by_pubkey(&self, pubkey: &str) -> anyhow::Result<Option<String>> {
		Ok(post_graphql::<IdentityNameByPubkey, _>(
			&self.gql_client,
Hugo Trentesaux's avatar
Hugo Trentesaux committed
			&self.gql_url,
			identity_name_by_pubkey::Variables {
				pubkey: pubkey.to_string(),
			},
		)
		.await?
		.data
		.and_then(|data| data.identity.into_iter().next().map(|idty| idty.name)))
	}
	pub async fn pubkey_by_username(&self, username: &str) -> anyhow::Result<Option<String>> {
		Ok(post_graphql::<IdentityPubkeyByName, _>(
			&self.gql_client,
Hugo Trentesaux's avatar
Hugo Trentesaux committed
			self.gql_url.clone(),
			identity_pubkey_by_name::Variables {
				name: username.to_string(),
			},
		)
		.await?
		.data
		.and_then(|data| data.identity_by_pk.map(|idty| idty.pubkey)))
	}
	/// fetch latest block number
	pub async fn fetch_latest_block(&self) -> Result<u64, anyhow::Error> {
		Ok(post_graphql::<LatestBlock, _>(
			&self.gql_client,
			self.gql_url.clone(),
			latest_block::Variables {},
		)
		.await?
		.data
		.unwrap() // must have a data field
		.parameters
		.first()
		.unwrap() // must have one and only one parameter matching request
		.value
		.clone()
		.unwrap() // must have a value field
		.as_u64()
		.unwrap()) // must be a Number of blocks
	}

	/// fetch genesis hash
	pub async fn fetch_genesis_hash(&self) -> Result<Hash, anyhow::Error> {
		Ok(post_graphql::<GenesisHash, _>(
			&self.gql_client,
			self.gql_url.clone(),
			genesis_hash::Variables {},
		)
		.await?
		.data
		.unwrap() // must have a data field
		.block
		.first()
		.unwrap() // must have one and only one block matching request
		.hash
		.clone()
		.parse::<Hash>()
		.unwrap())
	}
}

#[derive(Clone, Default, Debug, clap::Parser)]
pub enum Subcommand {
	#[default]
	/// Show indexer endpoint
	ShowEndpoint,
	/// Fetch latest indexed block
	LatestBlock,
	/// Fetch genesis block hash
	GenesisHash,
pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<()> {
	// build indexer because it is needed for all subcommands
	let data = data.build_indexer()?;
	// match subcommand
	match command {
		Subcommand::ShowEndpoint => {
			println!("indexer endpoint: {}", data.indexer().gql_url);
		}
		Subcommand::LatestBlock => {
			println!(
				"latest block indexed by {} is: {}",
				data.cfg.indexer_endpoint,
				data.indexer().fetch_latest_block().await?
			);
		}
		Subcommand::GenesisHash => {
			println!(
				"hash of genesis block is: {}",
				data.indexer().fetch_genesis_hash().await?
			);
		}