-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Specifically handle the connection directive when selecting store keys, fix #1779 #1801
Changes from all commits
f71304f
a6f7af4
dd32cb6
c8ec6fb
699ddd6
6d62776
3e136bd
33aaf02
4f4c455
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,38 @@ function addTypenameToSelectionSet( | |
} | ||
} | ||
|
||
function removeConnectionDirectiveFromSelectionSet(selectionSet: SelectionSetNode) { | ||
if (selectionSet.selections) { | ||
selectionSet.selections.forEach((selection) => { | ||
if (selection.kind === 'Field' && selection as FieldNode && selection.directives) { | ||
selection.directives = selection.directives.filter((directive) => { | ||
const willRemove = directive.name.value === 'connection'; | ||
if (willRemove) { | ||
if (!directive.arguments || !directive.arguments.some((arg) => arg.name.value === 'key')) { | ||
console.warn('Removing an @connection directive even though it does not have a key. ' + | ||
'You may want to use the key parameter to specify a store key.'); | ||
} | ||
} | ||
|
||
return !willRemove; | ||
}); | ||
} | ||
}); | ||
|
||
selectionSet.selections.forEach((selection) => { | ||
if (selection.kind === 'Field') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could just "duck type" here and check if |
||
if (selection.selectionSet) { | ||
removeConnectionDirectiveFromSelectionSet(selection.selectionSet); | ||
} | ||
} else if (selection.kind === 'InlineFragment') { | ||
if (selection.selectionSet) { | ||
removeConnectionDirectiveFromSelectionSet(selection.selectionSet); | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
|
||
export function addTypenameToDocument(doc: DocumentNode) { | ||
checkDocument(doc); | ||
const docClone = cloneDeep(doc); | ||
|
@@ -62,3 +94,14 @@ export function addTypenameToDocument(doc: DocumentNode) { | |
|
||
return docClone; | ||
} | ||
|
||
export function removeConnectionDirectiveFromDocument(doc: DocumentNode) { | ||
checkDocument(doc); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we're now doing all of these twice (for addTypename as well), it might make more sense to just have one function called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind, we won't do it in the same place, so this comment doesn't apply any more. |
||
const docClone = cloneDeep(doc); | ||
|
||
docClone.definitions.forEach((definition: DefinitionNode) => { | ||
removeConnectionDirectiveFromSelectionSet((definition as OperationDefinitionNode).selectionSet); | ||
}); | ||
|
||
return docClone; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2554,8 +2554,137 @@ describe('client', () => { | |
|
||
return withWarning(() => client.query({ query }), /Missing field description/); | ||
}); | ||
|
||
it('runs a query with the connection directive and writes it to the store key defined in the directive', () => { | ||
const query = gql` | ||
{ | ||
books(skip: 0, limit: 2) @connection(key: "abc") { | ||
name | ||
} | ||
}`; | ||
|
||
const transformedQuery = gql` | ||
{ | ||
books(skip: 0, limit: 2) @connection(key: "abc") { | ||
name | ||
__typename | ||
} | ||
}`; | ||
|
||
const result = { | ||
'books': [ | ||
{ | ||
'name': 'abcd', | ||
'__typename': 'Book', | ||
}, | ||
], | ||
}; | ||
|
||
const networkInterface = mockNetworkInterface({ | ||
request: { query: transformedQuery }, | ||
result: { data: result }, | ||
}); | ||
|
||
const client = new ApolloClient({ | ||
networkInterface, | ||
}); | ||
|
||
return client.query({ query }).then((actualResult) => { | ||
assert.deepEqual(actualResult.data, result); | ||
}); | ||
}); | ||
|
||
it('should not remove the connection directive at the store level', () => { | ||
const query = gql` | ||
{ | ||
books(skip: 0, limit: 2) @connection { | ||
name | ||
} | ||
}`; | ||
|
||
const transformedQuery = gql` | ||
{ | ||
books(skip: 0, limit: 2) @connection { | ||
name | ||
__typename | ||
} | ||
}`; | ||
|
||
const result = { | ||
'books': [ | ||
{ | ||
'name': 'abcd', | ||
'__typename': 'Book', | ||
}, | ||
], | ||
}; | ||
|
||
const networkInterface = mockNetworkInterface({ | ||
request: { query: transformedQuery }, | ||
result: { data: result }, | ||
}); | ||
|
||
const client = new ApolloClient({ | ||
networkInterface, | ||
}); | ||
|
||
return client.query({ query }).then((actualResult) => { | ||
assert.deepEqual(actualResult.data, result); | ||
}); | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There should be an end-to-end test as well that checks that the store contains the right objects after a query. The structure can be the same as for this test. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a test to check if it prints a warning when |
||
}); | ||
|
||
it('should run a query with the connection directive and write the result to the store key defined in the directive', () => { | ||
const query = gql` | ||
{ | ||
books(skip: 0, limit: 2) @connection(key: "abc") { | ||
name | ||
} | ||
}`; | ||
|
||
const transformedQuery = gql` | ||
{ | ||
books(skip: 0, limit: 2) @connection(key: "abc") { | ||
name | ||
__typename | ||
} | ||
}`; | ||
|
||
const result = { | ||
'books': [ | ||
{ | ||
'name': 'abcd', | ||
'__typename': 'Book', | ||
}, | ||
], | ||
}; | ||
|
||
const networkInterface = mockNetworkInterface({ | ||
request: { query: transformedQuery }, | ||
result: { data: result }, | ||
}); | ||
|
||
const client = new ApolloClient({ | ||
networkInterface, | ||
}); | ||
|
||
return client.query({ query }).then((actualResult) => { | ||
assert.deepEqual(actualResult.data, result); | ||
assert.deepEqual(client.store.getState().apollo.data, { | ||
'ROOT_QUERY.abc.0': { name: 'abcd', __typename: 'Book' }, | ||
'ROOT_QUERY': { | ||
abc: [ | ||
{ | ||
'generated': true, | ||
'id': 'ROOT_QUERY.abc.0', | ||
'type': 'id', | ||
}, | ||
], | ||
}, | ||
}); | ||
}); | ||
}); | ||
|
||
function clientRoundtrip( | ||
query: DocumentNode, | ||
data: ExecutionResult, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not include the recursion inside the previous loop?