pallet_messages_rpc/
lib.rs

1// Strong Documentation Lints
2#![deny(
3	rustdoc::broken_intra_doc_links,
4	rustdoc::missing_crate_level_docs,
5	rustdoc::invalid_codeblock_attributes,
6	missing_docs
7)]
8
9//! Custom APIs for [Messages](../pallet_messages/index.html)
10
11#[cfg(feature = "std")]
12use common_helpers::rpc::map_rpc_result;
13use common_primitives::{messages::*, schema::*};
14use frame_support::{ensure, fail};
15use jsonrpsee::{
16	core::{async_trait, RpcResult},
17	proc_macros::rpc,
18	types::{ErrorObject, ErrorObjectOwned},
19};
20use pallet_messages_runtime_api::MessagesRuntimeApi;
21use sp_api::ProvideRuntimeApi;
22use sp_blockchain::HeaderBackend;
23use sp_runtime::traits::Block as BlockT;
24use std::sync::Arc;
25
26#[cfg(test)]
27mod tests;
28
29/// Frequency Messages Custom RPC API
30#[rpc(client, server)]
31pub trait MessagesApi {
32	/// Retrieve paginated messages by schema id
33	#[method(name = "messages_getBySchemaId")]
34	fn get_messages_by_schema_id(
35		&self,
36		schema_id: SchemaId,
37		pagination: BlockPaginationRequest,
38	) -> RpcResult<BlockPaginationResponse<MessageResponse>>;
39}
40
41/// The client handler for the API used by Frequency Service RPC with `jsonrpsee`
42pub struct MessagesHandler<C, M> {
43	client: Arc<C>,
44	_marker: std::marker::PhantomData<M>,
45}
46
47impl<C, M> MessagesHandler<C, M> {
48	/// Create new instance with the given reference to the client.
49	pub fn new(client: Arc<C>) -> Self {
50		Self { client, _marker: Default::default() }
51	}
52}
53
54/// Errors that occur on the client RPC
55#[derive(Debug)]
56pub enum MessageRpcError {
57	/// Pagination request is bad
58	InvalidPaginationRequest,
59	/// Likely passed u32 block count
60	TypeConversionOverflow,
61	/// Schema Id doesn't exist or errored when retrieving from state
62	InvalidSchemaId,
63}
64
65impl From<MessageRpcError> for ErrorObjectOwned {
66	fn from(e: MessageRpcError) -> Self {
67		let msg = format!("{e:?}");
68		match e {
69			MessageRpcError::InvalidPaginationRequest => ErrorObject::owned(1, msg, None::<()>),
70			MessageRpcError::TypeConversionOverflow => ErrorObject::owned(2, msg, None::<()>),
71			MessageRpcError::InvalidSchemaId => ErrorObject::owned(3, msg, None::<()>),
72		}
73	}
74}
75
76#[async_trait]
77impl<C, Block> MessagesApiServer for MessagesHandler<C, Block>
78where
79	Block: BlockT,
80	C: ProvideRuntimeApi<Block> + HeaderBackend<Block> + 'static,
81	C::Api: MessagesRuntimeApi<Block>,
82{
83	fn get_messages_by_schema_id(
84		&self,
85		schema_id: SchemaId,
86		pagination: BlockPaginationRequest,
87	) -> RpcResult<BlockPaginationResponse<MessageResponse>> {
88		// Request Validation
89		ensure!(pagination.validate(), MessageRpcError::InvalidPaginationRequest);
90
91		// Connect to on-chain data
92		let api = self.client.runtime_api();
93		let at = self.client.info().best_hash;
94
95		// Schema Fetch and Check
96		let schema: SchemaResponse = match api.get_schema_by_id(at, schema_id) {
97			Ok(Some(s)) => s,
98			_ => fail!(MessageRpcError::InvalidSchemaId),
99		};
100
101		let mut response = BlockPaginationResponse::new();
102		let from: u32 = pagination.from_block;
103		let to: u32 = pagination.to_block;
104		let mut from_index = pagination.from_index;
105
106		'loops: for block_number in from..to {
107			let list: Vec<MessageResponse> = api
108				.get_messages_by_schema_and_block(
109					at,
110					schema.schema_id,
111					schema.payload_location,
112					block_number,
113				)
114				.unwrap_or_default();
115
116			let list_size: u32 =
117				list.len().try_into().map_err(|_| MessageRpcError::TypeConversionOverflow)?;
118			for i in from_index..list_size {
119				response.content.push(list[i as usize].clone());
120
121				if response.check_end_condition_and_set_next_pagination(
122					block_number,
123					i,
124					list_size,
125					&pagination,
126				) {
127					break 'loops
128				}
129			}
130
131			// next block starts from 0
132			from_index = 0;
133		}
134
135		map_rpc_result(Ok(response))
136	}
137}