artemis_normalized_cache/store/
store.rs

1use crate::{
2    store::{
3        data::{FieldKey, InMemoryData, Link, RefFieldKey},
4        deserializer::ObjectDeserializer,
5        serializer::ObjectSerializer
6    },
7    HashSet
8};
9use artemis::{
10    codegen::FieldSelector,
11    exchange::{Client, Operation, OperationOptions, OperationResult},
12    utils::progressive_hash,
13    GraphQLQuery, QueryError, RequestPolicy, Response
14};
15use flurry::{epoch, epoch::Guard};
16use fnv::FnvBuildHasher;
17#[cfg(target_arch = "wasm32")]
18use serde::de::DeserializeOwned;
19use serde::{de::Deserialize, Serialize};
20use serde_json::Value;
21use std::{collections::HashMap, error::Error, fmt, sync::Arc};
22#[cfg(target_arch = "wasm32")]
23use wasm_bindgen::JsValue;
24
25pub struct Store {
26    data: InMemoryData
27}
28
29#[derive(Debug)]
30pub enum StoreError {
31    //InvalidMetadata(String)
32}
33impl Error for StoreError {}
34
35impl fmt::Display for StoreError {
36    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        unreachable!()
38        /*        match self {
39            StoreError::InvalidMetadata(msg) => write!(f, "Invalid metadata: {}", msg)
40        }*/
41    }
42}
43
44const TYPENAME: FieldKey = FieldKey("__typename", String::new());
45
46pub fn is_root(typename: &str) -> bool {
47    typename == "Query" || typename == "Mutation" || typename == "Subscription"
48}
49
50/// A reference to the store used to run custom query updates
51#[derive(Clone)]
52pub struct QueryStore {
53    pub(crate) store: Arc<Store>
54}
55
56impl QueryStore {
57    /// Run a custom update function against the cache.
58    ///
59    /// # Parameters
60    ///
61    /// * `_query` - The [`GraphQLQuery`](../artemis/trait.GraphQLQuery.html) object for the query
62    /// you want to update.
63    /// * `variables` - The `Variables` for the query you want to update. It will only update
64    /// cached results for that set of variables.
65    /// * `updater_fn` - The custom updater function. This takes in an `Option<ResponseData>` that
66    /// represents the current state and should return an `Option<ResponseData>` that represents
67    /// the new state. `None` means deleting the entry in this context.
68    /// The current state is cloned, so feel free to modify and return it.
69    /// * `dependencies` - This is passed into the update closure and should simply be passed
70    /// through.
71    pub fn update_query<'a, Q: GraphQLQuery, F>(
72        &'a self,
73        _query: Q,
74        variables: Q::Variables,
75        updater_fn: F,
76        dependencies: &mut HashSet<String>
77    ) where
78        F: FnOnce(Option<Q::ResponseData>) -> Option<Q::ResponseData> + 'a
79    {
80        self.store
81            .update_query::<Q, _>(variables, updater_fn, dependencies);
82    }
83}
84
85impl From<Arc<Store>> for QueryStore {
86    fn from(store: Arc<Store>) -> Self {
87        Self { store }
88    }
89}
90
91impl Store {
92    pub fn update_query<'a, Q: GraphQLQuery, F>(
93        &self,
94        variables: Q::Variables,
95        updater_fn: F,
96        dependencies: &mut HashSet<String>
97    ) where
98        F: FnOnce(Option<Q::ResponseData>) -> Option<Q::ResponseData> + 'a
99    {
100        let (query, meta) = Q::build_query(variables.clone());
101        let key = progressive_hash(meta.query_key, &variables);
102        let op = Operation {
103            key,
104            query,
105            meta: meta.clone(),
106            options: OperationOptions {
107                extensions: None,
108                extra_headers: None,
109                request_policy: RequestPolicy::CacheOnly,
110                url: "http://0.0.0.0".parse().unwrap()
111            }
112        };
113        let data = self.read_query::<Q>(&op, dependencies);
114        let updated_data = updater_fn(data);
115        if let Some(updated_data) = updated_data {
116            let result = OperationResult {
117                key,
118                meta,
119                response: Response {
120                    data: Some(updated_data),
121                    errors: None,
122                    debug_info: None
123                }
124            };
125            self.write_query::<Q>(&result, &variables, false, dependencies)
126                .unwrap();
127        }
128    }
129
130    #[cfg(target_arch = "wasm32")]
131    pub fn update_query_js<Q: GraphQLQuery>(
132        self: &Arc<Self>,
133        variables: Value,
134        updater_fn: js_sys::Function,
135        dependencies: *mut usize
136    ) where
137        Q::Variables: DeserializeOwned
138    {
139        let variables: Q::Variables = serde_json::from_value(variables).unwrap();
140        let updater = move |current_data: Option<Q::ResponseData>| -> Option<Q::ResponseData> {
141            let this = JsValue::NULL;
142            let current_data = serde_wasm_bindgen::to_value(&current_data).unwrap();
143            let result = updater_fn.call1(&this, &current_data);
144            serde_wasm_bindgen::from_value(result).unwrap()
145        };
146        let dependencies = dependencies as *mut _ as *mut HashSet<String>;
147        let dependencies = unsafe { &mut *dependencies };
148        self.update_query::<Q, _>(variables, updater, dependencies);
149    }
150
151    pub fn new(custom_keys: HashMap<&'static str, String, FnvBuildHasher>) -> Self {
152        Self {
153            data: InMemoryData::new(custom_keys)
154        }
155    }
156
157    pub fn key_of_entity(
158        &self,
159        typename: &str,
160        entity: &serde_json::Map<String, serde_json::Value>
161    ) -> Option<String> {
162        if is_root(typename) {
163            return Some(typename.to_string());
164        }
165
166        let custom_id_key = self.data.custom_keys.get(typename);
167        let id = if let Some(custom_key) = custom_id_key {
168            entity.get(custom_key).and_then(|val| val.as_str())
169        } else {
170            entity
171                .get("id")
172                .or_else(|| entity.get("_id"))
173                .and_then(|val| val.as_str())
174        };
175
176        id.map(|id| {
177            let mut key = String::with_capacity(typename.len() + id.len() + 1);
178            key.push_str(typename);
179            key.push_str(":");
180            key.push_str(id);
181            key
182        })
183    }
184
185    pub fn write_query<Q: GraphQLQuery>(
186        &self,
187        query: &OperationResult<Q::ResponseData>,
188        variables: &Q::Variables,
189        optimistic: bool,
190        dependencies: &mut HashSet<String>
191    ) -> Result<(), QueryError> {
192        if query.response.data.is_none() {
193            return Ok(());
194        }
195
196        let data: &Q::ResponseData = query.response.data.as_ref().unwrap();
197        let selection = Q::selection(variables);
198        let guard = epoch::pin();
199        let optimistic_key = if optimistic { Some(query.key) } else { None };
200        let root_key = query.meta.operation_type.to_string();
201
202        let serializer = ObjectSerializer::new(
203            &self.data,
204            &guard,
205            &selection,
206            query.meta.operation_type.to_str(),
207            Some(root_key),
208            dependencies,
209            optimistic_key
210        );
211
212        data.serialize(serializer)?;
213
214        if !optimistic {
215            self.data.set_dependencies(query.key, dependencies);
216            self.data.collect_garbage();
217        }
218
219        Ok(())
220    }
221
222    pub fn clear_optimistic_layer(&self, query_key: u64) {
223        self.data.clear_optimistic_layer(query_key);
224    }
225
226    pub fn read_query<Q: GraphQLQuery>(
227        &self,
228        query: &Operation<Q::Variables>,
229        dependencies: *mut HashSet<String>
230    ) -> Option<Q::ResponseData> {
231        let root_key = query.meta.operation_type.to_string();
232        let selection = Q::selection(&query.query.variables);
233        let guard = epoch::pin();
234        let deserializer =
235            ObjectDeserializer::new(&self.data, &selection, &root_key, &guard, dependencies);
236        /*        let value = self.read_entity(root_key, &selection, dependencies)?;
237        let data: Q::ResponseData =
238            serde_json::from_value(value).expect("Cache result didn't match type");*/
239        let data = Q::ResponseData::deserialize(deserializer);
240        match data {
241            Ok(data) => Some(data),
242            Err(e) if e.is_missing() => None,
243            Err(e) => panic!("{}", e)
244        }
245    }
246
247    #[inline]
248    fn field_key<'a>(field_name: &'static str, args: &'a String) -> RefFieldKey<'a> {
249        RefFieldKey(field_name, args)
250    }
251
252    fn invalidate_union(
253        &self,
254        optimistic_key: Option<u64>,
255        entity_key: &str,
256        subselection: &dyn Fn(&str) -> Vec<FieldSelector>,
257        invalidated: &mut HashSet<String>,
258        guard: &Guard
259    ) {
260        let typename = self
261            .data
262            .read_record(entity_key, (&TYPENAME).into(), guard)
263            .expect("Missing typename from union type. This is a codegen error.");
264        let typename = typename.as_str().unwrap();
265        let subselection = subselection(typename);
266        self.invalidate_selection(
267            optimistic_key,
268            entity_key,
269            &subselection,
270            invalidated,
271            guard
272        );
273    }
274
275    pub fn invalidate_query<Q: GraphQLQuery>(
276        &self,
277        result: &OperationResult<Q::ResponseData>,
278        variables: &Q::Variables,
279        optimistic: bool,
280        dependencies: &mut HashSet<String>
281    ) {
282        if result.response.data.is_none() {
283            return;
284        }
285        let key = result.meta.operation_type.to_string();
286
287        let selection = Q::selection(variables);
288        let guard = epoch::pin();
289        let optimistic_key = if optimistic { Some(result.key) } else { None };
290
291        self.invalidate_selection(optimistic_key, &key, &selection, dependencies, &guard);
292
293        if !optimistic {
294            self.data.clear_optimistic_layer(result.key);
295        }
296    }
297
298    pub fn rerun_queries<C: Client>(
299        &self,
300        entities: HashSet<String>,
301        originating_query: u64,
302        client: &C
303    ) {
304        //println!("Rerunning queries: {:?}", entities);
305        let queries: HashSet<_> = entities
306            .iter()
307            .filter(|it| *it != "Query")
308            .flat_map(|entity| self.data.get_dependencies(entity))
309            .filter(|it| *it != originating_query)
310            .collect();
311
312        for query in queries {
313            client.rerun_query(query);
314        }
315    }
316
317    fn write_record(
318        &self,
319        optimistic_key: Option<u64>,
320        entity_key: &str,
321        field_key: FieldKey,
322        value: Option<Value>
323    ) {
324        let guard = epoch::pin();
325        if let Some(optimistic_key) = optimistic_key {
326            self.data
327                .write_record_optimistic(optimistic_key, entity_key, field_key, value, &guard);
328        } else {
329            self.data.write_record(entity_key, field_key, value, &guard);
330        }
331    }
332
333    fn invalidate_selection(
334        &self,
335        optimistic_key: Option<u64>,
336        entity_key: &str,
337        selection: &[FieldSelector],
338        invalidated: &mut HashSet<String>,
339        guard: &Guard
340    ) {
341        if entity_key != "Mutation" {
342            invalidated.insert(entity_key.to_owned());
343        }
344        for field in selection {
345            match field {
346                FieldSelector::Scalar(field_name, args) => {
347                    self.write_record(
348                        optimistic_key,
349                        entity_key,
350                        FieldKey(*field_name, args.to_owned()),
351                        None
352                    );
353                }
354                FieldSelector::Object(field_name, args, _, subselection) => {
355                    let field_key = Self::field_key(*field_name, args);
356                    if let Some(link) = self.data.read_link(entity_key, field_key, &guard) {
357                        match link {
358                            Link::Single(entity_key) => self.invalidate_selection(
359                                optimistic_key,
360                                entity_key,
361                                subselection,
362                                invalidated,
363                                guard
364                            ),
365                            Link::List(entity_keys) => {
366                                for entity_key in entity_keys {
367                                    self.invalidate_selection(
368                                        optimistic_key.clone(),
369                                        entity_key,
370                                        subselection,
371                                        invalidated,
372                                        guard
373                                    );
374                                }
375                            }
376                            _ => {}
377                        }
378                    }
379                }
380                FieldSelector::Union(field_name, args, subselection) => {
381                    let field_key = Self::field_key(*field_name, args);
382                    if let Some(link) = self.data.read_link(entity_key, field_key, &guard) {
383                        match link {
384                            Link::Single(ref entity_key) => self.invalidate_union(
385                                optimistic_key,
386                                entity_key,
387                                &**subselection,
388                                invalidated,
389                                guard
390                            ),
391                            Link::List(ref entity_keys) => {
392                                for entity_key in entity_keys {
393                                    self.invalidate_union(
394                                        optimistic_key.clone(),
395                                        entity_key,
396                                        &**subselection,
397                                        invalidated,
398                                        guard
399                                    )
400                                }
401                            }
402                            Link::Null => {}
403                        }
404                    }
405                }
406            }
407        }
408    }
409}