diff --git a/rust-libs/modules/gva/dbs-reader/src/endpoints.rs b/rust-libs/modules/gva/dbs-reader/src/endpoints.rs new file mode 100644 index 0000000000000000000000000000000000000000..47eae2d47252741a58ed69f08cba4f274792e80e --- /dev/null +++ b/rust-libs/modules/gva/dbs-reader/src/endpoints.rs @@ -0,0 +1,120 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +use crate::*; +use duniter_dbs::databases::dunp_v1::DunpV1DbReadable; + +#[cfg(test)] +mod tests { + use super::*; + use duniter_dbs::databases::dunp_v1::DunpV1DbWritable; + use duniter_dbs::PeerCardDbV1; + + #[test] + fn test_empty_endpoints() -> KvResult<()> { + // Populate DB + let dunp_db = duniter_dbs::databases::dunp_v1::DunpV1Db::<Mem>::open(MemConf::default())?; + let db_reader = DbsReaderImpl::mem(); + let pk = PublicKey::default(); + + dunp_db + .peers_old_write() + .upsert(PubKeyKeyV2(pk), PeerCardDbV1::default())?; + + // Request Data + let api_list = vec!["GVA".to_owned()]; + assert_eq!( + db_reader.endpoints_(&dunp_db, api_list)?, + Vec::<String>::new() + ); + + Ok(()) + } + #[test] + fn test_endpoints_with_empty_api_list() -> KvResult<()> { + let dummy_endpoint = "GVA S domain.tld 443 gva"; + + // Populate DB + let dunp_db = duniter_dbs::databases::dunp_v1::DunpV1Db::<Mem>::open(MemConf::default())?; + let db_reader = DbsReaderImpl::mem(); + let pk = PublicKey::default(); + let peer = PeerCardDbV1 { + endpoints: vec![dummy_endpoint.to_owned()], + ..Default::default() + }; + + dunp_db.peers_old_write().upsert(PubKeyKeyV2(pk), peer)?; + + // Request Data + let api_list = vec![]; + assert_eq!( + db_reader.endpoints_(&dunp_db, api_list)?, + Vec::<String>::new() + ); + + Ok(()) + } + #[test] + fn test_single_peer_endpoints() -> KvResult<()> { + let dummy_endpoint = "GVA S domain.tld 443 gva"; + + // Populate DB + let dunp_db = duniter_dbs::databases::dunp_v1::DunpV1Db::<Mem>::open(MemConf::default())?; + let db_reader = DbsReaderImpl::mem(); + let pk = PublicKey::default(); + let peer = PeerCardDbV1 { + endpoints: vec![dummy_endpoint.to_owned()], + ..Default::default() + }; + + dunp_db.peers_old_write().upsert(PubKeyKeyV2(pk), peer)?; + + // Request Data + let api_list = vec!["GVA".to_owned()]; + assert_eq!( + db_reader.endpoints_(&dunp_db, api_list)?, + vec![dummy_endpoint.to_owned()] + ); + + Ok(()) + } +} + +impl DbsReaderImpl { + pub(super) fn endpoints_<DB: DunpV1DbReadable>( + &self, + network_db: &DB, + mut api_list: Vec<String>, + ) -> KvResult<Vec<String>> { + if api_list.is_empty() { + return Ok(vec![]); + } + for api in &mut api_list { + api.push(' '); + } + network_db.peers_old().iter(.., |it| { + it.values() + .map_ok(|peer| { + peer.endpoints.into_iter().filter(|endpoint| { + api_list + .iter() + .any(|api| endpoint.starts_with(api.as_str())) + }) + }) + .flatten_ok() + .collect::<Result<Vec<String>, _>>() + }) + } +} diff --git a/rust-libs/modules/gva/dbs-reader/src/lib.rs b/rust-libs/modules/gva/dbs-reader/src/lib.rs index 385a8daaa533c9e971047aac22d5cb373247ed7e..dcf72cc05d546237fbdd4aeee4357069c19ca429 100644 --- a/rust-libs/modules/gva/dbs-reader/src/lib.rs +++ b/rust-libs/modules/gva/dbs-reader/src/lib.rs @@ -24,6 +24,7 @@ pub mod block; pub mod current_frame; +pub mod endpoints; pub mod find_inputs; pub mod idty; pub mod pagination; @@ -41,7 +42,7 @@ use dubp::documents::transaction::TransactionDocumentV10; use dubp::{block::DubpBlockV10, common::crypto::hashs::Hash}; use dubp::{common::prelude::BlockNumber, wallet::prelude::*}; use duniter_bca_types::utxo::Utxo; -use duniter_dbs::FileBackend; +use duniter_dbs::{databases::dunp_v1::DunpV1DbReadable, FileBackend}; use duniter_dbs::{ databases::{ bc_v2::{BcV2DbReadable, BcV2DbRo}, @@ -85,6 +86,11 @@ pub trait DbsReader { bc_db: &BcV2DbRo<FileBackend>, page_info: PageInfo<block::BlockCursor>, ) -> KvResult<PagedData<Vec<(block::BlockCursor, BlockMetaV2)>>>; + fn endpoints<Db: 'static + DunpV1DbReadable>( + &self, + network_db: &Db, + api_list: Vec<String>, + ) -> KvResult<Vec<String>>; fn find_inputs<TxsMpDb: 'static + TxsMpV2DbReadable>( &self, bc_db: &BcV2DbRo<FileBackend>, @@ -186,6 +192,14 @@ impl DbsReader for DbsReaderImpl { self.blocks_(bc_db, page_info) } + fn endpoints<Db: 'static + DunpV1DbReadable>( + &self, + network_db: &Db, + api_list: Vec<String>, + ) -> KvResult<Vec<String>> { + self.endpoints_(network_db, api_list) + } + fn find_inputs<TxsMpDb: 'static + TxsMpV2DbReadable>( &self, bc_db: &BcV2DbRo<FileBackend>, diff --git a/rust-libs/modules/gva/gql/src/queries.rs b/rust-libs/modules/gva/gql/src/queries.rs index 20de06c01ee0232aed45a3ae3503b5d972ae7f19..710782baacf13d6a493c9073823941da90826690 100644 --- a/rust-libs/modules/gva/gql/src/queries.rs +++ b/rust-libs/modules/gva/gql/src/queries.rs @@ -17,6 +17,7 @@ pub mod account_balance; pub mod block; pub mod current_block; pub mod current_frame; +pub mod endpoints; pub mod first_utxos_of_scripts; pub mod gen_tx; pub mod idty; @@ -33,6 +34,7 @@ pub struct QueryRoot( queries::block::BlockQuery, queries::current_block::CurrentBlockQuery, queries::current_frame::CurrentFrameQuery, + queries::endpoints::EndpointsQuery, queries::first_utxos_of_scripts::FirstUtxosQuery, queries::gen_tx::GenTxsQuery, queries::idty::IdtyQuery, diff --git a/rust-libs/modules/gva/gql/src/queries/endpoints.rs b/rust-libs/modules/gva/gql/src/queries/endpoints.rs new file mode 100644 index 0000000000000000000000000000000000000000..38dd6595a482363b1fe36adc2389c8ba7360309e --- /dev/null +++ b/rust-libs/modules/gva/gql/src/queries/endpoints.rs @@ -0,0 +1,80 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +use crate::*; + +// e2e requester pour obtenir la fiche de peer et un tableau de heads pour une pubKey donnée + +// e2e requester pour obtenir la list des endpoints connu filtrable par type (gva, bma, ws2p, es?data-pod) +// ? e2e list endpoints type +// ? renomer dunp_v1 en network_v1 & DunpV1Db & co + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::*; + use duniter_dbs::databases::dunp_v1::DunpV1Db; + + #[tokio::test] + async fn endpoints_gva_resolver() -> anyhow::Result<()> { + let mock_cm = MockAsyncAccessor::new(); + let mut mock_dbs_reader = MockDbsReader::new(); + mock_dbs_reader + .expect_endpoints::<DunpV1Db<FileBackend>>() + .times(1) + .returning(|_, _| { + Ok(vec![ + "GVA S g1.librelois.fr 443 gva".to_owned(), + "GVA S domain.tld 443 gva".to_owned(), + ]) + }); + let schema = create_schema(mock_cm, mock_dbs_reader)?; + assert_eq!( + exec_graphql_request(&schema, r#"{ endpoints(apiList:["GVA"]) }"#).await?, + serde_json::json!({ + "data": { + "endpoints": [ + "GVA S g1.librelois.fr 443 gva", + "GVA S domain.tld 443 gva" + ] + } + }) + ); + Ok(()) + } +} + +#[derive(Default)] +pub(crate) struct EndpointsQuery; +#[async_graphql::Object] +impl EndpointsQuery { + /// Get endpoints known by the node + async fn endpoints( + &self, + ctx: &async_graphql::Context<'_>, + #[graphql( + desc = "filter endpoints by api (exact match endpoint first word, case sensitive)" + )] + api_list: Vec<String>, + ) -> async_graphql::Result<Vec<String>> { + let data = ctx.data::<GvaSchemaData>()?; + let dbs_reader = data.dbs_reader(); + + Ok(data + .dbs_pool + .execute(move |dbs| dbs_reader.endpoints(&dbs.dunp_db, api_list)) + .await??) + } +}