-
-
Notifications
You must be signed in to change notification settings - Fork 149
Expand file tree
/
Copy pathinvalidation.ts
More file actions
96 lines (87 loc) · 3.4 KB
/
Copy pathinvalidation.ts
File metadata and controls
96 lines (87 loc) · 3.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import type { SchemaDef } from '@zenstackhq/schema';
import { log, type Logger } from './logging';
import { getMutatedModels, getReadModels } from './query-analysis';
import type { MaybePromise, ORMWriteActionType } from './types';
/**
* Type for a predicate that determines whether a query should be invalidated.
*/
export type InvalidationPredicate = ({ model, args }: { model: string; args: unknown }) => boolean;
/**
* Type for a function that invalidates queries matching the given predicate.
*/
export type InvalidateFunc = (predicate: InvalidationPredicate) => MaybePromise<void>;
/**
* Create a function that invalidates queries affected by the given mutation operation.
*
* @param model Model under mutation.
* @param operation Mutation operation (e.g, `update`).
* @param schema The schema.
* @param invalidator Function to invalidate queries matching a predicate. It should internally
* enumerate all query cache entries and invalidate those for which the predicate returns true.
* @param logging Logging option.
*/
export function createInvalidator(
model: string,
operation: string,
schema: SchemaDef,
invalidator: InvalidateFunc,
logging: Logger | undefined,
) {
const normalizedModel = normalizeModelName(model, schema);
return async (...args: unknown[]) => {
const [_, variables] = args;
const predicate = await getInvalidationPredicate(
normalizedModel,
operation as ORMWriteActionType,
variables,
schema,
logging,
);
await invalidator(predicate);
};
}
// gets a predicate for evaluating whether a query should be invalidated
async function getInvalidationPredicate(
model: string,
operation: ORMWriteActionType,
mutationArgs: any,
schema: SchemaDef,
logging: Logger | undefined,
): Promise<InvalidationPredicate> {
const mutatedModels = await getMutatedModels(model, operation, mutationArgs, schema);
return ({ model, args }) => {
if (mutatedModels.includes(model)) {
// direct match
if (logging) {
log(
logging,
`Marking "${model}" query for invalidation due to mutation "${operation}", query args: ${JSON.stringify(args)}`,
);
}
return true;
}
if (args) {
// traverse query args to find nested reads that match the model under mutation
if (findNestedRead(model, mutatedModels, schema, args)) {
if (logging) {
log(
logging,
`Marking "${model}" query for invalidation due to mutation "${operation}", query args: ${JSON.stringify(args)}`,
);
}
return true;
}
}
return false;
};
}
// find nested reads that match the given models
function findNestedRead(visitingModel: string, targetModels: string[], schema: SchemaDef, args: any) {
const modelsRead = getReadModels(visitingModel, schema, args);
return targetModels.some((m) => modelsRead.includes(m));
}
// resolves a model name to its canonical form as defined in the schema (case-insensitive match)
function normalizeModelName(model: string, schema: SchemaDef) {
const target = model.toLowerCase();
return Object.keys(schema.models).find((k) => k.toLowerCase() === target) ?? model;
}