forked from Mike-Gibson/mock-apollo-client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmockLink.ts
130 lines (102 loc) · 4.16 KB
/
mockLink.ts
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { ApolloLink, DocumentNode, Observable, Operation, FetchResult } from '@apollo/client/core';
import { print, visit } from 'graphql';
import { RequestHandler, RequestHandlerResponse } from './mockClient';
import { removeClientSetsFromDocument, removeConnectionDirectiveFromDocument } from '@apollo/client/utilities';
import { IMockSubscription, MockSubscription } from './mockSubscription';
export type MissingHandlerPolicy = 'throw-error' | 'warn-and-return-error' | 'return-error';
interface MockLinkOptions {
missingHandlerPolicy?: MissingHandlerPolicy;
}
const DEFAULT_MISSING_HANDLER_POLICY: MissingHandlerPolicy = 'throw-error';
export class MockLink extends ApolloLink {
constructor(options?: MockLinkOptions) {
super();
this.missingHandlerPolicy = options?.missingHandlerPolicy || DEFAULT_MISSING_HANDLER_POLICY;
}
private readonly missingHandlerPolicy: MissingHandlerPolicy;
private requestHandlers: Record<string, RequestHandler | undefined> = {};
setRequestHandler(requestQuery: DocumentNode, handler: RequestHandler): void {
const queryWithoutClientDirectives = removeClientSetsFromDocument(requestQuery);
if (queryWithoutClientDirectives === null) {
console.warn('Warning: mock-apollo-client - The query is entirely client side (using @client directives) so the request handler will not be registered.');
return;
}
const key = requestToKey(queryWithoutClientDirectives);
if (this.requestHandlers[key]) {
throw new Error(`Request handler already defined for query: ${print(requestQuery)}`);
}
this.requestHandlers[key] = handler;
}
request = (operation: Operation) => {
const key = requestToKey(operation.query);
const handler = this.requestHandlers[key];
if (!handler && this.missingHandlerPolicy === 'throw-error') {
throw new Error(getNotDefinedHandlerMessage(operation));
}
return new Observable<FetchResult>(observer => {
if (!handler) {
if (this.missingHandlerPolicy === 'warn-and-return-error') {
console.warn(getNotDefinedHandlerMessage(operation));
}
throw new Error(getNotDefinedHandlerMessage(operation));
}
let result:
| Promise<RequestHandlerResponse<any>>
| IMockSubscription<any>
| undefined = undefined;
try {
result = handler(operation.variables);
} catch (error) {
const message = error instanceof Error ? error.message : error;
throw new Error(`Unexpected error whilst calling request handler: ${message}`);
}
if (isPromise(result)) {
result
.then((result) => {
observer.next(result);
observer.complete();
})
.catch((error) => {
observer.error(error);
});
} else if (isSubscription(result)) {
result.subscribe(observer)
} else {
throw new Error(`Request handler must return a promise or subscription. Received '${typeof result}'.`);
}
return () => { };
});
};
}
const normalise = (requestQuery: DocumentNode): DocumentNode => {
let stripped = removeConnectionDirectiveFromDocument(requestQuery);
stripped = stripped !== null
? stripTypenames(stripped)
: null;
return stripped === null
? requestQuery
: stripped;
};
const stripTypenames = (document: DocumentNode): DocumentNode | null =>
visit(
document,
{
Field: {
enter: (node) => node.name.value === '__typename'
? null
: undefined,
},
});
const requestToKey = (query: DocumentNode): string => {
const normalised = normalise(query);
const queryString = query && print(normalised);
const requestKey = { query: queryString };
return JSON.stringify(requestKey);
}
const isPromise = (maybePromise: any): maybePromise is Promise<any> =>
maybePromise && typeof (maybePromise as any).then === 'function';
const isSubscription = (maybeSubscription: any): maybeSubscription is MockSubscription<any> =>
maybeSubscription && maybeSubscription instanceof MockSubscription;
const getNotDefinedHandlerMessage = (operation: Operation) => {
return `Request handler not defined for query: ${print(operation.query)}`
}