diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e4c58b..243859f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed + +- Updated the encoding and decoding method for GraphQL cursors by removing + `graphql::encode_cursor` and `graphql::decode_cursor` methods and replacing + them with the encoding and decoding methods of `OpaqueCursor`. + ### Fixed - Resolved an issue in the `applyNode` GraphQL API, where configuration values diff --git a/src/archive.rs b/src/archive.rs index 0c77f4e..fd802f4 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -48,7 +48,7 @@ impl Config { pub(crate) fn configure_reverse_proxies( store: &Arc>, - client: &Option, + client: Option<&reqwest::Client>, reverse_proxies: &[Self], ) -> Vec<(ArchiveState, Router)> { reverse_proxies @@ -57,10 +57,10 @@ impl Config { ( crate::archive::ArchiveState { store: store.clone(), - client: client.clone(), + client: client.cloned(), config: rp.clone(), }, - crate::archive::reverse_proxy(store.clone(), client.clone(), rp.clone()), + crate::archive::reverse_proxy(store.clone(), client.cloned(), rp.clone()), ) }) .collect() diff --git a/src/graphql.rs b/src/graphql.rs index 9cb3717..f8ed6cc 100644 --- a/src/graphql.rs +++ b/src/graphql.rs @@ -40,13 +40,12 @@ use std::future::Future; use std::net::SocketAddr; use std::sync::{Arc, Mutex}; -use async_graphql::connection::{ConnectionNameType, CursorType, EdgeNameType}; +use async_graphql::connection::{ConnectionNameType, CursorType, EdgeNameType, OpaqueCursor}; use async_graphql::{ connection::{Connection, Edge, EmptyFields}, Context, Guard, MergedObject, MergedSubscription, ObjectType, OutputType, Result, }; use chrono::TimeDelta; -use data_encoding::BASE64; use num_traits::ToPrimitive; #[cfg(test)] use review_database::HostNetworkGroup; @@ -236,13 +235,23 @@ async fn query_with_constraints( first: Option, last: Option, f: F, -) -> Result> +) -> Result>, Node, ConnectionFields, EmptyFields, Name>> where Node: OutputType, ConnectionFields: ObjectType, Name: ConnectionNameType, - F: FnOnce(Option, Option, Option, Option) -> R, - R: Future, E>>, + F: FnOnce( + Option>>, + Option>>, + Option, + Option, + ) -> R, + R: Future< + Output = Result< + Connection>, Node, ConnectionFields, EmptyFields, Name>, + E, + >, + >, E: Into, { extra_validate_pagination_params( @@ -322,49 +331,27 @@ async fn get_store<'a>(ctx: &Context<'a>) -> Result>>()?.read().await) } -/// Decodes a cursor used in pagination. -fn decode_cursor(cursor: &str) -> Option> { - BASE64.decode(cursor.as_bytes()).ok() -} - -/// Encodes a cursor used in pagination. -fn encode_cursor(cursor: &[u8]) -> String { - BASE64.encode(cursor) -} - -#[allow(clippy::type_complexity)] -fn decode_cursor_pair( - after: Option, - before: Option, -) -> Result<(Option>, Option>)> { - let after = if let Some(after) = after { - Some(decode_cursor(&after).ok_or("invalid cursor `after`")?) - } else { - None - }; - let before = if let Some(before) = before { - Some(decode_cursor(&before).ok_or("invalid cursor `before`")?) - } else { - None - }; - Ok((after, before)) -} - #[allow(clippy::type_complexity)] fn process_load_edges<'a, T, I, R>( table: &'a T, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, prefix: Option<&[u8]>, -) -> Result<(Vec>, bool, bool)> +) -> ( + std::vec::Vec>, + bool, + bool, +) where T: database::Iterable<'a, I>, I: std::iter::Iterator>, R: database::UniqueKey, { - let (after, before) = decode_cursor_pair(after, before)?; + let after = after.map(|cursor| cursor.0); + let before = before.map(|cursor| cursor.0); + let (nodes, has_previous, has_next) = if let Some(first) = first { let (nodes, has_more) = collect_edges(table, Direction::Forward, after, before, prefix, first); @@ -377,13 +364,13 @@ where (nodes, has_more, false) }; - Ok((nodes, has_previous, has_next)) + (nodes, has_previous, has_next) } fn load_edges_interim<'a, T, I, R>( table: &'a T, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, prefix: Option<&[u8]>, @@ -394,7 +381,7 @@ where R: database::UniqueKey, { let (nodes, has_previous, has_next) = - process_load_edges(table, after, before, first, last, prefix)?; + process_load_edges(table, after, before, first, last, prefix); let nodes = nodes .into_iter() @@ -403,14 +390,15 @@ where Ok((nodes, has_previous, has_next)) } +#[allow(clippy::type_complexity)] fn load_edges<'a, T, I, R, N, A, NodesField>( table: &'a T, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, additional_fields: A, -) -> Result> +) -> Result>, N, A, EmptyFields, NodesField>> where T: database::Iterable<'a, I>, I: std::iter::Iterator>, @@ -420,7 +408,7 @@ where NodesField: ConnectionNameType, { let (nodes, has_previous, has_next) = - process_load_edges(table, after, before, first, last, None)?; + process_load_edges(table, after, before, first, last, None); for node in &nodes { let Err(e) = node else { continue }; @@ -432,8 +420,8 @@ where Connection::with_additional_fields(has_previous, has_next, additional_fields); connection.edges.extend(nodes.into_iter().map(|node| { let Ok(node) = node else { unreachable!() }; - let encoded = encode_cursor(node.unique_key().as_ref()); - Edge::new(encoded, node.into()) + let key = node.unique_key().as_ref().to_vec(); + Edge::new(OpaqueCursor(key), node.into()) })); Ok(connection) } diff --git a/src/graphql/account.rs b/src/graphql/account.rs index 28b18f3..1bc8467 100644 --- a/src/graphql/account.rs +++ b/src/graphql/account.rs @@ -4,6 +4,7 @@ use std::{ }; use anyhow::anyhow; +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, Context, Enum, InputObject, Object, Result, SimpleObject, StringNumber, @@ -58,7 +59,7 @@ impl AccountQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, Account, AccountTotalCount, EmptyFields>> { query_with_constraints( after, before, @@ -325,10 +326,16 @@ impl AccountMutation { if let Some(mut account) = account_map.get(&username)? { validate_password(&account, &username, &password)?; validate_last_signin_time(&account, &username)?; - validate_allow_access_from(&account, &client_ip, &username)?; + validate_allow_access_from(&account, client_ip.as_ref(), &username)?; validate_max_parallel_sessions(&account, &store, &username)?; - sign_in_actions(&mut account, &store, &account_map, &client_ip, &username) + sign_in_actions( + &mut account, + &store, + &account_map, + client_ip.as_ref(), + &username, + ) } else { info!("{username} is not a valid username"); Err("incorrect username or password".into()) @@ -355,13 +362,19 @@ impl AccountMutation { if let Some(mut account) = account_map.get(&username)? { validate_password(&account, &username, &password)?; - validate_allow_access_from(&account, &client_ip, &username)?; + validate_allow_access_from(&account, client_ip.as_ref(), &username)?; validate_max_parallel_sessions(&account, &store, &username)?; validate_update_new_password(&password, &new_password, &username)?; account.update_password(&new_password)?; - sign_in_actions(&mut account, &store, &account_map, &client_ip, &username) + sign_in_actions( + &mut account, + &store, + &account_map, + client_ip.as_ref(), + &username, + ) } else { info!("{username} is not a valid username"); Err("incorrect username or password".into()) @@ -474,7 +487,7 @@ fn validate_last_signin_time(account: &types::Account, username: &str) -> Result fn validate_allow_access_from( account: &types::Account, - client_ip: &Option, + client_ip: Option<&SocketAddr>, username: &str, ) -> Result<()> { if let Some(allow_access_from) = account.allow_access_from.as_ref() { @@ -533,7 +546,7 @@ fn sign_in_actions( account: &mut types::Account, store: &Store, account_map: &Table, - client_ip: &Option, + client_ip: Option<&SocketAddr>, username: &str, ) -> Result { let (token, expiration_time) = @@ -720,11 +733,11 @@ impl AccountTotalCount { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, Account, AccountTotalCount, EmptyFields>> { let store = crate::graphql::get_store(ctx).await?; let table = store.account_map(); super::load_edges(&table, after, before, first, last, AccountTotalCount) diff --git a/src/graphql/allow_network.rs b/src/graphql/allow_network.rs index a3b1188..7521271 100644 --- a/src/graphql/allow_network.rs +++ b/src/graphql/allow_network.rs @@ -1,3 +1,4 @@ +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, Context, InputObject, Object, Result, ID, @@ -29,7 +30,8 @@ impl AllowNetworkQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, AllowNetwork, AllowNetworkTotalCount, EmptyFields>> + { query_with_constraints( after, before, @@ -197,11 +199,11 @@ impl AllowNetworkTotalCount { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, AllowNetwork, AllowNetworkTotalCount, EmptyFields>> { let store = crate::graphql::get_store(ctx).await?; let map = store.allow_network_map(); super::load_edges(&map, after, before, first, last, AllowNetworkTotalCount) diff --git a/src/graphql/block_network.rs b/src/graphql/block_network.rs index e84937c..9c0812b 100644 --- a/src/graphql/block_network.rs +++ b/src/graphql/block_network.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, Context, InputObject, Object, Result, ID, @@ -32,7 +33,8 @@ impl BlockNetworkQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, BlockNetwork, BlockNetworkTotalCount, EmptyFields>> + { query_with_constraints( after, before, @@ -198,11 +200,11 @@ impl BlockNetworkTotalCount { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, BlockNetwork, BlockNetworkTotalCount, EmptyFields>> { let db = super::get_store(ctx).await?; let map = db.block_network_map(); super::load_edges(&map, after, before, first, last, BlockNetworkTotalCount) diff --git a/src/graphql/category.rs b/src/graphql/category.rs index 49faeca..7c49e90 100644 --- a/src/graphql/category.rs +++ b/src/graphql/category.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, types::ID, @@ -29,7 +30,7 @@ impl CategoryQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, Category, CategoryTotalCount, EmptyFields>> { query_with_constraints( after, before, @@ -108,11 +109,11 @@ impl CategoryTotalCount { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, Category, CategoryTotalCount, EmptyFields>> { let store = super::get_store(ctx).await?; let table = store.category_map(); super::load_edges(&table, after, before, first, last, CategoryTotalCount) diff --git a/src/graphql/customer.rs b/src/graphql/customer.rs index 9256139..6b16afa 100644 --- a/src/graphql/customer.rs +++ b/src/graphql/customer.rs @@ -1,6 +1,7 @@ use std::convert::{TryFrom, TryInto}; use anyhow::Context as AnyhowContext; +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, types::ID, @@ -28,7 +29,7 @@ impl CustomerQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, Customer, CustomerTotalCount, EmptyFields>> { query_with_constraints( after, before, @@ -459,11 +460,11 @@ impl CustomerTotalCount { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, Customer, CustomerTotalCount, EmptyFields>> { let store = crate::graphql::get_store(ctx).await?; let map = store.customer_map(); super::load_edges(&map, after, before, first, last, CustomerTotalCount) @@ -614,11 +615,11 @@ mod tests { ); let res = schema - .execute(r#"{customerList(last: 10, before: "dDg="){edges{node{name}}totalCount,pageInfo{startCursor}}}"#) + .execute(r#"{customerList(last: 10, before: "WzExNiw1Nl0"){edges{node{name}}totalCount,pageInfo{startCursor}}}"#) .await; assert_eq!( res.data.to_string(), - r#"{customerList: {edges: [{node: {name: "t1"}}, {node: {name: "t10"}}, {node: {name: "t2"}}, {node: {name: "t3"}}, {node: {name: "t4"}}, {node: {name: "t5"}}, {node: {name: "t6"}}, {node: {name: "t7"}}], totalCount: 10, pageInfo: {startCursor: "dDE="}}}"# + r#"{customerList: {edges: [{node: {name: "t1"}}, {node: {name: "t10"}}, {node: {name: "t2"}}, {node: {name: "t3"}}, {node: {name: "t4"}}, {node: {name: "t5"}}, {node: {name: "t6"}}, {node: {name: "t7"}}], totalCount: 10, pageInfo: {startCursor: "WzExNiw0OV0"}}}"# ); let res = schema @@ -630,17 +631,17 @@ mod tests { let res = schema .execute( - r#"{customerList(first:10 after:"dDc=" ){edges{node{name}}totalCount,pageInfo{endCursor}}}"#, + r#"{customerList(first:10 after:"WzExNiw1NV0" ){edges{node{name}}totalCount,pageInfo{endCursor}}}"#, ) .await; assert_eq!( res.data.to_string(), - r#"{customerList: {edges: [{node: {name: "t8"}}, {node: {name: "t9"}}], totalCount: 10, pageInfo: {endCursor: "dDk="}}}"# + r#"{customerList: {edges: [{node: {name: "t8"}}, {node: {name: "t9"}}], totalCount: 10, pageInfo: {endCursor: "WzExNiw1N10"}}}"# ); let res = schema .execute( - r#"{customerList(first:10 before:"dDc=" ){edges{node{name}}totalCount,pageInfo{endCursor}}}"#, + r#"{customerList(first:10 before:"WzExNiw1NV0" ){edges{node{name}}totalCount,pageInfo{endCursor}}}"#, ) .await; assert!(res.is_err()); diff --git a/src/graphql/data_source.rs b/src/graphql/data_source.rs index fe4b123..b5c9e02 100644 --- a/src/graphql/data_source.rs +++ b/src/graphql/data_source.rs @@ -1,3 +1,4 @@ +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, types::ID, @@ -25,7 +26,8 @@ impl DataSourceQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, DataSource, DataSourceTotalCount, EmptyFields>> + { query_with_constraints( after, before, @@ -259,11 +261,11 @@ impl DataSourceTotalCount { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, DataSource, DataSourceTotalCount, EmptyFields>> { let store = crate::graphql::get_store(ctx).await?; let map = store.data_source_map(); diff --git a/src/graphql/filter.rs b/src/graphql/filter.rs index dca6e07..f467dc1 100644 --- a/src/graphql/filter.rs +++ b/src/graphql/filter.rs @@ -476,12 +476,12 @@ impl PartialEq for &database::Filter { _ => false, } && self.keywords == rhs.keywords - && cmp_option_vec_string_with_id(&self.network_tags, &rhs.network_tags) - && cmp_option_vec_string_with_id(&self.customers, &rhs.customers) + && cmp_option_vec_string_with_id(self.network_tags.as_ref(), rhs.network_tags.as_ref()) + && cmp_option_vec_string_with_id(self.customers.as_ref(), rhs.customers.as_ref()) && network_eq - && cmp_option_vec_string_with_id(&self.sensors, &rhs.sensors) - && cmp_option_vec_string_with_id(&self.os, &rhs.os) - && cmp_option_vec_string_with_id(&self.devices, &rhs.devices) + && cmp_option_vec_string_with_id(self.sensors.as_ref(), rhs.sensors.as_ref()) + && cmp_option_vec_string_with_id(self.os.as_ref(), rhs.os.as_ref()) + && cmp_option_vec_string_with_id(self.devices.as_ref(), rhs.devices.as_ref()) && self.host_names == rhs.host_names && self.user_ids == rhs.user_ids && self.user_names == rhs.user_names @@ -546,7 +546,7 @@ pub(super) enum TrafficDirection { To, } -fn cmp_option_vec_string_with_id(lhs: &Option>, rhs: &Option>) -> bool { +fn cmp_option_vec_string_with_id(lhs: Option<&Vec>, rhs: Option<&Vec>) -> bool { match (lhs, rhs) { (Some(lhs), Some(rhs)) => lhs .iter() diff --git a/src/graphql/network.rs b/src/graphql/network.rs index 52321e7..e25d2b3 100644 --- a/src/graphql/network.rs +++ b/src/graphql/network.rs @@ -1,5 +1,6 @@ use std::{convert::TryInto, mem::size_of}; +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, types::ID, @@ -31,7 +32,7 @@ impl NetworkQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, Network, NetworkTotalCount, EmptyFields>> { query_with_constraints( after, before, @@ -237,11 +238,11 @@ impl NetworkTotalCount { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, Network, NetworkTotalCount, EmptyFields>> { let store = crate::graphql::get_store(ctx).await?; let map = store.network_map(); super::load_edges(&map, after, before, first, last, NetworkTotalCount) diff --git a/src/graphql/node.rs b/src/graphql/node.rs index 6e492d3..eb9489b 100644 --- a/src/graphql/node.rs +++ b/src/graphql/node.rs @@ -370,7 +370,7 @@ impl NodeStatus { impl NodeStatus { fn new( node: database::Node, - resource_usage: &Option, + resource_usage: Option<&roxy::ResourceUsage>, ping: Option, manager: bool, ) -> Self { diff --git a/src/graphql/node/crud.rs b/src/graphql/node/crud.rs index 4e9f4ad..e2da70c 100644 --- a/src/graphql/node/crud.rs +++ b/src/graphql/node/crud.rs @@ -1,5 +1,6 @@ #![allow(clippy::fn_params_excessive_bools)] +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, types::ID, @@ -30,7 +31,7 @@ impl NodeQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, Node, NodeTotalCount, EmptyFields>> { query_with_constraints( after, before, @@ -159,11 +160,11 @@ impl NodeMutation { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, Node, NodeTotalCount, EmptyFields>> { let store = crate::graphql::get_store(ctx).await?; let map = store.node_map(); super::super::load_edges(&map, after, before, first, last, NodeTotalCount) diff --git a/src/graphql/node/status.rs b/src/graphql/node/status.rs index 866cf35..4a2f233 100644 --- a/src/graphql/node/status.rs +++ b/src/graphql/node/status.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, Edge, EmptyFields}, Context, Object, Result, @@ -25,7 +26,8 @@ impl NodeStatusQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, NodeStatus, NodeStatusTotalCount, EmptyFields>> + { query_with_constraints( after, before, @@ -39,11 +41,11 @@ impl NodeStatusQuery { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, NodeStatus, NodeStatusTotalCount, EmptyFields>> { let (node_list, has_previous, has_next) = { let store = crate::graphql::get_store(ctx).await?; let map = store.node_map(); @@ -67,9 +69,10 @@ async fn load( let (resource_usage, ping) = fetch_resource_usage_and_ping(agent_manager, hostname, is_manager).await; + let key = node.unique_key(); connection.edges.push(Edge::new( - crate::graphql::encode_cursor(node.unique_key()), - NodeStatus::new(node, &resource_usage, ping, is_manager), + OpaqueCursor(key.to_vec()), + NodeStatus::new(node, resource_usage.as_ref(), ping, is_manager), )); } Ok(connection) @@ -609,32 +612,32 @@ mod tests { .await; assert_eq!( res.data.to_string(), - r#"{nodeStatusList: {edges: [{node: {name: "test1"}}, {node: {name: "test2"}}, {node: {name: "test3"}}, {node: {name: "test4"}}, {node: {name: "test5"}}], pageInfo: {endCursor: "dGVzdDU="}}}"# + r#"{nodeStatusList: {edges: [{node: {name: "test1"}}, {node: {name: "test2"}}, {node: {name: "test3"}}, {node: {name: "test4"}}, {node: {name: "test5"}}], pageInfo: {endCursor: "WzExNiwxMDEsMTE1LDExNiw1M10"}}}"# ); let res = schema - .execute(r#"{nodeStatusList(last:3,before:"dGVzdDM="){edges{node{name}},pageInfo{startCursor}}}"#) + .execute(r#"{nodeStatusList(last:3,before:"WzExNiwxMDEsMTE1LDExNiw1MV0"){edges{node{name}},pageInfo{startCursor}}}"#) .await; assert_eq!( res.data.to_string(), - r#"{nodeStatusList: {edges: [{node: {name: "test1"}}, {node: {name: "test2"}}], pageInfo: {startCursor: "dGVzdDE="}}}"# + r#"{nodeStatusList: {edges: [{node: {name: "test1"}}, {node: {name: "test2"}}], pageInfo: {startCursor: "WzExNiwxMDEsMTE1LDExNiw0OV0"}}}"# ); let res = schema - .execute(r#"{nodeStatusList(first:3,after:"dGVzdDM="){edges{node{name}},pageInfo{endCursor}}}"#) + .execute(r#"{nodeStatusList(first:3,after:"WzExNiwxMDEsMTE1LDExNiw1MV0"){edges{node{name}},pageInfo{endCursor}}}"#) .await; assert_eq!( res.data.to_string(), - r#"{nodeStatusList: {edges: [{node: {name: "test4"}}, {node: {name: "test5"}}], pageInfo: {endCursor: "dGVzdDU="}}}"# + r#"{nodeStatusList: {edges: [{node: {name: "test4"}}, {node: {name: "test5"}}], pageInfo: {endCursor: "WzExNiwxMDEsMTE1LDExNiw1M10"}}}"# ); let res = schema - .execute(r#"{nodeStatusList(last:2, after:"dGVzdDU="){edges{node{name}}}}"#) + .execute(r#"{nodeStatusList(last:2, after:"WzExNiwxMDEsMTE1LDExNiw1M10"){edges{node{name}}}}"#) .await; assert!(res.is_err()); let res = schema - .execute(r#"{nodeStatusList(first:2, before:"dGVzdDU="){edges{node{name}}}}"#) + .execute(r#"{nodeStatusList(first:2, before:"WzExNiwxMDEsMTE1LDExNiw1M10"){edges{node{name}}}}"#) .await; assert!(res.is_err()); } diff --git a/src/graphql/outlier.rs b/src/graphql/outlier.rs index db64805..c892250 100644 --- a/src/graphql/outlier.rs +++ b/src/graphql/outlier.rs @@ -1,6 +1,7 @@ use std::{collections::HashMap, sync::Arc}; use anyhow::anyhow; +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, Edge, EmptyFields}, types::ID, @@ -19,7 +20,7 @@ use serde::Serialize; use tokio::{sync::RwLock, time}; use tracing::error; -use super::{encode_cursor, model::ModelDigest, query, Role, RoleGuard}; +use super::{model::ModelDigest, query, Role, RoleGuard}; const MAX_EVENT_NUM_OF_OUTLIER: usize = 50; const DEFAULT_RANKED_OUTLIER_FETCH_TIME: u64 = 60; @@ -178,7 +179,7 @@ impl OutlierQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, Outlier, OutlierTotalCount, EmptyFields>> { let model = model.as_str().parse()?; query( after, @@ -208,7 +209,9 @@ impl OutlierQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result< + Connection>, RankedOutlier, RankedOutlierTotalCount, EmptyFields>, + > { let filter = |node: RankedOutlier| if node.saved { Some(node) } else { None }; query( after, @@ -239,7 +242,9 @@ impl OutlierQuery { first: Option, last: Option, filter: Option, - ) -> Result> { + ) -> Result< + Connection>, RankedOutlier, RankedOutlierTotalCount, EmptyFields>, + > { query( after, before, @@ -261,12 +266,13 @@ async fn load_outliers( ctx: &Context<'_>, model_id: ID, time: Option, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, filter: fn(RankedOutlier) -> Option, -) -> Result> { +) -> Result>, RankedOutlier, RankedOutlierTotalCount, EmptyFields>> +{ let model_id: i32 = model_id.as_str().parse()?; let timestamp = time.map(|t| t.and_utc().timestamp_nanos_opt().unwrap_or_default()); @@ -294,7 +300,7 @@ async fn load_outliers( }, ); connection.edges.extend(nodes.into_iter().filter_map(|n| { - let cursor = encode_cursor(&n.unique_key()); + let cursor = OpaqueCursor(n.unique_key()); let n: RankedOutlier = n.into(); filter(n).map(|n| Edge::new(cursor, n)) })); @@ -478,14 +484,17 @@ impl OutlierTotalCount { async fn load( ctx: &Context<'_>, model_id: i32, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, Outlier, OutlierTotalCount, EmptyFields>> { let store = crate::graphql::get_store(ctx).await?; let table = store.outlier_map(); - let (after, before) = super::decode_cursor_pair(after, before)?; + + let after = after.map(|cursor| cursor.0); + let before = before.map(|cursor| cursor.0); + let decoded_after = after .as_deref() .map(|input| bincode::DefaultOptions::new().deserialize(input)) @@ -545,9 +554,9 @@ async fn load( }; let edges = batches.into_values().filter_map(|(from, to, mut ev)| { let cursor = bincode::DefaultOptions::new().serialize(&(from, to)).ok()?; - let encoded = super::encode_cursor(&cursor); + let cursor = OpaqueCursor(cursor); ev.events.sort_unstable(); - Some(Edge::new(encoded, ev)) + Some(Edge::new(cursor, ev)) }); let mut connection = Connection::with_additional_fields(has_previous, has_next, OutlierTotalCount { model_id }); @@ -567,8 +576,8 @@ pub(crate) fn datetime_from_ts_nano(time: i64) -> Option> { fn check_filter_to_ranked_outlier( node: &OutlierInfo, - filter: &Option, - tag_id_list: &Option>, + filter: Option<&SearchFilterInput>, + tag_id_list: Option<&Vec>, remarks_map: &IndexedTable<'_, TriageResponse>, ) -> Result { if let Some(filter) = filter { @@ -626,15 +635,17 @@ async fn load_ranked_outliers_with_filter( ctx: &Context<'_>, model_id: ID, time: Option, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, filter: Option, -) -> Result> { +) -> Result>, RankedOutlier, RankedOutlierTotalCount, EmptyFields>> +{ let model_id: i32 = model_id.as_str().parse()?; let timestamp = time.map(|t| t.and_utc().timestamp_nanos_opt().unwrap_or_default()); - let (after, before) = super::decode_cursor_pair(after, before)?; + let after = after.map(|cursor| cursor.0); + let before = before.map(|cursor| cursor.0); let (direction, count, from) = if let Some(first) = first { (Direction::Forward, first, after.as_deref()) } else if let Some(last) = last { @@ -692,22 +703,31 @@ async fn load_ranked_outliers_with_filter( if from != key { return Err(anyhow!("invalid cursor").into()); } - } else if check_filter_to_ranked_outlier(&node, &filter, &tag_id_list, &remarks_map)? { - nodes.push((encode_cursor(&key), node)); + } else if check_filter_to_ranked_outlier( + &node, + filter.as_ref(), + tag_id_list.as_ref(), + &remarks_map, + )? { + nodes.push((OpaqueCursor(key), node)); } } for res in ranked_outlier_iter { let node = res?; let key = node.unique_key(); - let encoded_key = encode_cursor(&key); - if check_filter_to_ranked_outlier(&node, &filter, &tag_id_list, &remarks_map)? { + if check_filter_to_ranked_outlier( + &node, + filter.as_ref(), + tag_id_list.as_ref(), + &remarks_map, + )? { if nodes.len() >= count { has_more = true; break; } - nodes.push((encoded_key, node)); + nodes.push((OpaqueCursor(key), node)); } } diff --git a/src/graphql/qualifier.rs b/src/graphql/qualifier.rs index 02517a7..54c2e6d 100644 --- a/src/graphql/qualifier.rs +++ b/src/graphql/qualifier.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, types::ID, @@ -29,7 +30,8 @@ impl QualifierQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, Qualifier, QualifierTotalCount, EmptyFields>> + { query_with_constraints( after, before, @@ -105,11 +107,11 @@ impl QualifierTotalCount { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, Qualifier, QualifierTotalCount, EmptyFields>> { let store = super::get_store(ctx).await?; let table = store.qualifier_map(); super::load_edges(&table, after, before, first, last, QualifierTotalCount) diff --git a/src/graphql/sampling.rs b/src/graphql/sampling.rs index d6270cb..140f256 100644 --- a/src/graphql/sampling.rs +++ b/src/graphql/sampling.rs @@ -1,5 +1,6 @@ use std::net::IpAddr; +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, types::ID, @@ -243,7 +244,9 @@ impl SamplingPolicyQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result< + Connection>, SamplingPolicy, SamplingPolicyTotalCount, EmptyFields>, + > { query_with_constraints( after, before, @@ -271,11 +274,12 @@ impl SamplingPolicyQuery { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, SamplingPolicy, SamplingPolicyTotalCount, EmptyFields>> +{ let store = crate::graphql::get_store(ctx).await?; let map = store.sampling_policy_map(); super::load_edges(&map, after, before, first, last, SamplingPolicyTotalCount) diff --git a/src/graphql/statistics.rs b/src/graphql/statistics.rs index 4f3fc16..d2c099e 100644 --- a/src/graphql/statistics.rs +++ b/src/graphql/statistics.rs @@ -1,3 +1,4 @@ +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, ConnectionNameType, Edge, EdgeNameType, EmptyFields}, types::ID, @@ -83,7 +84,9 @@ impl StatisticsQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result< + Connection>, Round, TotalCountByModel, EmptyFields, RoundByModel>, + > { let model = model.as_str().parse()?; query_with_constraints( @@ -227,11 +230,12 @@ async fn load_rounds_by_cluster( async fn load_rounds_by_model( ctx: &Context<'_>, model: i32, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, Round, TotalCountByModel, EmptyFields, RoundByModel>> +{ let store = super::get_store(ctx).await?; let table = store.batch_info_map(); super::load_edges( diff --git a/src/graphql/status.rs b/src/graphql/status.rs index 0b2874b..dd42f9c 100644 --- a/src/graphql/status.rs +++ b/src/graphql/status.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, types::ID, @@ -29,7 +30,7 @@ impl StatusQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, Status, StatusTotalCount, EmptyFields>> { query_with_constraints( after, before, @@ -105,11 +106,11 @@ impl StatusTotalCount { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, Status, StatusTotalCount, EmptyFields>> { let store = super::get_store(ctx).await?; let table = store.status_map(); super::load_edges(&table, after, before, first, last, StatusTotalCount) diff --git a/src/graphql/template.rs b/src/graphql/template.rs index 636f9d4..b6e4d23 100644 --- a/src/graphql/template.rs +++ b/src/graphql/template.rs @@ -1,5 +1,6 @@ use std::convert::{TryFrom, TryInto}; +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, Context, Enum, InputObject, Object, Result, StringNumber, Union, @@ -26,7 +27,7 @@ impl TemplateQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, Template, TemplateTotalCount, EmptyFields>> { query_with_constraints( after, before, @@ -347,11 +348,11 @@ impl TemplateTotalCount { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, Template, TemplateTotalCount, EmptyFields>> { let store = crate::graphql::get_store(ctx).await?; let map = store.template_map(); super::load_edges(&map, after, before, first, last, TemplateTotalCount) diff --git a/src/graphql/tor_exit_node.rs b/src/graphql/tor_exit_node.rs index 1c23db9..35b7e55 100644 --- a/src/graphql/tor_exit_node.rs +++ b/src/graphql/tor_exit_node.rs @@ -1,3 +1,4 @@ +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, Context, Object, Result, SimpleObject, @@ -25,7 +26,8 @@ impl TorExitNodeQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, TorExitNode, TorExitNodeTotalCount, EmptyFields>> + { query_with_constraints( after, before, @@ -95,11 +97,11 @@ impl TorExitNodeTotalCount { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, TorExitNode, TorExitNodeTotalCount, EmptyFields>> { let store = crate::graphql::get_store(ctx).await?; let map = store.tor_exit_node_map(); diff --git a/src/graphql/triage/policy.rs b/src/graphql/triage/policy.rs index c9ff3cc..a5a5768 100644 --- a/src/graphql/triage/policy.rs +++ b/src/graphql/triage/policy.rs @@ -1,3 +1,4 @@ +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, Context, Object, Result, ID, @@ -35,7 +36,8 @@ impl TriagePolicyQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, TriagePolicy, TriagePolicyTotalCount, EmptyFields>> + { query_with_constraints( after, before, @@ -63,11 +65,11 @@ impl TriagePolicyQuery { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, TriagePolicy, TriagePolicyTotalCount, EmptyFields>> { let store = crate::graphql::get_store(ctx).await?; let map = store.triage_policy_map(); super::super::load_edges(&map, after, before, first, last, TriagePolicyTotalCount) diff --git a/src/graphql/triage/response.rs b/src/graphql/triage/response.rs index e993af9..77d3696 100644 --- a/src/graphql/triage/response.rs +++ b/src/graphql/triage/response.rs @@ -1,3 +1,4 @@ +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, types::ID, @@ -73,7 +74,9 @@ impl super::TriageResponseQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result< + Connection>, TriageResponse, TriageResponseTotalCount, EmptyFields>, + > { query_with_constraints( after, before, @@ -101,11 +104,12 @@ impl super::TriageResponseQuery { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, TriageResponse, TriageResponseTotalCount, EmptyFields>> +{ let store = crate::graphql::get_store(ctx).await?; let table = store.triage_response_map(); crate::graphql::load_edges(&table, after, before, first, last, TriageResponseTotalCount) diff --git a/src/graphql/trusted_domain.rs b/src/graphql/trusted_domain.rs index 429b7c5..e787173 100644 --- a/src/graphql/trusted_domain.rs +++ b/src/graphql/trusted_domain.rs @@ -1,3 +1,4 @@ +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, Context, Object, Result, SimpleObject, @@ -23,7 +24,7 @@ impl TrustedDomainQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result>, TrustedDomain, EmptyFields, EmptyFields>> { query_with_constraints( after, before, @@ -96,11 +97,11 @@ impl From for TrustedDomain { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result>, TrustedDomain, EmptyFields, EmptyFields>> { let store = crate::graphql::get_store(ctx).await?; let map = store.trusted_domain_map(); super::load_edges(&map, after, before, first, last, EmptyFields) diff --git a/src/graphql/trusted_user_agent.rs b/src/graphql/trusted_user_agent.rs index 97d1835..7a83404 100644 --- a/src/graphql/trusted_user_agent.rs +++ b/src/graphql/trusted_user_agent.rs @@ -1,3 +1,4 @@ +use async_graphql::connection::OpaqueCursor; use async_graphql::{ connection::{Connection, EmptyFields}, Context, Object, Result, SimpleObject, @@ -27,7 +28,14 @@ impl UserAgentQuery { before: Option, first: Option, last: Option, - ) -> Result> { + ) -> Result< + Connection< + OpaqueCursor>, + TrustedUserAgent, + TrustedUserAgentTotalCount, + EmptyFields, + >, + > { query_with_constraints( after, before, @@ -158,11 +166,13 @@ pub fn get_trusted_user_agent_list(db: &Store) -> Result> { async fn load( ctx: &Context<'_>, - after: Option, - before: Option, + after: Option>>, + before: Option>>, first: Option, last: Option, -) -> Result> { +) -> Result< + Connection>, TrustedUserAgent, TrustedUserAgentTotalCount, EmptyFields>, +> { let store = crate::graphql::get_store(ctx).await?; let map = store.trusted_user_agent_map(); super::load_edges(&map, after, before, first, last, TrustedUserAgentTotalCount) diff --git a/src/lib.rs b/src/lib.rs index 396b2eb..2ca9b12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,7 +86,7 @@ where let proxies_config = crate::archive::Config::configure_reverse_proxies( &store, - &client, + client.as_ref(), &config.reverse_proxies, );