artemis_normalized_cache/store/
store.rs1use 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 }
33impl Error for StoreError {}
34
35impl fmt::Display for StoreError {
36 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 unreachable!()
38 }
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#[derive(Clone)]
52pub struct QueryStore {
53 pub(crate) store: Arc<Store>
54}
55
56impl QueryStore {
57 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(¤t_data).unwrap();
143 let result = updater_fn.call1(&this, ¤t_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 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 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}