Skip to content

Commit 30e9ae6

Browse files
committed
Allow putting __typename inside fragments where it is required
1 parent 2931a30 commit 30e9ae6

File tree

2 files changed

+63
-7
lines changed

2 files changed

+63
-7
lines changed

graphql_client_codegen/src/fragments.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::cell::Cell;
55

66
/// Represents a fragment extracted from a query document.
77
#[derive(Debug, PartialEq)]
8-
pub struct GqlFragment {
8+
pub(crate) struct GqlFragment {
99
/// The name of the fragment, matching one-to-one with the name in the GraphQL query document.
1010
pub name: String,
1111
/// The `on` clause of the fragment.

graphql_client_codegen/src/selection.rs

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,35 @@ impl SelectionItem {
4141
pub struct Selection(pub Vec<SelectionItem>);
4242

4343
impl Selection {
44-
pub(crate) fn extract_typename(
45-
&self,
46-
context: &crate::query::QueryContext,
44+
pub(crate) fn extract_typename<'s, 'context: 's>(
45+
&'s self,
46+
context: &'context crate::query::QueryContext,
4747
) -> Option<&SelectionField> {
48-
self.0.iter().filter_map(|f| f.as_typename()).next()
48+
// __typename is selected directly
49+
if let Some(field) = self.0.iter().filter_map(|f| f.as_typename()).next() {
50+
return Some(field);
51+
};
52+
53+
// typename is selected through a fragment
54+
self.0
55+
.iter()
56+
.filter_map(|f| match f {
57+
SelectionItem::FragmentSpread(SelectionFragmentSpread { fragment_name }) => {
58+
Some(fragment_name)
59+
}
60+
_ => None,
61+
})
62+
.filter_map(|fragment_name| {
63+
let fragment = context.fragments.get(fragment_name);
64+
65+
fragment.and_then(|fragment| fragment.selection.extract_typename(context))
66+
})
67+
.next()
68+
}
69+
70+
#[cfg(test)]
71+
pub(crate) fn new_empty() -> Selection {
72+
Selection(Vec::new())
4973
}
5074
}
5175

@@ -93,12 +117,44 @@ mod tests {
93117

94118
#[test]
95119
fn selection_extract_typename_simple_case() {
96-
let mut selection = Selection(Vec::new());
97-
let context = query::QueryContext::new_empty();
120+
let selection = Selection::new_empty();
121+
let context = crate::query::QueryContext::new_empty();
98122

99123
assert!(selection.extract_typename(&context).is_none());
100124
}
101125

126+
#[test]
127+
fn selection_extract_typename_in_fragemnt() {
128+
let mut selection = Selection::new_empty();
129+
selection
130+
.0
131+
.push(SelectionItem::FragmentSpread(SelectionFragmentSpread {
132+
fragment_name: "MyFragment".to_owned(),
133+
}));
134+
135+
let mut fragment_selection = Selection::new_empty();
136+
fragment_selection
137+
.0
138+
.push(SelectionItem::Field(SelectionField {
139+
alias: None,
140+
name: "__typename".to_string(),
141+
fields: Selection::new_empty(),
142+
}));
143+
144+
let mut context = crate::query::QueryContext::new_empty();
145+
context.fragments.insert(
146+
"MyFragment".to_string(),
147+
crate::fragments::GqlFragment {
148+
name: "MyFragment".to_string(),
149+
on: "something".into(),
150+
selection: fragment_selection,
151+
is_required: std::cell::Cell::new(false),
152+
},
153+
);
154+
155+
assert!(selection.extract_typename(&context).is_some());
156+
}
157+
102158
#[test]
103159
fn selection_from_graphql_parser_selection_set() {
104160
let query = r##"

0 commit comments

Comments
 (0)