Skip to content

Commit 57aadba

Browse files
committed
feat: support predicates on values, in addition to just target ids
1 parent 02f2455 commit 57aadba

File tree

5 files changed

+92
-67
lines changed

5 files changed

+92
-67
lines changed

README.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -317,12 +317,15 @@ The `__typename` of the GraphQL type that was created or deleted, or an array of
317317
array is given, a query must match all of the conditions in the array to be
318318
refetched.
319319
320-
##### `ids: any` (_optional_)
320+
##### `predicate: any` (_optional_)
321321
322-
A single id, an array of ids, or a `Set` of ids that were deleted. If given,
323-
only active queries whose current result contains an object with the given
324-
`typename` and `id` will be refetched.
322+
A single id, an array of ids, or a `Set` of ids that were deleted, or a
323+
predicate that takes an instance of the GraphQL type and returns `true` if the
324+
query should be refetched. If given, only active queries whose current result
325+
matches the predicate or contains an object with the given `typename` and `id`
326+
will be refetched.
325327
326328
##### `idField: string` (_optional, default_: `'id'`)
327329
328-
The name of the id field in the type that was deleted.
330+
The name of the id field in the type that was deleted. This is only used if
331+
`predicate` is not an id, array, or `Set` of ids, rather than a `function`.

src/doesQueryContain.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,15 @@ export default function doesQueryContain(
2121
types: Types,
2222
typename: string,
2323
data?: any,
24-
ids?: ?Set<any>,
25-
idField?: string = 'id'
24+
predicate?: ?(data: any) => boolean
2625
): boolean {
2726
const targetType = types[typename]
2827
if (!targetType) throw new Error(`type not found: ${typename}`)
2928
const potentialAncestors = getPotentialAncestors(targetType)
3029

3130
function doesNodeContain(node: Node, data: any, type: Type): boolean {
3231
if (type === targetType) {
33-
if (!ids || (data && ids.has(data[idField]))) return true
32+
if (!predicate || predicate(data)) return true
3433
}
3534
if (!type.name) return false
3635
const ancestorEntry = potentialAncestors.get(type)
@@ -54,9 +53,9 @@ export default function doesQueryContain(
5453
innerType = innerType.ofType
5554
}
5655
let innerData = data ? data[alias ? alias.value : name.value] : null
57-
if (ids && innerData == null) continue
56+
if (predicate && innerData == null) continue
5857

59-
if (ids && list) {
58+
if (predicate && list) {
6059
if (!Array.isArray(innerData)) continue
6160
for (let element of innerData) {
6261
if (doesNodeContain((selection: any), element, innerType)) {

src/index.js

+15-9
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ import getSchemaTypes from './getSchemaTypes'
55
import type { Types } from './getSchemaTypes'
66
import doesQueryContain from './doesQueryContain'
77

8-
function normalizeIds(ids: any): Set<any> {
9-
if (ids instanceof Set) return ids
10-
if (Array.isArray(ids)) return new Set(ids)
11-
return new Set([ids])
8+
function normalizePredicate(
9+
predicate: any,
10+
idField: string
11+
): (data: any) => boolean {
12+
if (typeof predicate === 'function') return predicate
13+
let ids = predicate
14+
if (Array.isArray(ids)) ids = new Set(ids)
15+
else if (!(ids instanceof Set)) ids = new Set([ids])
16+
return data => ids.has(data[idField])
1217
}
1318

1419
type Term = [string, any, ?string] | [string, any] | [string]
@@ -26,7 +31,7 @@ function every<T>(
2631
export default async function refetch(
2732
client: mixed,
2833
typenameOrTerms: string | $ReadOnlyArray<Term>,
29-
ids?: ?any,
34+
predicate?: ?any,
3035
idField?: string
3136
): Promise<any> {
3237
if (!(client instanceof ApolloClient))
@@ -38,7 +43,7 @@ export default async function refetch(
3843

3944
let terms
4045
if (typeof typenameOrTerms === 'string') {
41-
terms = [[typenameOrTerms, ids, idField]]
46+
terms = [[typenameOrTerms, predicate, idField]]
4247
} else if (Array.isArray(typenameOrTerms)) {
4348
terms = typenameOrTerms
4449
} else {
@@ -57,14 +62,15 @@ export default async function refetch(
5762
if (currentResult) data = currentResult.data
5863

5964
if (
60-
every(terms, ([typename, ids, idField]: any) =>
65+
every(terms, ([typename, predicate, idField]: any) =>
6166
doesQueryContain(
6267
document,
6368
types,
6469
typename,
6570
data,
66-
ids != null ? normalizeIds(ids) : null,
67-
idField || 'id'
71+
predicate != null
72+
? normalizePredicate(predicate, idField || 'id')
73+
: null
6874
)
6975
)
7076
) {

test/doesQueryContain.js

+63-46
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,19 @@ describe(`doesQueryContain`, function() {
4242
expect(doesQueryContain(document, types, 'Organization')).to.be.false
4343
expect(doesQueryContain(document, types, 'User')).to.be.false
4444

45-
expect(doesQueryContain(document, types, 'Device', data, new Set([1]))).to
46-
.be.true
47-
expect(doesQueryContain(document, types, 'Device', data, new Set([1, 2])))
45+
expect(doesQueryContain(document, types, 'Device', data, d => d.id === 1))
4846
.to.be.true
49-
expect(doesQueryContain(document, types, 'Device', data, new Set([2]))).to
50-
.be.false
47+
expect(
48+
doesQueryContain(
49+
document,
50+
types,
51+
'Device',
52+
data,
53+
d => d.id === 1 || d.id === 2
54+
)
55+
).to.be.true
56+
expect(doesQueryContain(document, types, 'Device', data, d => d.id === 2))
57+
.to.be.false
5158
})
5259
it(`connection test`, function() {
5360
const document = gql`
@@ -74,14 +81,21 @@ describe(`doesQueryContain`, function() {
7481
.false
7582
expect(doesQueryContain(document, types, 'User')).to.be.false
7683

77-
expect(doesQueryContain(document, types, 'Device', data, new Set([1]))).to
78-
.be.true
79-
expect(doesQueryContain(document, types, 'Device', data, new Set([2]))).to
80-
.be.true
81-
expect(doesQueryContain(document, types, 'Device', data, new Set([1, 2])))
84+
expect(doesQueryContain(document, types, 'Device', data, d => d.id === 1))
85+
.to.be.true
86+
expect(doesQueryContain(document, types, 'Device', data, d => d.id === 2))
8287
.to.be.true
83-
expect(doesQueryContain(document, types, 'Device', data, new Set([3]))).to
84-
.be.false
88+
expect(
89+
doesQueryContain(
90+
document,
91+
types,
92+
'Device',
93+
data,
94+
d => d.id === 1 || d.id === 2
95+
)
96+
).to.be.true
97+
expect(doesQueryContain(document, types, 'Device', data, d => d.id === 3))
98+
.to.be.false
8599
})
86100
it(`more complex test`, function() {
87101
const document = gql`
@@ -133,27 +147,40 @@ describe(`doesQueryContain`, function() {
133147
expect(doesQueryContain(document, types, 'User')).to.be.false
134148

135149
expect(
136-
doesQueryContain(document, types, 'Organization', data, new Set([1]))
150+
doesQueryContain(document, types, 'Organization', data, d => d.id === 1)
137151
).to.be.true
138152
expect(
139-
doesQueryContain(document, types, 'Organization', data, new Set([1, 2]))
153+
doesQueryContain(
154+
document,
155+
types,
156+
'Organization',
157+
data,
158+
d => d.id === 1 || d.id === 2
159+
)
140160
).to.be.true
141161
expect(
142-
doesQueryContain(document, types, 'Organization', data, new Set([3]))
162+
doesQueryContain(document, types, 'Organization', data, d => d.id === 3)
143163
).to.be.false
144164

145-
expect(doesQueryContain(document, types, 'Device', data, new Set([1]))).to
146-
.be.true
147-
expect(doesQueryContain(document, types, 'Device', data, new Set([2]))).to
148-
.be.true
149-
expect(doesQueryContain(document, types, 'Device', data, new Set([1, 2])))
165+
expect(doesQueryContain(document, types, 'Device', data, d => d.id === 1))
150166
.to.be.true
151-
expect(doesQueryContain(document, types, 'Device', data, new Set([3]))).to
152-
.be.true
153-
expect(doesQueryContain(document, types, 'Device', data, new Set([4]))).to
154-
.be.true
155-
expect(doesQueryContain(document, types, 'Device', data, new Set([5]))).to
156-
.be.false
167+
expect(doesQueryContain(document, types, 'Device', data, d => d.id === 2))
168+
.to.be.true
169+
expect(
170+
doesQueryContain(
171+
document,
172+
types,
173+
'Device',
174+
data,
175+
d => d.id === 1 || d.id === 2
176+
)
177+
).to.be.true
178+
expect(doesQueryContain(document, types, 'Device', data, d => d.id === 3))
179+
.to.be.true
180+
expect(doesQueryContain(document, types, 'Device', data, d => d.id === 4))
181+
.to.be.true
182+
expect(doesQueryContain(document, types, 'Device', data, d => d.id === 5))
183+
.to.be.false
157184
})
158185
it(`recursive type test`, function() {
159186
const document = gql`
@@ -187,8 +214,7 @@ describe(`doesQueryContain`, function() {
187214
types,
188215
'MetadataItem',
189216
data,
190-
new Set(['foo']),
191-
'tag'
217+
i => i.tag === 'foo'
192218
)
193219
).to.be.true
194220
expect(
@@ -197,8 +223,7 @@ describe(`doesQueryContain`, function() {
197223
types,
198224
'MetadataItem',
199225
data,
200-
new Set(['foo', 'foo/bar']),
201-
'tag'
226+
i => i.tag === 'foo/bar'
202227
)
203228
).to.be.true
204229
expect(
@@ -207,18 +232,7 @@ describe(`doesQueryContain`, function() {
207232
types,
208233
'MetadataItem',
209234
data,
210-
new Set(['foo/bar']),
211-
'tag'
212-
)
213-
).to.be.true
214-
expect(
215-
doesQueryContain(
216-
document,
217-
types,
218-
'MetadataItem',
219-
data,
220-
new Set(['foo/bar/baz']),
221-
'tag'
235+
i => i.tag === 'foo/bar/baz'
222236
)
223237
).to.be.false
224238
})
@@ -293,16 +307,19 @@ describe(`doesQueryContain`, function() {
293307
}
294308

295309
expect(
296-
doesQueryContain(document, types, 'Organization', data, new Set([7]))
310+
doesQueryContain(document, types, 'Organization', data, o => o.id === 7)
311+
).to.be.true
312+
expect(
313+
doesQueryContain(document, types, 'Organization', data, o => o.id === 5)
297314
).to.be.true
298315
expect(
299-
doesQueryContain(document, types, 'Organization', data, new Set([5, 8]))
316+
doesQueryContain(document, types, 'Organization', data, o => o.id === 8)
300317
).to.be.true
301318
expect(
302-
doesQueryContain(document, types, 'Organization', data, new Set([2]))
319+
doesQueryContain(document, types, 'Organization', data, o => o.id === 2)
303320
).to.be.true
304321
expect(
305-
doesQueryContain(document, types, 'Organization', data, new Set([3]))
322+
doesQueryContain(document, types, 'Organization', data, o => o.id === 3)
306323
).to.be.false
307324
})
308325
})

test/integration.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ describe(`integration test`, function() {
167167
org.userIds = org.userIds.filter(id => id !== 2)
168168
}
169169

170-
await refetch(client, 'User', [2])
170+
await refetch(client, 'User', u => u.id === 2)
171171

172172
const {
173173
data: { orgs: finalOrgs },
@@ -215,7 +215,7 @@ describe(`integration test`, function() {
215215
;(Users.get(2): any).organizationIds = [1]
216216
;(Organizations.get(2): any).userIds = [3]
217217

218-
await refetch(client, [['User', 2], ['Organization', 2]])
218+
await refetch(client, [['User', 2], ['Organization', o => o.id === 2]])
219219

220220
const {
221221
data: { orgs: finalOrgs },

0 commit comments

Comments
 (0)