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