diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5177bc91c4aac2ff10bed8d8975da55ea61f4889..5323b5f2863e6af92762d107b5dd1c1453f49457 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -100,8 +100,7 @@ tests:linux64:stable: script: - cd bin/dunitrust-server - RUSTFLAGS="-D warnings" cargo build --features=ssl - - cargo test --all --exclude durs-gva - - cargo test --package durs-gva -- --test-threads=1 + - cargo test --all - cargo test --all -- --ignored tests:arm-v7-:stable: @@ -142,7 +141,7 @@ tests:win64:stable: - cargo test --package durs-blockchain --target=x86_64-pc-windows-gnu - cargo test --package durs-dbs-tools --target=x86_64-pc-windows-gnu #- cargo test --package durs-skeleton-module --target=x86_64-pc-windows-gnu - - cargo test --package durs-gva --target=x86_64-pc-windows-gnu -- --test-threads=1 + - cargo test --package durs-gva --target=x86_64-pc-windows-gnu - cargo test --package durs-ws2p-v1-legacy --target=x86_64-pc-windows-gnu - cargo test --package durs-ws2p --target=x86_64-pc-windows-gnu - cargo test --package durs-ws2p-messages --target=x86_64-pc-windows-gnu diff --git a/Cargo.lock b/Cargo.lock index 2b5551845ef7fcb1b90eac95c7490173d0476893..687a33337d7c2095a90fc1e958ff6db2311dcf78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1032,6 +1032,7 @@ name = "durs-gva" version = "0.1.0" dependencies = [ "actix-web 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "dubp-block-doc 0.1.0", "dubp-blocks-tests-tools 0.1.0", diff --git a/lib/modules-lib/bc-db-reader/src/blocks.rs b/lib/modules-lib/bc-db-reader/src/blocks.rs index d23a149f941288fbbf4ac37b562c1a8a6bd851d4..2e2767225076f1f479d0f4f63097a1bd4f8fd0c2 100644 --- a/lib/modules-lib/bc-db-reader/src/blocks.rs +++ b/lib/modules-lib/bc-db-reader/src/blocks.rs @@ -185,6 +185,22 @@ pub fn get_blocks_in_local_blockchain<DB: DbReadable>( }) } +/// Get several blocks in local blockchain by their number +pub fn get_blocks_in_local_blockchain_by_numbers<DB: DbReadable, R: DbReader>( + db: &DB, + r: &R, + numbers: Vec<BlockNumber>, +) -> Result<Vec<DbBlock>, DbError> { + numbers + .into_iter() + .filter_map(|n| match get_db_block_in_local_blockchain(db, r, n) { + Ok(Some(db_block)) => Some(Ok(db_block)), + Ok(None) => None, + Err(e) => Some(Err(e)), + }) + .collect::<Result<Vec<DbBlock>, DbError>>() +} + /// Get current frame of calculating members pub fn get_current_frame<DB: DbReadable>( current_block: &BlockDocument, diff --git a/lib/modules-lib/bc-db-reader/src/trait.rs b/lib/modules-lib/bc-db-reader/src/trait.rs index 928f39998175b3671f1a34bd29b73e1f040e7f5d..f01d5693c978e49cfaf7ff4f3bbbfb508404764a 100644 --- a/lib/modules-lib/bc-db-reader/src/trait.rs +++ b/lib/modules-lib/bc-db-reader/src/trait.rs @@ -17,7 +17,7 @@ // ! Define read only trait use crate::blocks::DbBlock; -use crate::{BcDbRo, DbReadable, Reader}; +use crate::{BcDbRo, Reader}; use dubp_common_doc::{BlockNumber, Blockstamp}; use durs_dbs_tools::DbError; @@ -39,49 +39,6 @@ pub trait BcDbRoTrait { ) -> Result<Vec<DbBlock>, DbError>; } -impl BcDbRoTrait for BcDbRo { - #[inline] - fn get_current_blockstamp(&self) -> Result<Option<Blockstamp>, DbError> { - self.read(|r| crate::current_meta_datas::get_current_blockstamp_(self, r)) - } - fn get_current_block(&self) -> Result<Option<DbBlock>, DbError> { - self.read(|r| { - if let Some(current_blockstamp) = - crate::current_meta_datas::get_current_blockstamp_(self, r)? - { - crate::blocks::get_db_block_in_local_blockchain(self, r, current_blockstamp.id) - } else { - Ok(None) - } - }) - } - #[inline] - fn get_db_block_in_local_blockchain( - &self, - block_number: BlockNumber, - ) -> Result<Option<DbBlock>, DbError> { - self.read(|r| crate::blocks::get_db_block_in_local_blockchain(self, r, block_number)) - } - #[cfg(feature = "client-indexer")] - fn get_db_blocks_in_local_blockchain( - &self, - numbers: Vec<BlockNumber>, - ) -> Result<Vec<DbBlock>, DbError> { - self.read(|r| { - numbers - .into_iter() - .filter_map( - |n| match crate::blocks::get_db_block_in_local_blockchain(self, r, n) { - Ok(Some(db_block)) => Some(Ok(db_block)), - Ok(None) => None, - Err(e) => Some(Err(e)), - }, - ) - .collect::<Result<Vec<DbBlock>, DbError>>() - }) - } -} - pub struct BcDbRoWithReader<'r, 'db: 'r> { pub db: &'db BcDbRo, pub r: Reader<'r>, @@ -92,18 +49,24 @@ impl<'r, 'db: 'r> BcDbRoTrait for BcDbRoWithReader<'r, 'db> { crate::current_meta_datas::get_current_blockstamp_(self.db, self.r) } fn get_current_block(&self) -> Result<Option<DbBlock>, DbError> { - unimplemented!() + if let Some(current_blockstamp) = + crate::current_meta_datas::get_current_blockstamp_(self.db, self.r)? + { + crate::blocks::get_db_block_in_local_blockchain(self.db, self.r, current_blockstamp.id) + } else { + Ok(None) + } } fn get_db_block_in_local_blockchain( &self, - _block_number: BlockNumber, + block_number: BlockNumber, ) -> Result<Option<DbBlock>, DbError> { - unimplemented!() + crate::blocks::get_db_block_in_local_blockchain(self.db, self.r, block_number) } fn get_db_blocks_in_local_blockchain( &self, - _numbers: Vec<BlockNumber>, + numbers: Vec<BlockNumber>, ) -> Result<Vec<DbBlock>, DbError> { - unimplemented!() + crate::blocks::get_blocks_in_local_blockchain_by_numbers(self.db, self.r, numbers) } } diff --git a/lib/modules/gva/Cargo.toml b/lib/modules/gva/Cargo.toml index f141f1f6b751ad5982892e0b09cd94b21a85f873..e4a63861f3c14e8265198514fdc6bbb020b703a6 100644 --- a/lib/modules/gva/Cargo.toml +++ b/lib/modules/gva/Cargo.toml @@ -11,9 +11,10 @@ path = "src/lib.rs" [dependencies] actix-web = "1.0.9" +cfg-if = "0.1.10" dubp-block-doc = { path = "../../dubp/block-doc"} #, version = "0.1.0" } dup-crypto = { path = "../../crypto" } -durs-bc-db-reader = { path = "../../modules-lib/bc-db-reader" } +durs-bc-db-reader = { path = "../../modules-lib/bc-db-reader", features = ["client-indexer"] } durs-conf = { path = "../../core/conf" } durs-message = { path = "../../core/message" } durs-module = { path = "../../core/module" } @@ -34,6 +35,7 @@ serde_json = "1.0.41" structopt= "0.3.4" [dev-dependencies] +durs-bc-db-reader = { path = "../../modules-lib/bc-db-reader", features = ["client-indexer", "mock"] } dubp-blocks-tests-tools = { path = "../../tests-tools/blocks-tests-tools" } dup-crypto-tests-tools = { path = "../../tests-tools/crypto-tests-tools" } mockall = "0.5.2" diff --git a/lib/modules/gva/src/context.rs b/lib/modules/gva/src/context.rs index 4604812af79eafd6220341ae6b517a322cc4cb14..306f68903773001610d1327f08d748ae235b93c1 100644 --- a/lib/modules/gva/src/context.rs +++ b/lib/modules/gva/src/context.rs @@ -13,39 +13,54 @@ // 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/>. -#[cfg(not(test))] -use durs_bc_db_reader::BcDbRo; -use durs_common_tools::fatal_error; +//! Context for graphql resolvers -#[cfg(test)] -use crate::db::MockBcDbTrait; +use crate::db::BcDbRo; +use crate::schema::Schema; -/// GVA context (access to database) -static mut CONTEXT: Option<Context> = None; - -#[cfg(not(test))] -pub type DB = BcDbRo; -#[cfg(test)] -pub(crate) type DB = MockBcDbTrait; - -pub struct Context { - db: DB, +pub struct GlobalContext { + db: &'static BcDbRo, + pub(crate) schema: Schema, software_name: &'static str, software_version: &'static str, } -impl juniper::Context for Context {} - -impl Context { - pub(crate) fn new(db: DB, software_name: &'static str, software_version: &'static str) -> Self { - Context { +impl GlobalContext { + pub(crate) fn new( + db: &'static BcDbRo, + schema: Schema, + software_name: &'static str, + software_version: &'static str, + ) -> Self { + GlobalContext { db, + schema, software_name, software_version, } } +} + +pub struct QueryContext { + db: &'static BcDbRo, + software_name: &'static str, + software_version: &'static str, +} + +impl juniper::Context for QueryContext {} + +impl From<&GlobalContext> for QueryContext { + fn from(global_context: &GlobalContext) -> Self { + QueryContext { + db: global_context.db, + software_name: global_context.software_name, + software_version: global_context.software_version, + } + } +} - pub(crate) fn get_db(&self) -> &DB { +impl QueryContext { + pub(crate) fn get_db(&self) -> &BcDbRo { &self.db } @@ -57,19 +72,3 @@ impl Context { &self.software_version } } - -pub(crate) fn init(db: DB, soft_name: &'static str, soft_version: &'static str) { - unsafe { - CONTEXT.replace(Context::new(db, soft_name, soft_version)); - } -} - -pub fn get_context() -> &'static Context { - unsafe { - if let Some(ref context) = CONTEXT { - context - } else { - fatal_error!("GVA: no context"); - } - } -} diff --git a/lib/modules/gva/src/db.rs b/lib/modules/gva/src/db.rs index efbc0f057fbc40e20fd9acd35d7ca90720998bd3..1b492c0d07c0f0b8561185f96c7938ad54a7409f 100644 --- a/lib/modules/gva/src/db.rs +++ b/lib/modules/gva/src/db.rs @@ -15,75 +15,7 @@ //! Gva Module: database requests -pub use durs_bc_db_reader::DbError; - -use dubp_common_doc::{BlockNumber, Blockstamp}; -use durs_bc_db_reader::blocks::DbBlock; -use durs_bc_db_reader::{BcDbRo, DbReadable}; - -#[cfg(test)] -use mockall::predicate::*; +#[cfg(not(test))] +pub(crate) use durs_bc_db_reader::BcDbRo; #[cfg(test)] -use mockall::*; - -#[cfg_attr(test, automock)] -pub(crate) trait BcDbTrait { - fn get_current_blockstamp(&self) -> Result<Option<Blockstamp>, DbError>; - fn get_current_block(&self) -> Result<Option<DbBlock>, DbError>; - fn get_db_block_in_local_blockchain( - &self, - block_number: BlockNumber, - ) -> Result<Option<DbBlock>, DbError>; - fn get_db_blocks_in_local_blockchain( - &self, - numbers: Vec<BlockNumber>, - ) -> Result<Vec<DbBlock>, DbError>; -} - -impl<'a> BcDbTrait for BcDbRo { - #[inline] - fn get_current_blockstamp(&self) -> Result<Option<Blockstamp>, DbError> { - self.read(|r| durs_bc_db_reader::current_meta_datas::get_current_blockstamp_(self, r)) - } - fn get_current_block(&self) -> Result<Option<DbBlock>, DbError> { - self.read(|r| { - if let Some(current_blockstamp) = - durs_bc_db_reader::current_meta_datas::get_current_blockstamp_(self, r)? - { - durs_bc_db_reader::blocks::get_db_block_in_local_blockchain( - self, - r, - current_blockstamp.id, - ) - } else { - Ok(None) - } - }) - } - #[inline] - fn get_db_block_in_local_blockchain( - &self, - block_number: BlockNumber, - ) -> Result<Option<DbBlock>, DbError> { - self.read(|r| { - durs_bc_db_reader::blocks::get_db_block_in_local_blockchain(self, r, block_number) - }) - } - fn get_db_blocks_in_local_blockchain( - &self, - numbers: Vec<BlockNumber>, - ) -> Result<Vec<DbBlock>, DbError> { - self.read(|r| { - numbers - .into_iter() - .filter_map(|n| { - match durs_bc_db_reader::blocks::get_db_block_in_local_blockchain(self, r, n) { - Ok(Some(db_block)) => Some(Ok(db_block)), - Ok(None) => None, - Err(e) => Some(Err(e)), - } - }) - .collect::<Result<Vec<DbBlock>, DbError>>() - }) - } -} +pub(crate) use durs_bc_db_reader::MockBcDbRoTrait as BcDbRo; diff --git a/lib/modules/gva/src/graphql.rs b/lib/modules/gva/src/graphql.rs index b2fa335a752a6746e1948b7dc78431a8aa223067..3a58527fe55e95a0ac90117bebd0386263bf2040 100644 --- a/lib/modules/gva/src/graphql.rs +++ b/lib/modules/gva/src/graphql.rs @@ -16,19 +16,19 @@ //! Module that execute graphql queries -use crate::schema::Schema; +use crate::context::{GlobalContext, QueryContext}; use actix_web::{web, Error, HttpResponse}; use futures::future::Future; use juniper::http::GraphQLRequest; use std::sync::Arc; pub(crate) fn graphql( - schema: web::Data<Arc<Schema>>, + global_context: web::Data<Arc<GlobalContext>>, data: web::Json<GraphQLRequest>, ) -> impl Future<Item = HttpResponse, Error = Error> { - let context = crate::context::get_context(); + let query_context = QueryContext::from(global_context.as_ref()); web::block(move || { - let result = data.execute(&schema, context); + let result = data.execute(&global_context.schema, &query_context); serde_json::to_string(&result) }) .map_err(Error::from) diff --git a/lib/modules/gva/src/schema.rs b/lib/modules/gva/src/schema.rs index a2a7c0cb107c310ccb02b70632e3a51e7a65d690..92152e3ba56f51ba7a14fd11670faf59942152a7 100644 --- a/lib/modules/gva/src/schema.rs +++ b/lib/modules/gva/src/schema.rs @@ -21,13 +21,15 @@ mod queries; use self::entities::block::Block; use self::entities::node::{Node, Summary}; -use crate::context::Context; +use crate::context::QueryContext; +#[cfg(not(test))] +use durs_bc_db_reader::{BcDbRoWithReader, DbReadable}; use juniper::Executor; use juniper::FieldResult; use juniper_from_schema::graphql_schema_from_file; // generate schema from schema file -graphql_schema_from_file!("resources/schema.gql"); +graphql_schema_from_file!("resources/schema.gql", context_type: QueryContext); pub struct Query; @@ -35,32 +37,46 @@ impl QueryFields for Query { #[inline] fn field_node( &self, - executor: &Executor<'_, Context>, + executor: &Executor<'_, QueryContext>, trail: &QueryTrail<'_, Node, Walked>, ) -> FieldResult<Node> { - queries::node::execute(executor, trail) + queries::node::execute(executor.context(), trail) } #[inline] fn field_current( &self, - executor: &Executor<'_, Context>, + executor: &Executor<'_, QueryContext>, trail: &QueryTrail<'_, Block, Walked>, ) -> FieldResult<Option<Block>> { - queries::current::execute(executor, trail) + let db = executor.context().get_db(); + cfg_if::cfg_if! { + if #[cfg(not(test))] { + db.read(|r| queries::current::execute(&BcDbRoWithReader { db, r }, trail)).map_err(Into::into) + } else { + queries::current::execute(db, trail).map_err(Into::into) + } + } } #[inline] fn field_block( &self, - executor: &Executor<'_, Context>, + executor: &Executor<'_, QueryContext>, trail: &QueryTrail<'_, Block, Walked>, number: i32, ) -> FieldResult<Option<Block>> { - queries::block::execute(executor, trail, number) + let db = executor.context().get_db(); + cfg_if::cfg_if! { + if #[cfg(not(test))] { + db.read(|r| queries::block::execute(&BcDbRoWithReader { db, r }, trail, number)).map_err(Into::into) + } else { + queries::block::execute(db, trail, number).map_err(Into::into) + } + } } #[inline] fn field_blocks( &self, - executor: &Executor<'_, Context>, + executor: &Executor<'_, QueryContext>, trail: &QueryTrail<'_, Block, Walked>, block_interval_opt: Option<BlockInterval>, paging_opt: Option<Paging>, @@ -69,20 +85,37 @@ impl QueryFields for Query { if step <= 0 { step = 1; } - queries::blocks::execute( - executor, - trail, - paging_opt, - block_interval_opt, - step as usize, - ) + let db = executor.context().get_db(); + cfg_if::cfg_if! { + if #[cfg(not(test))] { + db.read(|r| { + queries::blocks::execute( + &BcDbRoWithReader { db, r }, + trail, + paging_opt, + block_interval_opt, + step as usize, + ) + }) + .map_err(Into::into) + } else { + queries::blocks::execute( + db, + trail, + paging_opt, + block_interval_opt, + step as usize, + ) + .map_err(Into::into) + } + } } } pub struct Mutation; impl MutationFields for Mutation { - fn field_noop(&self, _executor: &Executor<'_, Context>) -> FieldResult<&bool> { + fn field_noop(&self, _executor: &Executor<'_, QueryContext>) -> FieldResult<&bool> { Ok(&true) } } diff --git a/lib/modules/gva/src/schema/entities/block.rs b/lib/modules/gva/src/schema/entities/block.rs index 13aeb7a81d0c9353bc52ebe55b84a39f55796dbb..9053bcb7e4692d7f204e89c4985840ee7422f7f6 100644 --- a/lib/modules/gva/src/schema/entities/block.rs +++ b/lib/modules/gva/src/schema/entities/block.rs @@ -15,7 +15,7 @@ // ! Module define graphql Block type -use crate::context::Context; +use crate::context::QueryContext; use chrono::NaiveDateTime; use dubp_block_doc::block::BlockDocumentTrait; use dubp_common_doc::traits::Document; @@ -34,31 +34,34 @@ pub struct Block { } impl super::super::BlockFields for Block { - fn field_version(&self, _executor: &Executor<'_, Context>) -> FieldResult<&i32> { + fn field_version(&self, _executor: &Executor<'_, QueryContext>) -> FieldResult<&i32> { Ok(&self.version) } - fn field_currency(&self, _executor: &Executor<'_, Context>) -> FieldResult<&String> { + fn field_currency(&self, _executor: &Executor<'_, QueryContext>) -> FieldResult<&String> { Ok(&self.currency) } - fn field_issuer(&self, _executor: &Executor<'_, Context>) -> FieldResult<&String> { + fn field_issuer(&self, _executor: &Executor<'_, QueryContext>) -> FieldResult<&String> { Ok(&self.issuer) } - fn field_number(&self, _executor: &Executor<'_, Context>) -> FieldResult<&i32> { + fn field_number(&self, _executor: &Executor<'_, QueryContext>) -> FieldResult<&i32> { Ok(&self.number) } - fn field_hash(&self, _executor: &Executor<'_, Context>) -> FieldResult<&String> { + fn field_hash(&self, _executor: &Executor<'_, QueryContext>) -> FieldResult<&String> { Ok(&self.hash) } - fn field_common_time(&self, _executor: &Executor<'_, Context>) -> FieldResult<&NaiveDateTime> { + fn field_common_time( + &self, + _executor: &Executor<'_, QueryContext>, + ) -> FieldResult<&NaiveDateTime> { Ok(&self.common_time) } - fn field_pow_min(&self, _executor: &Executor<'_, Context>) -> FieldResult<&i32> { + fn field_pow_min(&self, _executor: &Executor<'_, QueryContext>) -> FieldResult<&i32> { Ok(&self.pow_min) } } diff --git a/lib/modules/gva/src/schema/entities/node.rs b/lib/modules/gva/src/schema/entities/node.rs index be7668bc595c7ce74ced7ac8e6b683ea6fb27a8f..06d786fa4b0a31dc83db3bc5b064469dd5ebef9b 100644 --- a/lib/modules/gva/src/schema/entities/node.rs +++ b/lib/modules/gva/src/schema/entities/node.rs @@ -15,7 +15,7 @@ // ! Module define graphql Node type and subtypes -use crate::context::Context; +use crate::context::QueryContext; use juniper::Executor; use juniper_from_schema::{QueryTrail, Walked}; @@ -31,7 +31,7 @@ pub struct Node { impl super::super::NodeFields for Node { fn field_summary( &self, - _executor: &Executor<'_, Context>, + _executor: &Executor<'_, QueryContext>, _trail: &QueryTrail<'_, Summary, Walked>, ) -> &Summary { &self.summary @@ -39,10 +39,10 @@ impl super::super::NodeFields for Node { } impl super::super::SummaryFields for Summary { - fn field_software(&self, _executor: &Executor<'_, Context>) -> String { + fn field_software(&self, _executor: &Executor<'_, QueryContext>) -> String { self.software.to_owned() } - fn field_version(&self, _executor: &Executor<'_, Context>) -> String { + fn field_version(&self, _executor: &Executor<'_, QueryContext>) -> String { self.version.to_owned() } } diff --git a/lib/modules/gva/src/schema/inputs/block_interval.rs b/lib/modules/gva/src/schema/inputs/block_interval.rs index 8b56aad86652711aa356249e04cd44237accc26c..15f75163ae95b5ea9bd98d26e4596b84bbc6f2c4 100644 --- a/lib/modules/gva/src/schema/inputs/block_interval.rs +++ b/lib/modules/gva/src/schema/inputs/block_interval.rs @@ -16,22 +16,21 @@ // ! BlockInterval input methods use super::super::BlockInterval; -use crate::db::BcDbTrait; -use durs_bc_db_reader::DbError; +use durs_bc_db_reader::{BcDbRoTrait, DbError}; use std::ops::RangeInclusive; const DEFAULT_START: usize = 0; const END_WHEN_EMPTY_BLOCKCHAIN: usize = 0; impl BlockInterval { - fn get_default_end<DB: BcDbTrait>(db: &DB) -> Result<usize, DbError> { + fn get_default_end<DB: BcDbRoTrait>(db: &DB) -> Result<usize, DbError> { if let Some(current_blockstamp) = db.get_current_blockstamp()? { Ok(current_blockstamp.id.0 as usize) } else { Ok(END_WHEN_EMPTY_BLOCKCHAIN) } } - pub(crate) fn get_range<DB: BcDbTrait>( + pub(crate) fn get_range<DB: BcDbRoTrait>( db: &DB, block_interval_opt: Option<BlockInterval>, ) -> Result<RangeInclusive<usize>, DbError> { @@ -68,12 +67,12 @@ impl BlockInterval { mod tests { use super::*; - use crate::db::MockBcDbTrait; + use crate::db::BcDbRo; use dubp_common_doc::{BlockHash, BlockNumber, Blockstamp}; #[test] fn test_block_interval_get_range_with_short_bc() -> Result<(), DbError> { - let mut mock_db = MockBcDbTrait::new(); + let mut mock_db = BcDbRo::new(); mock_db .expect_get_current_blockstamp() .times(1) @@ -92,7 +91,7 @@ mod tests { #[test] fn test_block_interval_get_range_with_long_bc() -> Result<(), DbError> { - let mut mock_db = MockBcDbTrait::new(); + let mut mock_db = BcDbRo::new(); mock_db .expect_get_current_blockstamp() .times(2) diff --git a/lib/modules/gva/src/schema/queries.rs b/lib/modules/gva/src/schema/queries.rs index 6a30b6653ae34f7f1d4fd398d3410b0ee490d66f..1cf29e5abe77dc05f26efa128b928951d173b422 100644 --- a/lib/modules/gva/src/schema/queries.rs +++ b/lib/modules/gva/src/schema/queries.rs @@ -20,19 +20,13 @@ pub mod blocks; pub mod current; pub mod node; -use durs_bc_db_reader::DbError; - -pub(crate) fn db_err_to_juniper_err(e: DbError) -> juniper::FieldError { - juniper::FieldError::from(format!("Db error: {:?}", e)) -} - #[cfg(test)] mod tests { - use crate::context; - use crate::db::MockBcDbTrait; + use crate::context::GlobalContext; + use crate::db::BcDbRo; use crate::graphql::graphql; - use crate::schema::{create_schema, Schema}; + use crate::schema::create_schema; use actix_web::dev::Body; use actix_web::http; use actix_web::test; @@ -42,19 +36,29 @@ mod tests { use std::str::FromStr; use std::sync::Arc; - pub(crate) fn setup(mock_db: MockBcDbTrait) -> web::Data<Arc<Schema>> { - context::init(mock_db, "soft_name", "soft_version"); + pub(crate) fn setup( + mock_db: BcDbRo, + db_container: &'static mut Option<BcDbRo>, + ) -> web::Data<Arc<GlobalContext>> { + // Give a static lifetime to the DB + let db = durs_common_tools::fns::r#static::to_static_ref(mock_db, db_container); - web::Data::new(std::sync::Arc::new(create_schema())) + // Init global context + web::Data::new(std::sync::Arc::new(GlobalContext::new( + db, + create_schema(), + "soft_name", + "soft_version", + ))) } pub(crate) fn test_gql_query( - schema: web::Data<Arc<Schema>>, + global_context: web::Data<Arc<GlobalContext>>, gql_query: &str, expected_response: serde_json::Value, ) { let resp = test::block_on(graphql( - schema, + global_context, web::Json(GraphQLRequest::new(gql_query.to_owned(), None, None)), )) .unwrap(); diff --git a/lib/modules/gva/src/schema/queries/block.rs b/lib/modules/gva/src/schema/queries/block.rs index 24ce10189cba8576315b0ee398a6004d5b1301d6..acb725b1a76f11122be8b09f34ba4d6ca3e72a14 100644 --- a/lib/modules/gva/src/schema/queries/block.rs +++ b/lib/modules/gva/src/schema/queries/block.rs @@ -15,37 +15,29 @@ // ! Module execute GraphQl schema block query -use super::db_err_to_juniper_err; -use crate::context::Context; -use crate::db::BcDbTrait; use crate::schema::entities::block::Block; use dubp_common_doc::BlockNumber; -use juniper::Executor; -use juniper::FieldResult; +use durs_bc_db_reader::{BcDbRoTrait, DbError}; use juniper_from_schema::{QueryTrail, Walked}; -pub(crate) fn execute( - executor: &Executor<'_, Context>, +pub(crate) fn execute<DB: BcDbRoTrait>( + db: &DB, _trail: &QueryTrail<'_, Block, Walked>, number: i32, -) -> FieldResult<Option<Block>> { +) -> Result<Option<Block>, DbError> { let block_number = if number >= 0 { BlockNumber(number as u32) } else { - return Err(juniper::FieldError::from("Block number must be positive.")); + BlockNumber(0) }; - executor - .context() - .get_db() - .get_db_block_in_local_blockchain(block_number) - .map_err(db_err_to_juniper_err) + db.get_db_block_in_local_blockchain(block_number) .map(|db_block_opt| db_block_opt.map(Into::into)) } #[cfg(test)] mod tests { - use crate::db::MockBcDbTrait; + use crate::db::BcDbRo; use crate::schema::queries::tests; use dubp_block_doc::block::BlockDocument; use dubp_blocks_tests_tools::mocks::gen_empty_timed_block_v10; @@ -56,9 +48,11 @@ mod tests { use mockall::predicate::eq; use serde_json::json; + static mut DB_BLOCK_1: Option<BcDbRo> = None; + #[test] fn test_graphql_block() { - let mut mock_db = MockBcDbTrait::new(); + let mut mock_db = BcDbRo::new(); mock_db .expect_get_db_block_in_local_blockchain() .with(eq(BlockNumber(42))) @@ -79,7 +73,7 @@ mod tests { })) }); - let schema = tests::setup(mock_db); + let schema = tests::setup(mock_db, unsafe { &mut DB_BLOCK_1 }); tests::test_gql_query( schema.clone(), diff --git a/lib/modules/gva/src/schema/queries/blocks.rs b/lib/modules/gva/src/schema/queries/blocks.rs index 5119963682bcf01ab1b66ad17b680bed1fecd0cf..be85e7fc2c393eeaec84b31d7e84938a003931d7 100644 --- a/lib/modules/gva/src/schema/queries/blocks.rs +++ b/lib/modules/gva/src/schema/queries/blocks.rs @@ -15,31 +15,24 @@ // ! Module execute GraphQl schema blocks query -use super::db_err_to_juniper_err; -use crate::context::Context; -use crate::db::BcDbTrait; use crate::schema::entities::block::Block; use crate::schema::inputs::paging::FilledPaging; use crate::schema::BlockInterval; use crate::schema::Paging; use dubp_common_doc::BlockNumber; use durs_bc_db_reader::blocks::DbBlock; -use juniper::Executor; -use juniper::FieldResult; +use durs_bc_db_reader::{BcDbRoTrait, DbError}; use juniper_from_schema::{QueryTrail, Walked}; -pub(crate) fn execute( - executor: &Executor<'_, Context>, +pub(crate) fn execute<DB: BcDbRoTrait>( + db: &DB, _trail: &QueryTrail<'_, Block, Walked>, paging_opt: Option<Paging>, block_interval_opt: Option<BlockInterval>, step: usize, -) -> FieldResult<Vec<Block>> { - let db = executor.context().get_db(); - +) -> Result<Vec<Block>, DbError> { // Get interval - let interval = - BlockInterval::get_range(db, block_interval_opt).map_err(db_err_to_juniper_err)?; + let interval = BlockInterval::get_range(db, block_interval_opt)?; // Get blocks numbers that respect filters let blocks_numbers: Vec<BlockNumber> = @@ -61,16 +54,14 @@ pub(crate) fn execute( .collect(); // Get blocks - let blocks: Vec<DbBlock> = db - .get_db_blocks_in_local_blockchain(blocks_numbers) - .map_err(db_err_to_juniper_err)?; + let blocks: Vec<DbBlock> = db.get_db_blocks_in_local_blockchain(blocks_numbers)?; Ok(blocks.into_iter().map(Into::into).collect()) } #[cfg(test)] mod tests { - use crate::db::MockBcDbTrait; + use crate::db::BcDbRo; use crate::schema::queries::tests; use dubp_block_doc::block::v10::BlockDocumentV10; use dubp_block_doc::block::BlockDocument; @@ -198,9 +189,11 @@ mod tests { }) } + static mut DB_TEST_BLOCKS_FROM_2: Option<BcDbRo> = None; + #[test] fn test_graphql_blocks_from_2() { - let mut mock_db = MockBcDbTrait::new(); + let mut mock_db = BcDbRo::new(); let block_2 = block_2(); let block_3 = block_3(); @@ -232,7 +225,7 @@ mod tests { ]) }); - let schema = tests::setup(mock_db); + let schema = tests::setup(mock_db, unsafe { &mut DB_TEST_BLOCKS_FROM_2 }); tests::test_gql_query( schema, @@ -249,9 +242,11 @@ mod tests { ); } + static mut DB_TEST_BLOCKS_STEP_2: Option<BcDbRo> = None; + #[test] fn test_graphql_blocks_with_step_2() { - let mut mock_db = MockBcDbTrait::new(); + let mut mock_db = BcDbRo::new(); let block_0 = block_0(); let current_block = block_2(); @@ -278,7 +273,7 @@ mod tests { ]) }); - let schema = tests::setup(mock_db); + let schema = tests::setup(mock_db, unsafe { &mut DB_TEST_BLOCKS_STEP_2 }); tests::test_gql_query( schema, @@ -306,9 +301,11 @@ mod tests { ); } + static mut DB_TEST_BLOCKS: Option<BcDbRo> = None; + #[test] fn test_graphql_blocks() { - let mut mock_db = MockBcDbTrait::new(); + let mut mock_db = BcDbRo::new(); let block_0 = block_0(); let block_1 = block_1(); @@ -340,7 +337,7 @@ mod tests { ]) }); - let schema = tests::setup(mock_db); + let schema = tests::setup(mock_db, unsafe { &mut DB_TEST_BLOCKS }); tests::test_gql_query( schema, diff --git a/lib/modules/gva/src/schema/queries/current.rs b/lib/modules/gva/src/schema/queries/current.rs index 076e702b68367f838527e31ac2b8e6932bd68cf3..6d70554efc6a579683dfc98d978c474884e1b007 100644 --- a/lib/modules/gva/src/schema/queries/current.rs +++ b/lib/modules/gva/src/schema/queries/current.rs @@ -15,29 +15,21 @@ // ! Module execute GraphQl schema current query -use super::db_err_to_juniper_err; -use crate::context::Context; -use crate::db::BcDbTrait; use crate::schema::entities::block::Block; -use juniper::Executor; -use juniper::FieldResult; +use durs_bc_db_reader::{BcDbRoTrait, DbError}; use juniper_from_schema::{QueryTrail, Walked}; -pub(crate) fn execute( - executor: &Executor<'_, Context>, +pub(crate) fn execute<DB: BcDbRoTrait>( + db: &DB, _trail: &QueryTrail<'_, Block, Walked>, -) -> FieldResult<Option<Block>> { - executor - .context() - .get_db() - .get_current_block() - .map_err(db_err_to_juniper_err) +) -> Result<Option<Block>, DbError> { + db.get_current_block() .map(|db_block_opt| db_block_opt.map(Into::into)) } #[cfg(test)] mod tests { - use crate::db::MockBcDbTrait; + use crate::db::BcDbRo; use crate::schema::queries::tests; use dubp_block_doc::block::BlockDocument; use dubp_blocks_tests_tools::mocks::gen_empty_timed_block_v10; @@ -47,9 +39,11 @@ mod tests { use durs_bc_db_reader::blocks::DbBlock; use serde_json::json; + static mut DB_TEST_CURRENT_1: Option<BcDbRo> = None; + #[test] fn test_graphql_current() { - let mut mock_db = MockBcDbTrait::new(); + let mut mock_db = BcDbRo::new(); mock_db.expect_get_current_block().returning(|| { let mut current_block = gen_empty_timed_block_v10( Blockstamp { @@ -67,7 +61,7 @@ mod tests { })) }); - let schema = tests::setup(mock_db); + let schema = tests::setup(mock_db, unsafe { &mut DB_TEST_CURRENT_1 }); tests::test_gql_query( schema, diff --git a/lib/modules/gva/src/schema/queries/node.rs b/lib/modules/gva/src/schema/queries/node.rs index b9b797231c327df91b5e501e9dd1c4cb02f585ba..c57bd1d715b27d385c100cffc118c9ddcab74b52 100644 --- a/lib/modules/gva/src/schema/queries/node.rs +++ b/lib/modules/gva/src/schema/queries/node.rs @@ -15,33 +15,34 @@ // ! Module execute GraphQl schema node query -use crate::context::Context; +use crate::context::QueryContext; use crate::schema::entities::node::{Node, Summary}; -use juniper::Executor; use juniper::FieldResult; use juniper_from_schema::{QueryTrail, Walked}; pub(crate) fn execute( - executor: &Executor<'_, Context>, + context: &QueryContext, _trail: &QueryTrail<'_, Node, Walked>, ) -> FieldResult<Node> { Ok(Node { summary: Summary { - software: executor.context().get_software_name(), - version: executor.context().get_software_version(), + software: context.get_software_name(), + version: context.get_software_version(), }, }) } #[cfg(test)] mod tests { - use crate::db::MockBcDbTrait; + use crate::db::BcDbRo; use crate::schema::queries::tests; use serde_json::json; + static mut DB_TEST_NODE_SUMMARY: Option<BcDbRo> = None; + #[test] - fn test_graphql_current() { - let schema = tests::setup(MockBcDbTrait::new()); + fn test_graphql_node_summary() { + let schema = tests::setup(BcDbRo::new(), unsafe { &mut DB_TEST_NODE_SUMMARY }); tests::test_gql_query( schema, diff --git a/lib/modules/gva/src/webserver.rs b/lib/modules/gva/src/webserver.rs index 20b949eef1188be8b462777f80f6c8d1bcea4375..da7ff2bf5e21b65a9c99fb06630c717ba1a54fca 100644 --- a/lib/modules/gva/src/webserver.rs +++ b/lib/modules/gva/src/webserver.rs @@ -14,7 +14,8 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. // web server implementaion based on actix-web -use crate::context; +use crate::context::GlobalContext; +use crate::db::BcDbRo; use crate::graphql::graphql; use crate::schema::create_schema; use actix_web::{middleware, web, App, HttpResponse, HttpServer}; @@ -27,8 +28,8 @@ use durs_network_documents::url::Url; use juniper::http::graphiql::graphiql_source; use std::net::SocketAddr; -#[cfg(test)] -use crate::db::MockBcDbTrait; +/// Database readonly handler (access to database) +static mut DB_RO_HANDLER: Option<BcDbRo> = None; fn graphiql() -> HttpResponse { let html = graphiql_source("/graphql"); @@ -44,12 +45,10 @@ pub fn start_web_server( ) -> std::io::Result<()> { info!("GVA web server start..."); + // Define listen addrs let addrs: Vec<SocketAddr> = Url::from_host_port_path(host, port, None).to_listenable_addr("http")?; - // Create Juniper schema - let schema = std::sync::Arc::new(create_schema()); - // Get DB #[cfg(not(test))] let db = { @@ -61,15 +60,23 @@ pub fn start_web_server( } }; #[cfg(test)] - let db = MockBcDbTrait::new(); + let db = BcDbRo::new(); + + // Give a static lifetime to the DB + let db = durs_common_tools::fns::r#static::to_static_ref(db, unsafe { &mut DB_RO_HANDLER }); - // Instanciate the context - context::init(db, soft_meta_datas.soft_name, soft_meta_datas.soft_version); + // Create global context + let global_context = std::sync::Arc::new(GlobalContext::new( + db, + create_schema(), + soft_meta_datas.soft_name, + soft_meta_datas.soft_version, + )); // Start http server HttpServer::new(move || { App::new() - .data(schema.clone()) + .data(global_context.clone()) .wrap(middleware::Logger::default()) .service(web::resource("/graphql").route(web::post().to_async(graphql))) .service(web::resource("/graphiql").route(web::get().to(graphiql))) diff --git a/lib/tools/common-tools/src/fns/mod.rs b/lib/tools/common-tools/src/fns/mod.rs index 572bed996ff9f4978e54ba8dca38254e068e9130..989587004c15e31986e3837ac4a8580638e29377 100644 --- a/lib/tools/common-tools/src/fns/mod.rs +++ b/lib/tools/common-tools/src/fns/mod.rs @@ -18,5 +18,6 @@ pub mod _u64; pub mod arrays; pub mod bin_file; +pub mod r#static; pub mod str_escape; pub mod time; diff --git a/lib/tools/common-tools/src/fns/static.rs b/lib/tools/common-tools/src/fns/static.rs new file mode 100644 index 0000000000000000000000000000000000000000..f262dd152c7d5ea17ec92081076dd77441212708 --- /dev/null +++ b/lib/tools/common-tools/src/fns/static.rs @@ -0,0 +1,26 @@ +// Copyright (C) 2019 É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/>. + +//! Common rust functions for static lifetime. + +/// Transforms any object into a static reference to that object +pub fn to_static_ref<T>(value: T, container: &'static mut Option<T>) -> &'static T { + container.replace(value); + if let Some(ref value) = container { + value + } else { + unreachable!() + } +}