Commit 10d6985d authored by Éloïs's avatar Éloïs

[feat] gva: blocks: add inputs interval and step & add negative paging

parent b53c922d
Pipeline #7264 failed with stages
in 33 minutes and 16 seconds
......@@ -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
}
#################################
......
......@@ -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)),
......
......@@ -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,
)
}
}
......
......@@ -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(),
......
// 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;
......@@ -13,84 +13,53 @@
// 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
// ! BlockInterval input methods
use super::Paging;
use super::super::BlockInterval;
use crate::db::BcDbTrait;
use durs_bc_db_reader::DbError;
use std::ops::Range;
use std::ops::RangeInclusive;
const DEFAULT_PAGE_NUMBER: i32 = 0;
const DEFAULT_PAGE_SIZE: i32 = 50;
const DEFAULT_FROM_BLOCK: i32 = 0;
const DEFAULT_START: usize = 0;
const END_WHEN_EMPTY_BLOCKCHAIN: usize = 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
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 {
int
Ok(END_WHEN_EMPTY_BLOCKCHAIN)
}
} 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
}
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 {
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)
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(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,
),
Ok(DEFAULT_START..=Self::get_default_end(db)?)
}
}
}
......@@ -103,15 +72,7 @@ mod tests {
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> {
fn test_block_interval_get_range_with_short_bc() -> Result<(), DbError> {
let mut mock_db = MockBcDbTrait::new();
mock_db
.expect_get_current_blockstamp()
......@@ -122,24 +83,19 @@ mod tests {
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()
0..=42,
BlockInterval::get_range(&mock_db, None)?
}
Ok(())
}
#[test]
fn test_filled_paging_range() -> Result<(), DbError> {
fn test_block_interval_get_range_with_long_bc() -> Result<(), DbError> {
let mut mock_db = MockBcDbTrait::new();
mock_db
.expect_get_current_blockstamp()
.times(3)
.times(2)
.returning(|| {
Ok(Some(Blockstamp {
id: BlockNumber(750),
......@@ -147,47 +103,25 @@ mod tests {
}))
});
let filled_paging = FilledPaging::new(&mock_db, None)?;
assert_eq! {
Range {
start: 0,
end: 50,
},
filled_paging.get_range()
0..=750,
BlockInterval::get_range(&mock_db, None)?
}
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()
500..=750,
BlockInterval::get_range(&mock_db, Some(BlockInterval {
from: Some(500),
to: None,
}))?
}
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()
500..=700,
BlockInterval::get_range(&mock_db, Some(BlockInterval {
from: Some(500),
to: Some(700),
}))?
}
Ok(())
......
// 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),
);
}
}
......@@ -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)]
......
......@@ -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)]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment