Skip to content

Commit ca0adea

Browse files
authored
Fix @_required directive when the schema is available (#3685)
1 parent 7d9302b commit ca0adea

File tree

3 files changed

+107
-1
lines changed

3 files changed

+107
-1
lines changed

.changeset/breezy-tomatoes-sing.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@urql/exchange-graphcache': minor
3+
---
4+
5+
Allow @_required directive to be used in combination with configured schemas

exchanges/graphcache/src/cacheExchange.test.ts

+100
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,106 @@ describe('directives', () => {
15441544
expect(reexecuteOperation).toHaveBeenCalledTimes(0);
15451545
expect(result.mock.calls[0][0].data).toEqual(null);
15461546
});
1547+
1548+
it('does not return missing fields when nullable fields from a defined schema are marked as required in the query', () => {
1549+
const client = createClient({
1550+
url: 'http://0.0.0.0',
1551+
exchanges: [],
1552+
});
1553+
const { source: ops$, next } = makeSubject<Operation>();
1554+
1555+
const initialQuery = gql`
1556+
query {
1557+
latestTodo {
1558+
id
1559+
}
1560+
}
1561+
`;
1562+
1563+
const query = gql`
1564+
{
1565+
latestTodo {
1566+
id
1567+
author @_required {
1568+
id
1569+
name
1570+
}
1571+
}
1572+
}
1573+
`;
1574+
1575+
const initialQueryOperation = client.createRequestOperation('query', {
1576+
key: 1,
1577+
query: initialQuery,
1578+
variables: undefined,
1579+
});
1580+
1581+
const queryOperation = client.createRequestOperation('query', {
1582+
key: 2,
1583+
query,
1584+
variables: undefined,
1585+
});
1586+
1587+
const initialQueryResult: OperationResult = {
1588+
...queryResponse,
1589+
operation: initialQueryOperation,
1590+
data: {
1591+
__typename: 'Query',
1592+
latestTodo: {
1593+
__typename: 'Todo',
1594+
id: '1',
1595+
},
1596+
},
1597+
};
1598+
1599+
const queryResult: OperationResult = {
1600+
...queryResponse,
1601+
operation: queryOperation,
1602+
data: {
1603+
__typename: 'Query',
1604+
latestTodo: {
1605+
__typename: 'Todo',
1606+
id: '1',
1607+
author: null,
1608+
},
1609+
},
1610+
};
1611+
1612+
const response = vi.fn((forwardOp: Operation): OperationResult => {
1613+
if (forwardOp.key === 1) {
1614+
return initialQueryResult;
1615+
} else if (forwardOp.key === 2) {
1616+
return queryResult;
1617+
}
1618+
return undefined as any;
1619+
});
1620+
1621+
const result = vi.fn();
1622+
const forward: ExchangeIO = ops$ => pipe(ops$, map(response), share);
1623+
1624+
pipe(
1625+
cacheExchange({
1626+
schema: minifyIntrospectionQuery(
1627+
// eslint-disable-next-line
1628+
require('./test-utils/simple_schema.json')
1629+
),
1630+
})({ forward, client, dispatchDebug })(ops$),
1631+
tap(result),
1632+
publish
1633+
);
1634+
1635+
next(initialQueryOperation);
1636+
vi.runAllTimers();
1637+
next(queryOperation);
1638+
vi.runAllTimers();
1639+
1640+
expect(result.mock.calls[0][0].data).toEqual({
1641+
latestTodo: {
1642+
id: '1',
1643+
},
1644+
});
1645+
expect(result.mock.calls[1][0].data).toEqual(null);
1646+
});
15471647
});
15481648

15491649
describe('optimistic updates', () => {

exchanges/graphcache/src/operations/query.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,8 @@ const readSelection = (
534534
(directives.optional ||
535535
(optionalRef && !directives.required) ||
536536
!!getFieldError(ctx) ||
537-
(store.schema &&
537+
(!directives.required &&
538+
store.schema &&
538539
isFieldNullable(store.schema, typename, fieldName, ctx.store.logger)))
539540
) {
540541
// The field is uncached or has errored, so it'll be set to null and skipped

0 commit comments

Comments
 (0)