1#![cfg_attr(
6 not(test),
7 deny(
8 clippy::exit,
9 clippy::panic,
10 clippy::unwrap_used,
11 clippy::expect_used,
12 clippy::indexing_slicing,
13 clippy::unimplemented,
14 clippy::todo,
15 missing_docs
16 )
17)]
18
19use apollo_compiler::ExecutableDocument;
20use apollo_compiler::Node;
21use apollo_compiler::collections::IndexSet;
22use apollo_compiler::executable::Field;
23use apollo_compiler::executable::FieldSet;
24use apollo_compiler::executable::Selection;
25use apollo_compiler::executable::SelectionSet;
26use multimap::MultiMap;
27
28use super::lit_expr::LitExpr;
29use super::location::Ranged;
30use super::location::WithRange;
31use super::parser::PathList;
32use crate::connectors::JSONSelection;
33use crate::connectors::PathSelection;
34use crate::connectors::SubSelection;
35use crate::connectors::json_selection::Alias;
36use crate::connectors::json_selection::NamedSelection;
37use crate::connectors::json_selection::NamingPrefix;
38use crate::connectors::json_selection::TopLevelSelection;
39
40impl JSONSelection {
41 pub fn apply_selection_set(
49 &self,
50 abstract_types: &IndexSet<String>,
51 document: &ExecutableDocument,
52 selection_set: &SelectionSet,
53 required_keys: Option<&FieldSet>,
54 ) -> Self {
55 let selection_set = required_keys.map_or_else(
56 || selection_set.clone(),
57 |keys| {
58 keys.selection_set.selections.iter().cloned().fold(
59 selection_set.clone(),
60 |mut acc, selection| {
61 acc.push(selection);
62 acc
63 },
64 )
65 },
66 );
67
68 match &self.inner {
69 TopLevelSelection::Named(sub) => Self {
70 inner: TopLevelSelection::Named(sub.apply_selection_set(
71 abstract_types,
72 document,
73 &selection_set,
74 )),
75 spec: self.spec,
76 },
77 TopLevelSelection::Value(lit) => {
78 let new_lit = match lit.as_ref() {
85 LitExpr::Path(path) => {
86 let refined =
87 path.apply_selection_set(abstract_types, document, &selection_set);
88 WithRange::new(LitExpr::Path(refined), lit.range())
89 }
90 _ => lit.clone(),
91 };
92 Self {
93 inner: TopLevelSelection::Value(new_lit),
94 spec: self.spec,
95 }
96 }
97 }
98 }
99}
100
101impl SubSelection {
102 pub fn apply_selection_set(
104 &self,
105 abstract_types: &IndexSet<String>,
106 document: &ExecutableDocument,
107 selection_set: &SelectionSet,
108 ) -> Self {
109 let mut new_selections = Vec::new();
110 let field_map = map_fields_by_name(document, selection_set);
111
112 if field_map.contains_key("__typename")
115 && !abstract_types.contains(&selection_set.ty.to_string())
119 {
120 debug_assert_ne!(selection_set.ty.to_string(), "_Entity");
126
127 new_selections.push(NamedSelection {
128 prefix: NamingPrefix::Alias(Alias::new("__typename")),
129 path: WithRange::new(
135 LitExpr::Path(PathSelection {
136 path: WithRange::new(
137 PathList::Expr(
138 WithRange::new(LitExpr::String(selection_set.ty.to_string()), None),
139 WithRange::new(PathList::Empty, None),
140 ),
141 None,
142 ),
143 }),
144 None,
145 ),
146 });
147 }
148
149 fn apply_to_named_value(
154 value: &WithRange<LitExpr>,
155 abstract_types: &IndexSet<String>,
156 document: &ExecutableDocument,
157 selection_set: &SelectionSet,
158 ) -> WithRange<LitExpr> {
159 match value.as_ref() {
160 LitExpr::Path(path) => {
161 let refined = path.apply_selection_set(abstract_types, document, selection_set);
162 WithRange::new(LitExpr::Path(refined), value.range())
163 }
164 _ => value.clone(),
165 }
166 }
167
168 for selection in &self.selections {
169 if let Some(single_key_for_selection) = selection.get_single_key() {
170 if let Some(fields) = field_map.get_vec(single_key_for_selection.as_str()) {
173 for field in fields {
174 let applied_path = apply_to_named_value(
175 &selection.path,
176 abstract_types,
177 document,
178 &field.selection_set,
179 );
180
181 new_selections.push(NamedSelection {
182 prefix: selection.prefix.clone(),
183 path: applied_path,
184 });
185 }
186 } else {
187 }
190 } else {
191 new_selections.push(NamedSelection {
196 prefix: selection.prefix.clone(),
197 path: apply_to_named_value(
198 &selection.path,
199 abstract_types,
200 document,
201 selection_set,
202 ),
203 });
204 }
205 }
206
207 Self {
208 selections: new_selections,
209 range: self.range.clone(),
213 }
214 }
215}
216
217impl PathSelection {
218 pub fn apply_selection_set(
220 &self,
221 abstract_types: &IndexSet<String>,
222 document: &ExecutableDocument,
223 selection_set: &SelectionSet,
224 ) -> Self {
225 Self {
226 path: WithRange::new(
227 self.path
228 .apply_selection_set(abstract_types, document, selection_set),
229 self.path.range(),
230 ),
231 }
232 }
233}
234
235impl PathList {
236 pub(crate) fn apply_selection_set(
237 &self,
238 abstract_types: &IndexSet<String>,
239 document: &ExecutableDocument,
240 selection_set: &SelectionSet,
241 ) -> Self {
242 match self {
243 Self::Var(name, path) => Self::Var(
244 name.clone(),
245 WithRange::new(
246 path.apply_selection_set(abstract_types, document, selection_set),
247 path.range(),
248 ),
249 ),
250 Self::Key(key, path) => Self::Key(
251 key.clone(),
252 WithRange::new(
253 path.apply_selection_set(abstract_types, document, selection_set),
254 path.range(),
255 ),
256 ),
257 Self::Expr(expr, path) => Self::Expr(
258 expr.clone(),
259 WithRange::new(
260 path.apply_selection_set(abstract_types, document, selection_set),
261 path.range(),
262 ),
263 ),
264 Self::Method(method_name, args, path) => Self::Method(
265 method_name.clone(),
266 args.clone(),
267 WithRange::new(
268 path.apply_selection_set(abstract_types, document, selection_set),
269 path.range(),
270 ),
271 ),
272 Self::Question(tail) => Self::Question(WithRange::new(
273 tail.apply_selection_set(abstract_types, document, selection_set),
274 tail.range(),
275 )),
276 Self::Selection(sub) => {
277 Self::Selection(sub.apply_selection_set(abstract_types, document, selection_set))
278 }
279 Self::Empty => Self::Empty,
280 }
281 }
282}
283
284fn map_fields_by_name<'a>(
285 document: &'a ExecutableDocument,
286 set: &'a SelectionSet,
287) -> MultiMap<String, &'a Node<Field>> {
288 let mut map = MultiMap::new();
289 map_fields_by_name_impl(document, set, &mut map);
290 map
291}
292
293fn map_fields_by_name_impl<'a>(
294 document: &'a ExecutableDocument,
295 set: &'a SelectionSet,
296 map: &mut MultiMap<String, &'a Node<Field>>,
297) {
298 for selection in &set.selections {
299 match selection {
300 Selection::Field(field) => {
301 map.insert(field.name.to_string(), field);
302 }
303 Selection::FragmentSpread(f) => {
304 if let Some(fragment) = f.fragment_def(document) {
305 map_fields_by_name_impl(document, &fragment.selection_set, map);
306 }
307 }
308 Selection::InlineFragment(fragment) => {
309 map_fields_by_name_impl(document, &fragment.selection_set, map);
310 }
311 }
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use apollo_compiler::ExecutableDocument;
318 use apollo_compiler::Schema;
319 use apollo_compiler::collections::IndexSet;
320 use apollo_compiler::executable::FieldSet;
321 use apollo_compiler::executable::SelectionSet;
322 use apollo_compiler::name;
323 use apollo_compiler::validation::Valid;
324 use pretty_assertions::assert_eq;
325
326 use crate::assert_snapshot;
327
328 fn selection_set(schema: &Valid<Schema>, s: &str) -> (ExecutableDocument, SelectionSet) {
329 let document = ExecutableDocument::parse_and_validate(schema, s, "./").unwrap();
330 let selection_set = document
331 .operations
332 .anonymous
333 .as_ref()
334 .unwrap()
335 .selection_set
336 .fields()
337 .next()
338 .unwrap()
339 .selection_set
340 .clone();
341 (document.into_inner(), selection_set)
342 }
343
344 #[test]
345 fn test() {
346 let json = super::JSONSelection::parse(
347 r###"
348 $.result {
349 a
350 b: c
351 d: e.f
352 g
353 h: 'i-j'
354 k: { l m: n }
355 }
356 "###,
357 )
358 .unwrap();
359
360 let schema = Schema::parse_and_validate(
361 r###"
362 type Query {
363 t: T
364 }
365
366 type T {
367 a: String
368 b: String
369 d: String
370 g: String
371 h: String
372 k: K
373 }
374
375 type K {
376 l: String
377 m: String
378 }
379 "###,
380 "./",
381 )
382 .unwrap();
383
384 let (document, selection_set) = selection_set(
385 &schema,
386 "{ t { z: a, y: b, x: d, w: h v: k { u: l t: m } } }",
387 );
388
389 let transformed =
390 json.apply_selection_set(&IndexSet::default(), &document, &selection_set, None);
391 assert_eq!(
392 transformed.to_string(),
393 r###"$.result {
394 a
395 b: c
396 d: e.f
397 h: "i-j"
398 k: {
399 l
400 m: n
401 }
402}"###
403 );
404 }
405
406 #[test]
407 fn test_star() {
408 let json_selection = super::JSONSelection::parse(
409 r###"
410 $.result {
411 a
412 b_alias: b
413 c {
414 d
415 e_alias: e
416 h: "h"
417 i: "i"
418 group: {
419 j
420 k
421 }
422 }
423 path_to_f: c.f
424 }
425 "###,
426 )
427 .unwrap();
428
429 let schema = Schema::parse_and_validate(
430 r###"
431 type Query {
432 t: T
433 }
434
435 type T {
436 a: String
437 b_alias: String
438 c: C
439 path_to_f: String
440 }
441
442 type C {
443 d: String
444 e_alias: String
445 h: String
446 i: String
447 group: Group
448 }
449
450 type Group {
451 j: String
452 k: String
453 }
454 "###,
455 "./",
456 )
457 .unwrap();
458
459 let (document, selection_set) = selection_set(
460 &schema,
461 "{ t { a b_alias c { e: e_alias h group { j } } path_to_f } }",
462 );
463
464 let transformed = json_selection.apply_selection_set(
465 &IndexSet::default(),
466 &document,
467 &selection_set,
468 None,
469 );
470 assert_eq!(
471 transformed.to_string(),
472 r###"$.result {
473 a
474 b_alias: b
475 c {
476 e_alias: e
477 h: "h"
478 group: {
479 j
480 }
481 }
482 path_to_f: c.f
483}"###
484 );
485
486 let data = serde_json_bytes::json!({
487 "result": {
488 "a": "a",
489 "b": "b",
490 "c": {
491 "d": "d",
492 "e": "e",
493 "f": "f",
494 "g": "g",
495 "h": "h",
496 "i": "i",
497 "j": "j",
498 "k": "k",
499 },
500 }
501 });
502 let result = transformed.apply_to(&data);
503 assert_eq!(
504 result,
505 (
506 Some(serde_json_bytes::json!(
507 {
508 "a": "a",
509 "b_alias": "b",
510 "c": {
511 "e_alias": "e",
512 "h": "h",
513 "group": {
514 "j": "j"
515 },
516 },
517 "path_to_f": "f",
518 })),
519 vec![]
520 )
521 );
522 }
523
524 #[test]
525 fn test_depth() {
526 let json = super::JSONSelection::parse(
527 r###"
528 $.result {
529 a {
530 b {
531 renamed: c
532 }
533 }
534 }
535 "###,
536 )
537 .unwrap();
538
539 let schema = Schema::parse_and_validate(
540 r###"
541 type Query {
542 t: T
543 }
544
545 type T {
546 a: A
547 }
548
549 type A {
550 b: B
551 }
552
553 type B {
554 renamed: String
555 }
556 "###,
557 "./",
558 )
559 .unwrap();
560
561 let (document, selection_set) = selection_set(&schema, "{ t { a { b { renamed } } } }");
562
563 let transformed =
564 json.apply_selection_set(&IndexSet::default(), &document, &selection_set, None);
565 assert_eq!(
566 transformed.to_string(),
567 r###"$.result {
568 a {
569 b {
570 renamed: c
571 }
572 }
573}"###
574 );
575
576 let data = serde_json_bytes::json!({
577 "result": {
578 "a": {
579 "b": {
580 "c": "c",
581 }
582 }
583 }
584 }
585 );
586 let result = transformed.apply_to(&data);
587 assert_eq!(
588 result,
589 (
590 Some(serde_json_bytes::json!({"a": { "b": { "renamed": "c" } } } )),
591 vec![]
592 )
593 );
594 }
595
596 #[test]
597 fn test_typename() {
598 let json = super::JSONSelection::parse(
599 r###"
600 $.result {
601 id
602 author: {
603 id: authorId
604 }
605 }
606 "###,
607 )
608 .unwrap();
609
610 let schema = Schema::parse_and_validate(
611 r###"
612 type Query {
613 t: T
614 }
615
616 type T {
617 id: ID
618 author: A
619 }
620
621 type A {
622 id: ID
623 }
624 "###,
625 "./",
626 )
627 .unwrap();
628
629 let (document, selection_set) =
630 selection_set(&schema, "{ t { id __typename author { __typename id } } }");
631
632 let transformed =
633 json.apply_selection_set(&IndexSet::default(), &document, &selection_set, None);
634 assert_eq!(
635 transformed.to_string(),
636 r###"$.result {
637 __typename: $("T")
638 id
639 author: {
640 __typename: $("A")
641 id: authorId
642 }
643}"###
644 );
645 }
646
647 #[test]
648 fn test_fragments() {
649 let json = super::JSONSelection::parse(
650 r###"
651 reviews: result {
652 id
653 product: { upc: product_upc }
654 author: { id: author_id }
655 }
656 "###,
657 )
658 .unwrap();
659
660 let schema = Schema::parse_and_validate(
661 r###"
662 type Query {
663 _entities(representations: [_Any!]!): [_Entity]
664 }
665
666 scalar _Any
667
668 union _Entity = Product
669
670 type Product {
671 upc: String
672 reviews: [Review]
673 }
674
675 type Review {
676 id: ID
677 product: Product
678 author: User
679 }
680
681 type User {
682 id: ID
683 }
684 "###,
685 "./",
686 )
687 .unwrap();
688
689 let (document, selection_set) = selection_set(
690 &schema,
691 "query ($representations: [_Any!]!) {
692 _entities(representations: $representations) {
693 ..._generated_onProduct1_0
694 }
695 }
696 fragment _generated_onProduct1_0 on Product {
697 reviews {
698 id
699 product {
700 __typename
701 upc
702 }
703 author {
704 __typename
705 id
706 }
707 }
708 }",
709 );
710
711 let transformed =
712 json.apply_selection_set(&IndexSet::default(), &document, &selection_set, None);
713 assert_eq!(
714 transformed.to_string(),
715 r###"reviews: result {
716 id
717 product: {
718 __typename: $("Product")
719 upc: product_upc
720 }
721 author: {
722 __typename: $("User")
723 id: author_id
724 }
725}"###
726 );
727 }
728
729 #[test]
730 fn test_ensuring_key_fields() {
731 let json = super::JSONSelection::parse(
732 r###"
733 id
734 store { id }
735 name
736 price
737 "###,
738 )
739 .unwrap();
740
741 let schema = Schema::parse_and_validate(
742 r###"
743 type Query {
744 product: Product
745 }
746
747 type Product {
748 id: ID!
749 store: Store!
750 name: String!
751 price: String
752 }
753
754 type Store {
755 id: ID!
756 }
757 "###,
758 "./",
759 )
760 .unwrap();
761
762 let (document, selection_set) = selection_set(&schema, "{ product { name price } }");
763
764 let keys =
765 FieldSet::parse_and_validate(&schema, name!(Product), "id store { id }", "").unwrap();
766
767 let transformed =
768 json.apply_selection_set(&IndexSet::default(), &document, &selection_set, Some(&keys));
769 assert_snapshot!(transformed.to_string(), @r"
770 id
771 store {
772 id
773 }
774 name
775 price
776 ");
777 }
778}