From 10d6985d76d30fcd3695f8bbaf50c596c096ce76 Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Tue, 19 Nov 2019 00:52:59 +0100 Subject: [PATCH] [feat] gva: blocks: add inputs interval and step & add negative paging --- lib/modules/gva/resources/schema.gql | 11 +- lib/modules/gva/src/db.rs | 18 +- lib/modules/gva/src/schema.rs | 15 +- lib/modules/gva/src/schema/entities/block.rs | 4 +- lib/modules/gva/src/schema/inputs.rs | 19 ++ .../gva/src/schema/inputs/block_interval.rs | 129 +++++++++ lib/modules/gva/src/schema/inputs/paging.rs | 242 ++++++++++++++++ lib/modules/gva/src/schema/paging.rs | 195 ------------- lib/modules/gva/src/schema/queries/block.rs | 2 +- lib/modules/gva/src/schema/queries/blocks.rs | 268 +++++++++++++++--- lib/modules/gva/src/schema/queries/current.rs | 2 +- 11 files changed, 651 insertions(+), 254 deletions(-) create mode 100644 lib/modules/gva/src/schema/inputs.rs create mode 100644 lib/modules/gva/src/schema/inputs/block_interval.rs create mode 100644 lib/modules/gva/src/schema/inputs/paging.rs delete mode 100644 lib/modules/gva/src/schema/paging.rs diff --git a/lib/modules/gva/resources/schema.gql b/lib/modules/gva/resources/schema.gql index 163060c7..ed35e883 100644 --- a/lib/modules/gva/resources/schema.gql +++ b/lib/modules/gva/resources/schema.gql @@ -8,7 +8,7 @@ type Query { node: Node! @juniper(ownership: "owned") current: Block @juniper(ownership: "owned") block(number: Int!): Block @juniper(ownership: "owned") - blocks(paging: Paging): [Block!]! @juniper(ownership: "owned") + blocks(interval: BlockInterval, paging: Paging, step: Int = 1): [Block!]! @juniper(ownership: "owned") } type Mutation { @@ -19,12 +19,15 @@ type Mutation { # Inputs ################################# +input BlockInterval { + from: Int # default value: 0 + # If toBlock is null, current block number is used + to: Int +} + input Paging { pageNumber: Int # default value: 0 pageSize: Int # default value: 50 - fromBlock: Int # default value: 0 - # If toBlock is null, current block number is used - toBlock: Int } ################################# diff --git a/lib/modules/gva/src/db.rs b/lib/modules/gva/src/db.rs index 9fcfac64..efbc0f05 100644 --- a/lib/modules/gva/src/db.rs +++ b/lib/modules/gva/src/db.rs @@ -20,7 +20,6 @@ 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}; -use std::ops::Range; #[cfg(test)] use mockall::predicate::*; @@ -35,8 +34,10 @@ pub(crate) trait BcDbTrait { &self, block_number: BlockNumber, ) -> Result<Option<DbBlock>, DbError>; - fn get_db_blocks_in_local_blockchain(&self, range: Range<u32>) - -> Result<Vec<DbBlock>, DbError>; + fn get_db_blocks_in_local_blockchain( + &self, + numbers: Vec<BlockNumber>, + ) -> Result<Vec<DbBlock>, DbError>; } impl<'a> BcDbTrait for BcDbRo { @@ -70,16 +71,13 @@ impl<'a> BcDbTrait for BcDbRo { } fn get_db_blocks_in_local_blockchain( &self, - range: Range<u32>, + numbers: Vec<BlockNumber>, ) -> Result<Vec<DbBlock>, DbError> { self.read(|r| { - range + numbers + .into_iter() .filter_map(|n| { - match durs_bc_db_reader::blocks::get_db_block_in_local_blockchain( - self, - r, - BlockNumber(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)), diff --git a/lib/modules/gva/src/schema.rs b/lib/modules/gva/src/schema.rs index 18bc5550..a2a7c0cb 100644 --- a/lib/modules/gva/src/schema.rs +++ b/lib/modules/gva/src/schema.rs @@ -16,7 +16,7 @@ // ! Module define GraphQl schema mod entities; -mod paging; +pub mod inputs; mod queries; use self::entities::block::Block; @@ -62,9 +62,20 @@ impl QueryFields for Query { &self, executor: &Executor<'_, Context>, trail: &QueryTrail<'_, Block, Walked>, + block_interval_opt: Option<BlockInterval>, paging_opt: Option<Paging>, + mut step: i32, ) -> FieldResult<Vec<Block>> { - queries::blocks::execute(executor, trail, paging_opt) + if step <= 0 { + step = 1; + } + queries::blocks::execute( + executor, + trail, + paging_opt, + block_interval_opt, + step as usize, + ) } } diff --git a/lib/modules/gva/src/schema/entities/block.rs b/lib/modules/gva/src/schema/entities/block.rs index fee7d248..13aeb7a8 100644 --- a/lib/modules/gva/src/schema/entities/block.rs +++ b/lib/modules/gva/src/schema/entities/block.rs @@ -63,8 +63,8 @@ impl super::super::BlockFields for Block { } } -impl Block { - pub fn from_db_block(db_block: DbBlock) -> Block { +impl From<DbBlock> for Block { + fn from(db_block: DbBlock) -> Block { Block { version: db_block.block.version() as i32, currency: db_block.block.currency().to_string(), diff --git a/lib/modules/gva/src/schema/inputs.rs b/lib/modules/gva/src/schema/inputs.rs new file mode 100644 index 00000000..4fb601be --- /dev/null +++ b/lib/modules/gva/src/schema/inputs.rs @@ -0,0 +1,19 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// 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/>. + +// ! Schema inputs methods + +mod block_interval; +pub mod paging; diff --git a/lib/modules/gva/src/schema/inputs/block_interval.rs b/lib/modules/gva/src/schema/inputs/block_interval.rs new file mode 100644 index 00000000..8b56aad8 --- /dev/null +++ b/lib/modules/gva/src/schema/inputs/block_interval.rs @@ -0,0 +1,129 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// 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/>. + +// ! BlockInterval input methods + +use super::super::BlockInterval; +use crate::db::BcDbTrait; +use durs_bc_db_reader::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> { + 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>( + db: &DB, + block_interval_opt: Option<BlockInterval>, + ) -> Result<RangeInclusive<usize>, DbError> { + if let Some(block_interval) = block_interval_opt { + let start = if let Some(from) = block_interval.from { + if from.is_negative() { + 0 + } else { + from as usize + } + } else { + DEFAULT_START + }; + let mut end = if let Some(to) = block_interval.to { + if to.is_negative() { + 0 + } else { + to as usize + } + } else { + Self::get_default_end(db)? + }; + if start > end { + end = start; + } + Ok(start..=end) + } else { + Ok(DEFAULT_START..=Self::get_default_end(db)?) + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::db::MockBcDbTrait; + 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(); + mock_db + .expect_get_current_blockstamp() + .times(1) + .returning(|| { + Ok(Some(Blockstamp { + id: BlockNumber(42), + hash: BlockHash(dup_crypto::hashs::Hash::default()), + })) + }); + assert_eq! { + 0..=42, + BlockInterval::get_range(&mock_db, None)? + } + Ok(()) + } + + #[test] + fn test_block_interval_get_range_with_long_bc() -> Result<(), DbError> { + let mut mock_db = MockBcDbTrait::new(); + mock_db + .expect_get_current_blockstamp() + .times(2) + .returning(|| { + Ok(Some(Blockstamp { + id: BlockNumber(750), + hash: BlockHash(dup_crypto::hashs::Hash::default()), + })) + }); + + assert_eq! { + 0..=750, + BlockInterval::get_range(&mock_db, None)? + } + + assert_eq! { + 500..=750, + BlockInterval::get_range(&mock_db, Some(BlockInterval { + from: Some(500), + to: None, + }))? + } + + assert_eq! { + 500..=700, + BlockInterval::get_range(&mock_db, Some(BlockInterval { + from: Some(500), + to: Some(700), + }))? + } + + Ok(()) + } +} diff --git a/lib/modules/gva/src/schema/inputs/paging.rs b/lib/modules/gva/src/schema/inputs/paging.rs new file mode 100644 index 00000000..7642c1a3 --- /dev/null +++ b/lib/modules/gva/src/schema/inputs/paging.rs @@ -0,0 +1,242 @@ +// Copyright (C) 2017-2019 The AXIOM TEAM Association. +// +// 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/>. + +// ! Paging input methods + +use super::super::Paging; +use std::ops::Range; + +const DEFAULT_PAGE_NUMBER_I32: i32 = 0; +const DEFAULT_PAGE_NUMBER: isize = 0; +const DEFAULT_PAGE_SIZE: usize = 50; + +const MIN_PAGE_SIZE: i32 = 1; +const MAX_PAGE_SIZE: i32 = 500; + +#[derive(Debug, PartialEq)] +pub struct FilledPaging { + pub page_number: isize, + pub page_size: usize, +} + +impl Default for FilledPaging { + fn default() -> Self { + FilledPaging { + page_number: DEFAULT_PAGE_NUMBER, + page_size: DEFAULT_PAGE_SIZE, + } + } +} + +impl From<Option<Paging>> for FilledPaging { + fn from(paging_opt: Option<Paging>) -> Self { + if let Some(paging) = paging_opt { + FilledPaging { + page_number: paging.page_number.unwrap_or(DEFAULT_PAGE_NUMBER_I32) as isize, + page_size: if let Some(page_size) = paging.page_size { + if page_size < MIN_PAGE_SIZE { + MIN_PAGE_SIZE as usize + } else if page_size > MAX_PAGE_SIZE { + MAX_PAGE_SIZE as usize + } else { + page_size as usize + } + } else { + DEFAULT_PAGE_SIZE + }, + } + } else { + FilledPaging::default() + } + } +} + +impl FilledPaging { + pub(crate) fn get_page_range(&self, count_elems: usize, step: usize) -> Range<usize> { + let page_extended_size = self.page_size * step; + let mut count_pages = count_elems / page_extended_size; + if count_elems % page_extended_size > 0 { + count_pages += 1; + } + let page_number = if self.page_number.is_negative() { + std::cmp::max(0, count_pages as isize - self.page_number.abs()) as usize + } else { + self.page_number as usize + }; + + Range { + start: std::cmp::min(count_elems, page_number * page_extended_size), + end: std::cmp::min(count_elems, (page_number + 1) * page_extended_size), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_default() { + assert_eq!( + FilledPaging { + page_number: DEFAULT_PAGE_NUMBER, + page_size: DEFAULT_PAGE_SIZE, + }, + FilledPaging::default(), + ) + } + + #[test] + fn test_from_none_paging() { + assert_eq!( + FilledPaging { + page_number: DEFAULT_PAGE_NUMBER, + page_size: DEFAULT_PAGE_SIZE, + }, + FilledPaging::from(None), + ) + } + + #[test] + fn test_from_some_paging() { + assert_eq!( + FilledPaging { + page_number: 0, + page_size: 10, + }, + FilledPaging::from(Some(Paging { + page_number: None, + page_size: Some(10) + })), + ); + assert_eq!( + FilledPaging { + page_number: 1, + page_size: 50, + }, + FilledPaging::from(Some(Paging { + page_number: Some(1), + page_size: None + })), + ); + assert_eq!( + FilledPaging { + page_number: 1, + page_size: 10, + }, + FilledPaging::from(Some(Paging { + page_number: Some(1), + page_size: Some(10) + })), + ) + } + + #[test] + fn test_get_page_range() { + assert_eq!( + Range { start: 10, end: 20 }, + FilledPaging { + page_number: 1, + page_size: 10, + } + .get_page_range(5_000, 1), + ); + assert_eq!( + Range { + start: 4_980, + end: 4_990 + }, + FilledPaging { + page_number: -2, + page_size: 10, + } + .get_page_range(5_000, 1), + ); + assert_eq!( + Range { start: 10, end: 15 }, + FilledPaging { + page_number: 1, + page_size: 10, + } + .get_page_range(15, 1), + ); + assert_eq!( + Range { start: 15, end: 15 }, + FilledPaging { + page_number: 2, + page_size: 10, + } + .get_page_range(15, 1), + ); + assert_eq!( + Range { start: 20, end: 40 }, + FilledPaging { + page_number: 1, + page_size: 10, + } + .get_page_range(5_000, 2), + ); + assert_eq!( + Range { + start: 4_980, + end: 5_000 + }, + FilledPaging { + page_number: -1, + page_size: 10, + } + .get_page_range(5_000, 2), + ); + assert_eq!( + Range { start: 0, end: 400 }, + FilledPaging { + page_number: -1, + page_size: 500, + } + .get_page_range(400, 2), + ); + assert_eq!( + Range { + start: 0, + end: 1_000 + }, + FilledPaging { + page_number: -3, + page_size: 400, + } + .get_page_range(1_000, 5), + ); + assert_eq!( + Range { + start: 2_000, + end: 3_000 + }, + FilledPaging { + page_number: -1, + page_size: 400, + } + .get_page_range(3_000, 5), + ); + assert_eq!( + Range { start: 40, end: 80 }, + FilledPaging { + page_number: -2, + page_size: 40, + } + .get_page_range(100, 1), + ); + } +} diff --git a/lib/modules/gva/src/schema/paging.rs b/lib/modules/gva/src/schema/paging.rs deleted file mode 100644 index 4b85efa8..00000000 --- a/lib/modules/gva/src/schema/paging.rs +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright (C) 2017-2019 The AXIOM TEAM Association. -// -// 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/>. - -// ! Schema paging input - -use super::Paging; -use crate::db::BcDbTrait; -use durs_bc_db_reader::DbError; -use std::ops::Range; - -const DEFAULT_PAGE_NUMBER: i32 = 0; -const DEFAULT_PAGE_SIZE: i32 = 50; -const DEFAULT_FROM_BLOCK: i32 = 0; - -const MAX_PAGE_SIZE: i32 = 500; - -/// Paging with all values filled in -pub struct FilledPaging { - page_number: usize, - page_size: usize, - from_block: u32, - to_block: u32, -} - -#[inline] -fn i32_opt_to_positive_i32(int_opt: Option<i32>, default: i32) -> i32 { - if let Some(int) = int_opt { - if int < 0 { - 0 - } else { - int - } - } else { - default - } -} - -impl FilledPaging { - pub(crate) fn new<DB: BcDbTrait>(db: &DB, paging_opt: Option<Paging>) -> Result<Self, DbError> { - if let Some(paging) = paging_opt { - Ok(FilledPaging { - page_number: i32_opt_to_positive_i32(paging.page_number, DEFAULT_PAGE_NUMBER) - as usize, - page_size: std::cmp::min( - MAX_PAGE_SIZE, - i32_opt_to_positive_i32(paging.page_size, DEFAULT_PAGE_SIZE), - ) as usize, - from_block: i32_opt_to_positive_i32(paging.from_block, DEFAULT_FROM_BLOCK) as u32, - to_block: if let Some(to_block) = paging.to_block { - if to_block < 0 { - 0 - } else { - to_block as u32 - } - } else { - Self::get_default_to_block(db)? - }, - }) - } else { - Ok(FilledPaging { - page_number: DEFAULT_PAGE_NUMBER as usize, - page_size: DEFAULT_PAGE_SIZE as usize, - from_block: DEFAULT_FROM_BLOCK as u32, - to_block: Self::get_default_to_block(db)?, - }) - } - } - fn get_default_to_block<DB: BcDbTrait>(db: &DB) -> Result<u32, DbError> { - if let Some(current_blockstamp) = db.get_current_blockstamp()? { - Ok(current_blockstamp.id.0) - } else { - Ok(0) - } - } - pub fn get_range(&self) -> Range<u32> { - Range { - start: self.from_block + (self.page_number * self.page_size) as u32, - end: std::cmp::min( - self.to_block + 1, - self.from_block + ((self.page_number + 1) * self.page_size) as u32, - ), - } - } -} - -#[cfg(test)] -mod tests { - - use super::*; - use crate::db::MockBcDbTrait; - use dubp_common_doc::{BlockHash, BlockNumber, Blockstamp}; - - #[test] - fn test_i32_opt_to_positive_i32() { - assert_eq!(3, i32_opt_to_positive_i32(Some(3), 1)); - assert_eq!(0, i32_opt_to_positive_i32(Some(-2), 1)); - assert_eq!(50, i32_opt_to_positive_i32(None, 50)); - assert_eq!(0, i32_opt_to_positive_i32(Some(0), 1)); - } - - #[test] - fn test_filled_paging_range_with_short_bc() -> Result<(), DbError> { - let mut mock_db = MockBcDbTrait::new(); - mock_db - .expect_get_current_blockstamp() - .times(1) - .returning(|| { - Ok(Some(Blockstamp { - id: BlockNumber(42), - hash: BlockHash(dup_crypto::hashs::Hash::default()), - })) - }); - - let filled_paging = FilledPaging::new(&mock_db, None)?; - assert_eq! { - Range { - start: 0, - end: 43, - }, - filled_paging.get_range() - } - Ok(()) - } - - #[test] - fn test_filled_paging_range() -> Result<(), DbError> { - let mut mock_db = MockBcDbTrait::new(); - mock_db - .expect_get_current_blockstamp() - .times(3) - .returning(|| { - Ok(Some(Blockstamp { - id: BlockNumber(750), - hash: BlockHash(dup_crypto::hashs::Hash::default()), - })) - }); - - let filled_paging = FilledPaging::new(&mock_db, None)?; - assert_eq! { - Range { - start: 0, - end: 50, - }, - filled_paging.get_range() - } - - let filled_paging = FilledPaging::new( - &mock_db, - Some(Paging { - page_number: Some(1), - page_size: Some(100), - from_block: Some(500), - to_block: None, - }), - )?; - assert_eq! { - Range { - start: 600, - end: 700, - }, - filled_paging.get_range() - } - - let filled_paging = FilledPaging::new( - &mock_db, - Some(Paging { - page_number: Some(2), - page_size: Some(100), - from_block: Some(500), - to_block: None, - }), - )?; - assert_eq! { - Range { - start: 700, - end: 751, - }, - filled_paging.get_range() - } - - Ok(()) - } -} diff --git a/lib/modules/gva/src/schema/queries/block.rs b/lib/modules/gva/src/schema/queries/block.rs index 4ad02dd8..24ce1018 100644 --- a/lib/modules/gva/src/schema/queries/block.rs +++ b/lib/modules/gva/src/schema/queries/block.rs @@ -40,7 +40,7 @@ pub(crate) fn execute( .get_db() .get_db_block_in_local_blockchain(block_number) .map_err(db_err_to_juniper_err) - .map(|db_block_opt| db_block_opt.map(Block::from_db_block)) + .map(|db_block_opt| db_block_opt.map(Into::into)) } #[cfg(test)] diff --git a/lib/modules/gva/src/schema/queries/blocks.rs b/lib/modules/gva/src/schema/queries/blocks.rs index fa88927b..51199636 100644 --- a/lib/modules/gva/src/schema/queries/blocks.rs +++ b/lib/modules/gva/src/schema/queries/blocks.rs @@ -19,8 +19,10 @@ use super::db_err_to_juniper_err; use crate::context::Context; use crate::db::BcDbTrait; use crate::schema::entities::block::Block; -use crate::schema::paging; +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; @@ -30,37 +32,47 @@ pub(crate) fn execute( executor: &Executor<'_, Context>, _trail: &QueryTrail<'_, Block, Walked>, paging_opt: Option<Paging>, + block_interval_opt: Option<BlockInterval>, + step: usize, ) -> FieldResult<Vec<Block>> { let db = executor.context().get_db(); + // Get interval + let interval = + BlockInterval::get_range(db, block_interval_opt).map_err(db_err_to_juniper_err)?; + + // Get blocks numbers that respect filters + let blocks_numbers: Vec<BlockNumber> = + interval.clone().map(|i| BlockNumber(i as u32)).collect(); // TODO + + // Apply interval + let blocks_numbers: Vec<BlockNumber> = blocks_numbers + .into_iter() + .filter(|n| interval.contains(&(n.0 as usize))) + .collect(); + let count = blocks_numbers.len(); + + // Apply paging and step + let paging = FilledPaging::from(paging_opt); + let page_range = paging.get_page_range(count, step); + let blocks_numbers: Vec<BlockNumber> = page_range + .step_by(step) + .map(|i| blocks_numbers[i]) + .collect(); + + // Get blocks let blocks: Vec<DbBlock> = db - .get_db_blocks_in_local_blockchain( - paging::FilledPaging::new(db, paging_opt) - .map_err(db_err_to_juniper_err)? - .get_range(), - ) + .get_db_blocks_in_local_blockchain(blocks_numbers) .map_err(db_err_to_juniper_err)?; - Ok(blocks.into_iter().map(Block::from_db_block).collect()) - - /*let db: &BcDbRo = &executor.context().get_db(); - db.read(|r| { - paging::FilledPaging::new(db, paging_opt)? - .get_range() - .filter_map(|n| match block::get_block(db, r, BlockNumber(n)) { - Ok(Some(db_block)) => Some(Ok(db_block)), - Ok(None) => None, - Err(e) => Some(Err(e)), - }) - .collect::<Result<Vec<Block>, DbError>>() - }) - .map_err(db_err_to_juniper_err)*/ + Ok(blocks.into_iter().map(Into::into).collect()) } #[cfg(test)] mod tests { use crate::db::MockBcDbTrait; use crate::schema::queries::tests; + use dubp_block_doc::block::v10::BlockDocumentV10; use dubp_block_doc::block::BlockDocument; use dubp_blocks_tests_tools::mocks::gen_empty_timed_block_v10; use dubp_common_doc::traits::Document; @@ -70,12 +82,8 @@ mod tests { use durs_bc_db_reader::blocks::DbBlock; use mockall::predicate::eq; use serde_json::json; - use std::ops::Range; - - #[test] - fn test_graphql_blocks() { - let mut mock_db = MockBcDbTrait::new(); + fn block_0() -> BlockDocumentV10 { let mut block_0 = gen_empty_timed_block_v10( Blockstamp { id: BlockNumber(0), @@ -85,6 +93,20 @@ mod tests { Hash::default(), ); block_0.issuers = vec![pubkey('A')]; + block_0 + } + fn block_0_json() -> serde_json::Value { + json!({ + "commonTime": 1_488_987_127.0, + "currency": "test_currency", + "hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "issuer": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "number": 0, + "version": 10 + }) + } + + fn block_1() -> BlockDocumentV10 { let mut block_1 = gen_empty_timed_block_v10( Blockstamp { id: BlockNumber(1), @@ -94,7 +116,21 @@ mod tests { Hash::default(), ); block_1.issuers = vec![pubkey('B')]; - let mut current_block = gen_empty_timed_block_v10( + block_1 + } + fn block_1_json() -> serde_json::Value { + json!({ + "commonTime": 1_488_987_128.0, + "currency": "test_currency", + "hash": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", + "issuer": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", + "number": 1, + "version": 10 + }) + } + + fn block_2() -> BlockDocumentV10 { + let mut block_2 = gen_empty_timed_block_v10( Blockstamp { id: BlockNumber(2), hash: BlockHash(hash('C')), @@ -102,7 +138,73 @@ mod tests { 1_488_987_129, Hash::default(), ); - current_block.issuers = vec![pubkey('C')]; + block_2.issuers = vec![pubkey('C')]; + block_2 + } + fn block_2_json() -> serde_json::Value { + json!({ + "commonTime": 1_488_987_129.0, + "currency": "test_currency", + "hash": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "issuer": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", + "number": 2, + "version": 10 + }) + } + + fn block_3() -> BlockDocumentV10 { + let mut block_3 = gen_empty_timed_block_v10( + Blockstamp { + id: BlockNumber(3), + hash: BlockHash(hash('D')), + }, + 1_488_987_130, + Hash::default(), + ); + block_3.issuers = vec![pubkey('D')]; + block_3 + } + fn block_3_json() -> serde_json::Value { + json!({ + "commonTime": 1_488_987_130.0, + "currency": "test_currency", + "hash": "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD", + "issuer": "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD", + "number": 3, + "version": 10 + }) + } + + fn block_4() -> BlockDocumentV10 { + let mut block_4 = gen_empty_timed_block_v10( + Blockstamp { + id: BlockNumber(4), + hash: BlockHash(hash('E')), + }, + 1_488_987_131, + Hash::default(), + ); + block_4.issuers = vec![pubkey('E')]; + block_4 + } + fn block_4_json() -> serde_json::Value { + json!({ + "commonTime": 1_488_987_131.0, + "currency": "test_currency", + "hash": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", + "issuer": "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", + "number": 4, + "version": 10 + }) + } + + #[test] + fn test_graphql_blocks_from_2() { + let mut mock_db = MockBcDbTrait::new(); + + let block_2 = block_2(); + let block_3 = block_3(); + let current_block = block_4(); let current_blockstamp = current_block.blockstamp(); mock_db @@ -112,15 +214,15 @@ mod tests { mock_db .expect_get_db_blocks_in_local_blockchain() - .with(eq(Range { start: 0, end: 3 })) + .with(eq(vec![BlockNumber(2), BlockNumber(3), BlockNumber(4)])) .returning(move |_| { Ok(vec![ DbBlock { - block: BlockDocument::V10(block_0.clone()), + block: BlockDocument::V10(block_2.clone()), expire_certs: None, }, DbBlock { - block: BlockDocument::V10(block_1.clone()), + block: BlockDocument::V10(block_3.clone()), expire_certs: None, }, DbBlock { @@ -134,7 +236,53 @@ mod tests { tests::test_gql_query( schema, - "{ blocks { commonTime, currency, hash, issuer, number, version } }", + "{ blocks(interval: { from: 2 }) { commonTime, currency, hash, issuer, number, version } }", + json!({ + "data": { + "blocks": [ + block_2_json(), + block_3_json(), + block_4_json(), + ] + } + }), + ); + } + + #[test] + fn test_graphql_blocks_with_step_2() { + let mut mock_db = MockBcDbTrait::new(); + + let block_0 = block_0(); + let current_block = block_2(); + + let current_blockstamp = current_block.blockstamp(); + mock_db + .expect_get_current_blockstamp() + .times(1) + .returning(move || Ok(Some(current_blockstamp))); + + mock_db + .expect_get_db_blocks_in_local_blockchain() + .with(eq(vec![BlockNumber(0), BlockNumber(2)])) + .returning(move |_| { + Ok(vec![ + DbBlock { + block: BlockDocument::V10(block_0.clone()), + expire_certs: None, + }, + DbBlock { + block: BlockDocument::V10(current_block.clone()), + expire_certs: None, + }, + ]) + }); + + let schema = tests::setup(mock_db); + + tests::test_gql_query( + schema, + "{ blocks(step: 2) { commonTime, currency, hash, issuer, number, version } }", json!({ "data": { "blocks": [{ @@ -145,14 +293,6 @@ mod tests { "number": 0, "version": 10 }, - { - "commonTime": 1_488_987_128.0, - "currency": "test_currency", - "hash": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", - "issuer": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", - "number": 1, - "version": 10 - }, { "commonTime": 1_488_987_129.0, "currency": "test_currency", @@ -165,4 +305,54 @@ mod tests { }), ); } + + #[test] + fn test_graphql_blocks() { + let mut mock_db = MockBcDbTrait::new(); + + let block_0 = block_0(); + let block_1 = block_1(); + let current_block = block_2(); + + let current_blockstamp = current_block.blockstamp(); + mock_db + .expect_get_current_blockstamp() + .times(1) + .returning(move || Ok(Some(current_blockstamp))); + + mock_db + .expect_get_db_blocks_in_local_blockchain() + .with(eq(vec![BlockNumber(0), BlockNumber(1), BlockNumber(2)])) + .returning(move |_| { + Ok(vec![ + DbBlock { + block: BlockDocument::V10(block_0.clone()), + expire_certs: None, + }, + DbBlock { + block: BlockDocument::V10(block_1.clone()), + expire_certs: None, + }, + DbBlock { + block: BlockDocument::V10(current_block.clone()), + expire_certs: None, + }, + ]) + }); + + let schema = tests::setup(mock_db); + + tests::test_gql_query( + schema, + "{ blocks { commonTime, currency, hash, issuer, number, version } }", + json!({ + "data": { + "blocks": [ + block_0_json(), + block_1_json(), + block_2_json()] + } + }), + ); + } } diff --git a/lib/modules/gva/src/schema/queries/current.rs b/lib/modules/gva/src/schema/queries/current.rs index 771e750e..076e702b 100644 --- a/lib/modules/gva/src/schema/queries/current.rs +++ b/lib/modules/gva/src/schema/queries/current.rs @@ -32,7 +32,7 @@ pub(crate) fn execute( .get_db() .get_current_block() .map_err(db_err_to_juniper_err) - .map(|db_block_opt| db_block_opt.map(Block::from_db_block)) + .map(|db_block_opt| db_block_opt.map(Into::into)) } #[cfg(test)] -- GitLab