1
1
use futures01:: future;
2
2
use graphql_parser:: query as q;
3
- use std:: collections:: BTreeMap ;
3
+ use std:: collections:: { BTreeMap , HashMap } ;
4
4
use std:: env;
5
5
use std:: str:: FromStr ;
6
6
use std:: sync:: Arc ;
@@ -9,12 +9,12 @@ use std::time::{Duration, Instant};
9
9
use crate :: prelude:: {
10
10
object, object_value, QueryExecutionOptions , StoreResolver , SubscriptionExecutionOptions ,
11
11
} ;
12
- use crate :: query:: execute_query;
12
+ use crate :: query:: { execute_query, shape_hash :: shape_hash } ;
13
13
use crate :: subscription:: execute_prepared_subscription;
14
14
use graph:: prelude:: {
15
15
o, EthereumBlockPointer , GraphQlRunner as GraphQlRunnerTrait , Logger , Query ,
16
16
QueryExecutionError , QueryResult , QueryResultFuture , Store , StoreError , SubgraphDeploymentId ,
17
- SubgraphDeploymentStore , Subscription , SubscriptionResultFuture ,
17
+ SubgraphDeploymentStore , Subscription , SubscriptionError , SubscriptionResultFuture ,
18
18
} ;
19
19
20
20
use lazy_static:: lazy_static;
@@ -23,6 +23,7 @@ use lazy_static::lazy_static;
23
23
pub struct GraphQlRunner < S > {
24
24
logger : Logger ,
25
25
store : Arc < S > ,
26
+ expensive : HashMap < u64 , Arc < q:: Document > > ,
26
27
}
27
28
28
29
lazy_static ! {
@@ -53,10 +54,15 @@ where
53
54
S : Store + SubgraphDeploymentStore ,
54
55
{
55
56
/// Creates a new query runner.
56
- pub fn new ( logger : & Logger , store : Arc < S > ) -> Self {
57
+ pub fn new ( logger : & Logger , store : Arc < S > , expensive : & Vec < Arc < q:: Document > > ) -> Self {
58
+ let expensive = expensive
59
+ . into_iter ( )
60
+ . map ( |doc| ( shape_hash ( & doc) , doc. clone ( ) ) )
61
+ . collect :: < HashMap < _ , _ > > ( ) ;
57
62
GraphQlRunner {
58
63
logger : logger. new ( o ! ( "component" => "GraphQlRunner" ) ) ,
59
64
store,
65
+ expensive,
60
66
}
61
67
}
62
68
@@ -138,6 +144,14 @@ where
138
144
Ok ( QueryResult :: new ( Some ( q:: Value :: Object ( values) ) ) )
139
145
}
140
146
}
147
+
148
+ pub fn check_too_expensive ( & self , query : & Query ) -> Result < ( ) , Vec < QueryExecutionError > > {
149
+ if self . expensive . contains_key ( & shape_hash ( & query. document ) ) {
150
+ Err ( vec ! [ QueryExecutionError :: TooExpensive ] )
151
+ } else {
152
+ Ok ( ( ) )
153
+ }
154
+ }
141
155
}
142
156
143
157
impl < S > GraphQlRunnerTrait for GraphQlRunner < S >
@@ -160,14 +174,19 @@ where
160
174
max_depth : Option < u8 > ,
161
175
max_first : Option < u32 > ,
162
176
) -> QueryResultFuture {
163
- let result = match self . execute ( query , max_complexity , max_depth , max_first ) {
164
- Ok ( result ) => result ,
165
- Err ( e ) => QueryResult :: from ( e ) ,
166
- } ;
177
+ let result = self
178
+ . check_too_expensive ( & query )
179
+ . and_then ( |_| self . execute ( query , max_complexity , max_depth , max_first ) )
180
+ . unwrap_or_else ( |e| QueryResult :: from ( e ) ) ;
167
181
Box :: new ( future:: ok ( result) )
168
182
}
169
183
170
184
fn run_subscription ( & self , subscription : Subscription ) -> SubscriptionResultFuture {
185
+ if let Err ( errs) = self . check_too_expensive ( & subscription. query ) {
186
+ let err = SubscriptionError :: GraphQLError ( errs) ;
187
+ return Box :: new ( future:: result ( Err ( err) ) ) ;
188
+ }
189
+
171
190
let query = match crate :: execution:: Query :: new (
172
191
subscription. query ,
173
192
* GRAPHQL_MAX_COMPLEXITY ,
0 commit comments