graphql-http 1.7.2 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/lib/audits/server.d.mts +4 -1
- package/lib/audits/server.d.ts +4 -1
- package/lib/audits/server.js +90 -49
- package/lib/audits/server.mjs +90 -49
- package/package.json +1 -1
- package/umd/graphql-http.min.js.gz +0 -0
package/README.md
CHANGED
|
@@ -735,7 +735,6 @@ If you want a feature-full server with bleeding edge technologies, you're recomm
|
|
|
735
735
|
| [hotchocolate](https://chillicream.com/docs/hotchocolate) | [✅ Compliant](/implementations/hotchocolate/README.md) |
|
|
736
736
|
| [postgraphile](https://www.graphile.org/postgraphile/) | [✅ Compliant](/implementations/postgraphile/README.md) |
|
|
737
737
|
| [apollo-server](https://www.apollographql.com/docs/apollo-server/) | [✅ Compliant](/implementations/apollo-server/README.md) |
|
|
738
|
-
| [mercurius](https://mercurius.dev) | [✅ Compliant](/implementations/mercurius/README.md) |
|
|
739
738
|
|
|
740
739
|
## [Documentation](docs/)
|
|
741
740
|
|
package/lib/audits/server.d.mts
CHANGED
|
@@ -12,8 +12,11 @@ import { Audit, AuditResult } from './common';
|
|
|
12
12
|
export interface ServerAuditOptions {
|
|
13
13
|
/**
|
|
14
14
|
* The URL of the GraphQL server for the audit.
|
|
15
|
+
*
|
|
16
|
+
* A function can be also supplied, in this case -
|
|
17
|
+
* every audit will invoke the function to get the URL.
|
|
15
18
|
*/
|
|
16
|
-
url: string;
|
|
19
|
+
url: string | Promise<string> | (() => string | Promise<string>);
|
|
17
20
|
/**
|
|
18
21
|
* The Fetch function to use.
|
|
19
22
|
*
|
package/lib/audits/server.d.ts
CHANGED
|
@@ -12,8 +12,11 @@ import { Audit, AuditResult } from './common';
|
|
|
12
12
|
export interface ServerAuditOptions {
|
|
13
13
|
/**
|
|
14
14
|
* The URL of the GraphQL server for the audit.
|
|
15
|
+
*
|
|
16
|
+
* A function can be also supplied, in this case -
|
|
17
|
+
* every audit will invoke the function to get the URL.
|
|
15
18
|
*/
|
|
16
|
-
url: string;
|
|
19
|
+
url: string | Promise<string> | (() => string | Promise<string>);
|
|
17
20
|
/**
|
|
18
21
|
* The Fetch function to use.
|
|
19
22
|
*
|
package/lib/audits/server.js
CHANGED
|
@@ -19,7 +19,7 @@ function serverAudits(opts) {
|
|
|
19
19
|
(0, utils_1.audit)(
|
|
20
20
|
// TODO: convert to MUST after watershed
|
|
21
21
|
'SHOULD accept application/graphql-response+json and match the content-type', async () => {
|
|
22
|
-
const res = await fetchFn(opts.url, {
|
|
22
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
23
23
|
method: 'POST',
|
|
24
24
|
headers: {
|
|
25
25
|
'content-type': 'application/json',
|
|
@@ -31,7 +31,7 @@ function serverAudits(opts) {
|
|
|
31
31
|
(0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('application/graphql-response+json');
|
|
32
32
|
}),
|
|
33
33
|
(0, utils_1.audit)('MUST accept application/json and match the content-type', async () => {
|
|
34
|
-
const res = await fetchFn(opts.url, {
|
|
34
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
35
35
|
method: 'POST',
|
|
36
36
|
headers: {
|
|
37
37
|
'content-type': 'application/json',
|
|
@@ -43,7 +43,7 @@ function serverAudits(opts) {
|
|
|
43
43
|
(0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('application/json');
|
|
44
44
|
}),
|
|
45
45
|
(0, utils_1.audit)('SHOULD accept */* and use application/json for the content-type', async () => {
|
|
46
|
-
const res = await fetchFn(opts.url, {
|
|
46
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
47
47
|
method: 'POST',
|
|
48
48
|
headers: {
|
|
49
49
|
'content-type': 'application/json',
|
|
@@ -55,14 +55,14 @@ function serverAudits(opts) {
|
|
|
55
55
|
(0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('application/json');
|
|
56
56
|
}),
|
|
57
57
|
(0, utils_1.audit)('SHOULD assume application/json content-type when accept is missing', async () => {
|
|
58
|
-
const url = new URL(opts.url);
|
|
58
|
+
const url = new URL(await getUrl(opts.url));
|
|
59
59
|
url.searchParams.set('query', '{ __typename }');
|
|
60
60
|
const res = await fetchFn(url.toString());
|
|
61
61
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
62
62
|
(0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('application/json');
|
|
63
63
|
}),
|
|
64
64
|
(0, utils_1.audit)('MUST use utf-8 encoding when responding', async () => {
|
|
65
|
-
const res = await fetchFn(opts.url, {
|
|
65
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
66
66
|
method: 'POST',
|
|
67
67
|
headers: {
|
|
68
68
|
'content-type': 'application/json',
|
|
@@ -91,7 +91,7 @@ function serverAudits(opts) {
|
|
|
91
91
|
}
|
|
92
92
|
}),
|
|
93
93
|
(0, utils_1.audit)('MUST accept utf-8 encoding', async () => {
|
|
94
|
-
const res = await fetchFn(opts.url, {
|
|
94
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
95
95
|
method: 'POST',
|
|
96
96
|
headers: {
|
|
97
97
|
'content-type': 'application/json; charset=utf-8',
|
|
@@ -102,7 +102,7 @@ function serverAudits(opts) {
|
|
|
102
102
|
(0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('utf-8');
|
|
103
103
|
}),
|
|
104
104
|
(0, utils_1.audit)('MUST assume utf-8 if encoding is unspecified', async () => {
|
|
105
|
-
const res = await fetchFn(opts.url, {
|
|
105
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
106
106
|
method: 'POST',
|
|
107
107
|
headers: {
|
|
108
108
|
'content-type': 'application/json',
|
|
@@ -114,7 +114,7 @@ function serverAudits(opts) {
|
|
|
114
114
|
}),
|
|
115
115
|
// Request
|
|
116
116
|
(0, utils_1.audit)('MUST accept POST requests', async () => {
|
|
117
|
-
const res = await fetchFn(opts.url, {
|
|
117
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
118
118
|
method: 'POST',
|
|
119
119
|
headers: { 'content-type': 'application/json' },
|
|
120
120
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
@@ -122,7 +122,7 @@ function serverAudits(opts) {
|
|
|
122
122
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
123
123
|
}),
|
|
124
124
|
(0, utils_1.audit)('MAY accept application/x-www-form-urlencoded formatted GET requests', async () => {
|
|
125
|
-
const url = new URL(opts.url);
|
|
125
|
+
const url = new URL(await getUrl(opts.url));
|
|
126
126
|
url.searchParams.set('query', '{ __typename }');
|
|
127
127
|
const res = await fetchFn(url.toString());
|
|
128
128
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
@@ -130,7 +130,7 @@ function serverAudits(opts) {
|
|
|
130
130
|
// Request GET
|
|
131
131
|
// TODO: this is a MUST if the server supports GET requests
|
|
132
132
|
(0, utils_1.audit)('MAY NOT allow executing mutations on GET requests', async () => {
|
|
133
|
-
const url = new URL(opts.url);
|
|
133
|
+
const url = new URL(await getUrl(opts.url));
|
|
134
134
|
url.searchParams.set('query', 'mutation { __typename }');
|
|
135
135
|
const res = await fetchFn(url.toString(), {
|
|
136
136
|
headers: {
|
|
@@ -141,13 +141,13 @@ function serverAudits(opts) {
|
|
|
141
141
|
}),
|
|
142
142
|
// Request POST
|
|
143
143
|
(0, utils_1.audit)('SHOULD respond with 4xx status code if content-type is not supplied on POST requests', async () => {
|
|
144
|
-
const res = await fetchFn(opts.url, {
|
|
144
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
145
145
|
method: 'POST',
|
|
146
146
|
});
|
|
147
147
|
(0, utils_1.assert)('Status code', res.status).toBeBetween(400, 499);
|
|
148
148
|
}),
|
|
149
149
|
(0, utils_1.audit)('MUST accept application/json POST requests', async () => {
|
|
150
|
-
const res = await fetchFn(opts.url, {
|
|
150
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
151
151
|
method: 'POST',
|
|
152
152
|
headers: { 'content-type': 'application/json' },
|
|
153
153
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
@@ -156,7 +156,7 @@ function serverAudits(opts) {
|
|
|
156
156
|
}),
|
|
157
157
|
(0, utils_1.audit)('MUST require a request body on POST', async () => {
|
|
158
158
|
var _a;
|
|
159
|
-
const res = await fetchFn(opts.url, {
|
|
159
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
160
160
|
method: 'POST',
|
|
161
161
|
headers: { 'content-type': 'application/json' },
|
|
162
162
|
});
|
|
@@ -171,7 +171,7 @@ function serverAudits(opts) {
|
|
|
171
171
|
(0, utils_1.audit)(
|
|
172
172
|
// TODO: convert to MUST after watershed
|
|
173
173
|
'SHOULD use 400 status code on missing {query} parameter when accepting application/graphql-response+json', async () => {
|
|
174
|
-
const res = await fetchFn(opts.url, {
|
|
174
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
175
175
|
method: 'POST',
|
|
176
176
|
headers: {
|
|
177
177
|
'content-type': 'application/json',
|
|
@@ -182,7 +182,7 @@ function serverAudits(opts) {
|
|
|
182
182
|
(0, utils_1.assert)('Status code', res.status).toBe(400);
|
|
183
183
|
}),
|
|
184
184
|
(0, utils_1.audit)('SHOULD use 200 status code with errors field on missing {query} parameter when accepting application/json', async () => {
|
|
185
|
-
const res = await fetchFn(opts.url, {
|
|
185
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
186
186
|
method: 'POST',
|
|
187
187
|
headers: {
|
|
188
188
|
'content-type': 'application/json',
|
|
@@ -196,7 +196,7 @@ function serverAudits(opts) {
|
|
|
196
196
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
|
|
197
197
|
// TODO: convert to MUST after watershed
|
|
198
198
|
`SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {query} parameter when accepting application/graphql-response+json`, async () => {
|
|
199
|
-
const res = await fetchFn(opts.url, {
|
|
199
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
200
200
|
method: 'POST',
|
|
201
201
|
headers: {
|
|
202
202
|
'content-type': 'application/json',
|
|
@@ -209,7 +209,7 @@ function serverAudits(opts) {
|
|
|
209
209
|
(0, utils_1.assert)('Status code', res.status).toBe(400);
|
|
210
210
|
})),
|
|
211
211
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {query} parameter when accepting application/json`, async () => {
|
|
212
|
-
const res = await fetchFn(opts.url, {
|
|
212
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
213
213
|
method: 'POST',
|
|
214
214
|
headers: {
|
|
215
215
|
'content-type': 'application/json',
|
|
@@ -225,7 +225,7 @@ function serverAudits(opts) {
|
|
|
225
225
|
(0, utils_1.audit)(
|
|
226
226
|
// TODO: convert to MUST after watershed
|
|
227
227
|
'SHOULD allow string {query} parameter when accepting application/graphql-response+json', async () => {
|
|
228
|
-
const res = await fetchFn(opts.url, {
|
|
228
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
229
229
|
method: 'POST',
|
|
230
230
|
headers: {
|
|
231
231
|
'content-type': 'application/json',
|
|
@@ -238,7 +238,7 @@ function serverAudits(opts) {
|
|
|
238
238
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
239
239
|
}),
|
|
240
240
|
(0, utils_1.audit)('MUST allow string {query} parameter when accepting application/json', async () => {
|
|
241
|
-
const res = await fetchFn(opts.url, {
|
|
241
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
242
242
|
method: 'POST',
|
|
243
243
|
headers: {
|
|
244
244
|
'content-type': 'application/json',
|
|
@@ -254,7 +254,7 @@ function serverAudits(opts) {
|
|
|
254
254
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
|
|
255
255
|
// TODO: convert to MUST after watershed
|
|
256
256
|
`SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {operationName} parameter when accepting application/graphql-response+json`, async () => {
|
|
257
|
-
const res = await fetchFn(opts.url, {
|
|
257
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
258
258
|
method: 'POST',
|
|
259
259
|
headers: {
|
|
260
260
|
'content-type': 'application/json',
|
|
@@ -268,7 +268,7 @@ function serverAudits(opts) {
|
|
|
268
268
|
(0, utils_1.assert)('Status code', res.status).toBe(400);
|
|
269
269
|
})),
|
|
270
270
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {operationName} parameter when accepting application/json`, async () => {
|
|
271
|
-
const res = await fetchFn(opts.url, {
|
|
271
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
272
272
|
method: 'POST',
|
|
273
273
|
headers: {
|
|
274
274
|
'content-type': 'application/json',
|
|
@@ -285,7 +285,7 @@ function serverAudits(opts) {
|
|
|
285
285
|
(0, utils_1.audit)(
|
|
286
286
|
// TODO: convert to MUST after watershed
|
|
287
287
|
'SHOULD allow string {operationName} parameter when accepting application/graphql-response+json', async () => {
|
|
288
|
-
const res = await fetchFn(opts.url, {
|
|
288
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
289
289
|
method: 'POST',
|
|
290
290
|
headers: {
|
|
291
291
|
'content-type': 'application/json',
|
|
@@ -299,7 +299,7 @@ function serverAudits(opts) {
|
|
|
299
299
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
300
300
|
}),
|
|
301
301
|
(0, utils_1.audit)('MUST allow string {operationName} parameter when accepting application/json', async () => {
|
|
302
|
-
const res = await fetchFn(opts.url, {
|
|
302
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
303
303
|
method: 'POST',
|
|
304
304
|
headers: {
|
|
305
305
|
'content-type': 'application/json',
|
|
@@ -313,10 +313,44 @@ function serverAudits(opts) {
|
|
|
313
313
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
314
314
|
(0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
|
|
315
315
|
}),
|
|
316
|
+
...['variables', 'operationName', 'extensions'].flatMap((parameter) => [
|
|
317
|
+
(0, utils_1.audit)(
|
|
318
|
+
// TODO: convert to MUST after watershed
|
|
319
|
+
`SHOULD allow null {${parameter}} parameter when accepting application/graphql-response+json`, async () => {
|
|
320
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
321
|
+
method: 'POST',
|
|
322
|
+
headers: {
|
|
323
|
+
'content-type': 'application/json',
|
|
324
|
+
accept: 'application/graphql-response+json',
|
|
325
|
+
},
|
|
326
|
+
body: JSON.stringify({
|
|
327
|
+
query: '{ __typename }',
|
|
328
|
+
[parameter]: null,
|
|
329
|
+
}),
|
|
330
|
+
});
|
|
331
|
+
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
332
|
+
(0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
|
|
333
|
+
}),
|
|
334
|
+
(0, utils_1.audit)(`MUST allow null {${parameter}} parameter when accepting application/json`, async () => {
|
|
335
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
336
|
+
method: 'POST',
|
|
337
|
+
headers: {
|
|
338
|
+
'content-type': 'application/json',
|
|
339
|
+
accept: 'application/json',
|
|
340
|
+
},
|
|
341
|
+
body: JSON.stringify({
|
|
342
|
+
query: '{ __typename }',
|
|
343
|
+
[parameter]: null,
|
|
344
|
+
}),
|
|
345
|
+
});
|
|
346
|
+
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
347
|
+
(0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
|
|
348
|
+
}),
|
|
349
|
+
]),
|
|
316
350
|
...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
|
|
317
351
|
// TODO: convert to MUST after watershed
|
|
318
352
|
`SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {variables} parameter when accepting application/graphql-response+json`, async () => {
|
|
319
|
-
const res = await fetchFn(opts.url, {
|
|
353
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
320
354
|
method: 'POST',
|
|
321
355
|
headers: {
|
|
322
356
|
'content-type': 'application/json',
|
|
@@ -330,7 +364,7 @@ function serverAudits(opts) {
|
|
|
330
364
|
(0, utils_1.assert)('Status code', res.status).toBe(400);
|
|
331
365
|
})),
|
|
332
366
|
...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {variables} parameter when accepting application/json`, async () => {
|
|
333
|
-
const res = await fetchFn(opts.url, {
|
|
367
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
334
368
|
method: 'POST',
|
|
335
369
|
headers: {
|
|
336
370
|
'content-type': 'application/json',
|
|
@@ -347,7 +381,7 @@ function serverAudits(opts) {
|
|
|
347
381
|
(0, utils_1.audit)(
|
|
348
382
|
// TODO: convert to MUST after watershed
|
|
349
383
|
'SHOULD allow map {variables} parameter when accepting application/graphql-response+json', async () => {
|
|
350
|
-
const res = await fetchFn(opts.url, {
|
|
384
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
351
385
|
method: 'POST',
|
|
352
386
|
headers: {
|
|
353
387
|
'content-type': 'application/json',
|
|
@@ -361,7 +395,7 @@ function serverAudits(opts) {
|
|
|
361
395
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
362
396
|
}),
|
|
363
397
|
(0, utils_1.audit)('MUST allow map {variables} parameter when accepting application/json', async () => {
|
|
364
|
-
const res = await fetchFn(opts.url, {
|
|
398
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
365
399
|
method: 'POST',
|
|
366
400
|
headers: {
|
|
367
401
|
'content-type': 'application/json',
|
|
@@ -376,7 +410,7 @@ function serverAudits(opts) {
|
|
|
376
410
|
(0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
|
|
377
411
|
}),
|
|
378
412
|
(0, utils_1.audit)('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/graphql-response+json', async () => {
|
|
379
|
-
const url = new URL(opts.url);
|
|
413
|
+
const url = new URL(await getUrl(opts.url));
|
|
380
414
|
url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
|
|
381
415
|
url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
|
|
382
416
|
const res = await fetchFn(url.toString(), {
|
|
@@ -388,7 +422,7 @@ function serverAudits(opts) {
|
|
|
388
422
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
389
423
|
}),
|
|
390
424
|
(0, utils_1.audit)('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/json', async () => {
|
|
391
|
-
const url = new URL(opts.url);
|
|
425
|
+
const url = new URL(await getUrl(opts.url));
|
|
392
426
|
url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
|
|
393
427
|
url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
|
|
394
428
|
const res = await fetchFn(url.toString(), {
|
|
@@ -403,7 +437,7 @@ function serverAudits(opts) {
|
|
|
403
437
|
...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
|
|
404
438
|
// TODO: convert to MUST after watershed
|
|
405
439
|
`SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {extensions} parameter when accepting application/graphql-response+json`, async () => {
|
|
406
|
-
const res = await fetchFn(opts.url, {
|
|
440
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
407
441
|
method: 'POST',
|
|
408
442
|
headers: {
|
|
409
443
|
'content-type': 'application/json',
|
|
@@ -417,7 +451,7 @@ function serverAudits(opts) {
|
|
|
417
451
|
(0, utils_1.assert)('Status code', res.status).toBe(400);
|
|
418
452
|
})),
|
|
419
453
|
...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {extensions} parameter when accepting application/json`, async () => {
|
|
420
|
-
const res = await fetchFn(opts.url, {
|
|
454
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
421
455
|
method: 'POST',
|
|
422
456
|
headers: {
|
|
423
457
|
'content-type': 'application/json',
|
|
@@ -434,7 +468,7 @@ function serverAudits(opts) {
|
|
|
434
468
|
(0, utils_1.audit)(
|
|
435
469
|
// TODO: convert to MUST after watershed
|
|
436
470
|
'SHOULD allow map {extensions} parameter when accepting application/graphql-response+json', async () => {
|
|
437
|
-
const res = await fetchFn(opts.url, {
|
|
471
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
438
472
|
method: 'POST',
|
|
439
473
|
headers: {
|
|
440
474
|
'content-type': 'application/json',
|
|
@@ -448,7 +482,7 @@ function serverAudits(opts) {
|
|
|
448
482
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
449
483
|
}),
|
|
450
484
|
(0, utils_1.audit)('MUST allow map {extensions} parameter when accepting application/json', async () => {
|
|
451
|
-
const res = await fetchFn(opts.url, {
|
|
485
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
452
486
|
method: 'POST',
|
|
453
487
|
headers: {
|
|
454
488
|
'content-type': 'application/json',
|
|
@@ -465,7 +499,7 @@ function serverAudits(opts) {
|
|
|
465
499
|
// TODO: audit('MUST accept a map for the {extensions} parameter'),
|
|
466
500
|
// Response application/json
|
|
467
501
|
(0, utils_1.audit)('SHOULD use 200 status code on JSON parsing failure when accepting application/json', async () => {
|
|
468
|
-
const res = await fetchFn(opts.url, {
|
|
502
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
469
503
|
method: 'POST',
|
|
470
504
|
headers: {
|
|
471
505
|
'content-type': 'application/json',
|
|
@@ -476,7 +510,7 @@ function serverAudits(opts) {
|
|
|
476
510
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
477
511
|
}),
|
|
478
512
|
(0, utils_1.audit)('SHOULD use 200 status code if parameters are invalid when accepting application/json', async () => {
|
|
479
|
-
const res = await fetchFn(opts.url, {
|
|
513
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
480
514
|
method: 'POST',
|
|
481
515
|
headers: {
|
|
482
516
|
'content-type': 'application/json',
|
|
@@ -489,7 +523,7 @@ function serverAudits(opts) {
|
|
|
489
523
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
490
524
|
}),
|
|
491
525
|
(0, utils_1.audit)('SHOULD use 200 status code on document parsing failure when accepting application/json', async () => {
|
|
492
|
-
const res = await fetchFn(opts.url, {
|
|
526
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
493
527
|
method: 'POST',
|
|
494
528
|
headers: {
|
|
495
529
|
'content-type': 'application/json',
|
|
@@ -500,7 +534,7 @@ function serverAudits(opts) {
|
|
|
500
534
|
(0, utils_1.assert)('Status code', res.status).toBe(200);
|
|
501
535
|
}),
|
|
502
536
|
(0, utils_1.audit)('SHOULD use 200 status code on document validation failure when accepting application/json', async () => {
|
|
503
|
-
const res = await fetchFn(opts.url, {
|
|
537
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
504
538
|
method: 'POST',
|
|
505
539
|
headers: {
|
|
506
540
|
'content-type': 'application/json',
|
|
@@ -516,7 +550,7 @@ function serverAudits(opts) {
|
|
|
516
550
|
(0, utils_1.audit)(
|
|
517
551
|
// TODO: convert to MUST after watershed
|
|
518
552
|
'SHOULD use 4xx or 5xx status codes on JSON parsing failure when accepting application/graphql-response+json', async () => {
|
|
519
|
-
const res = await fetchFn(opts.url, {
|
|
553
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
520
554
|
method: 'POST',
|
|
521
555
|
headers: {
|
|
522
556
|
'content-type': 'application/json',
|
|
@@ -527,7 +561,7 @@ function serverAudits(opts) {
|
|
|
527
561
|
(0, utils_1.assert)('Status code', res.status).toBeBetween(400, 499);
|
|
528
562
|
}),
|
|
529
563
|
(0, utils_1.audit)('SHOULD use 400 status code on JSON parsing failure when accepting application/graphql-response+json', async () => {
|
|
530
|
-
const res = await fetchFn(opts.url, {
|
|
564
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
531
565
|
method: 'POST',
|
|
532
566
|
headers: {
|
|
533
567
|
'content-type': 'application/json',
|
|
@@ -538,7 +572,7 @@ function serverAudits(opts) {
|
|
|
538
572
|
(0, utils_1.assert)('Status code', res.status).toBe(400);
|
|
539
573
|
}),
|
|
540
574
|
(0, utils_1.audit)('SHOULD not contain the data entry on JSON parsing failure when accepting application/graphql-response+json', async () => {
|
|
541
|
-
const res = await fetchFn(opts.url, {
|
|
575
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
542
576
|
method: 'POST',
|
|
543
577
|
headers: {
|
|
544
578
|
'content-type': 'application/json',
|
|
@@ -551,7 +585,7 @@ function serverAudits(opts) {
|
|
|
551
585
|
(0, utils_1.audit)(
|
|
552
586
|
// TODO: convert to MUST after watershed
|
|
553
587
|
'SHOULD use 4xx or 5xx status codes if parameters are invalid when accepting application/graphql-response+json', async () => {
|
|
554
|
-
const res = await fetchFn(opts.url, {
|
|
588
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
555
589
|
method: 'POST',
|
|
556
590
|
headers: {
|
|
557
591
|
'content-type': 'application/json',
|
|
@@ -564,7 +598,7 @@ function serverAudits(opts) {
|
|
|
564
598
|
(0, utils_1.assert)('Status code', res.status).toBeBetween(400, 599);
|
|
565
599
|
}),
|
|
566
600
|
(0, utils_1.audit)('SHOULD use 400 status code if parameters are invalid when accepting application/graphql-response+json', async () => {
|
|
567
|
-
const res = await fetchFn(opts.url, {
|
|
601
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
568
602
|
method: 'POST',
|
|
569
603
|
headers: {
|
|
570
604
|
'content-type': 'application/json',
|
|
@@ -577,7 +611,7 @@ function serverAudits(opts) {
|
|
|
577
611
|
(0, utils_1.assert)('Status code', res.status).toBe(400);
|
|
578
612
|
}),
|
|
579
613
|
(0, utils_1.audit)('SHOULD not contain the data entry if parameters are invalid when accepting application/graphql-response+json', async () => {
|
|
580
|
-
const res = await fetchFn(opts.url, {
|
|
614
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
581
615
|
method: 'POST',
|
|
582
616
|
headers: {
|
|
583
617
|
'content-type': 'application/json',
|
|
@@ -592,7 +626,7 @@ function serverAudits(opts) {
|
|
|
592
626
|
(0, utils_1.audit)(
|
|
593
627
|
// TODO: convert to MUST after watershed
|
|
594
628
|
'SHOULD use 4xx or 5xx status codes on document parsing failure when accepting application/graphql-response+json', async () => {
|
|
595
|
-
const res = await fetchFn(opts.url, {
|
|
629
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
596
630
|
method: 'POST',
|
|
597
631
|
headers: {
|
|
598
632
|
'content-type': 'application/json',
|
|
@@ -605,7 +639,7 @@ function serverAudits(opts) {
|
|
|
605
639
|
(0, utils_1.assert)('Status code', res.status).toBeBetween(400, 599);
|
|
606
640
|
}),
|
|
607
641
|
(0, utils_1.audit)('SHOULD use 400 status code on document parsing failure when accepting application/graphql-response+json', async () => {
|
|
608
|
-
const res = await fetchFn(opts.url, {
|
|
642
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
609
643
|
method: 'POST',
|
|
610
644
|
headers: {
|
|
611
645
|
'content-type': 'application/json',
|
|
@@ -618,7 +652,7 @@ function serverAudits(opts) {
|
|
|
618
652
|
(0, utils_1.assert)('Status code', res.status).toBe(400);
|
|
619
653
|
}),
|
|
620
654
|
(0, utils_1.audit)('SHOULD not contain the data entry on document parsing failure when accepting application/graphql-response+json', async () => {
|
|
621
|
-
const res = await fetchFn(opts.url, {
|
|
655
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
622
656
|
method: 'POST',
|
|
623
657
|
headers: {
|
|
624
658
|
'content-type': 'application/json',
|
|
@@ -633,7 +667,7 @@ function serverAudits(opts) {
|
|
|
633
667
|
(0, utils_1.audit)(
|
|
634
668
|
// TODO: convert to MUST after watershed
|
|
635
669
|
'SHOULD use 4xx or 5xx status codes on document validation failure when accepting application/graphql-response+json', async () => {
|
|
636
|
-
const res = await fetchFn(opts.url, {
|
|
670
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
637
671
|
method: 'POST',
|
|
638
672
|
headers: {
|
|
639
673
|
'content-type': 'application/json',
|
|
@@ -646,7 +680,7 @@ function serverAudits(opts) {
|
|
|
646
680
|
(0, utils_1.assert)('Status code', res.status).toBeBetween(400, 599);
|
|
647
681
|
}),
|
|
648
682
|
(0, utils_1.audit)('SHOULD use 400 status code on document validation failure when accepting application/graphql-response+json', async () => {
|
|
649
|
-
const res = await fetchFn(opts.url, {
|
|
683
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
650
684
|
method: 'POST',
|
|
651
685
|
headers: {
|
|
652
686
|
'content-type': 'application/json',
|
|
@@ -659,7 +693,7 @@ function serverAudits(opts) {
|
|
|
659
693
|
(0, utils_1.assert)('Status code', res.status).toBe(400);
|
|
660
694
|
}),
|
|
661
695
|
(0, utils_1.audit)('SHOULD not contain the data entry on document validation failure when accepting application/graphql-response+json', async () => {
|
|
662
|
-
const res = await fetchFn(opts.url, {
|
|
696
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
663
697
|
method: 'POST',
|
|
664
698
|
headers: {
|
|
665
699
|
'content-type': 'application/json',
|
|
@@ -692,3 +726,10 @@ async function auditServer(opts) {
|
|
|
692
726
|
return await Promise.all(audits.map(({ fn }) => fn()));
|
|
693
727
|
}
|
|
694
728
|
exports.auditServer = auditServer;
|
|
729
|
+
/** @private */
|
|
730
|
+
async function getUrl(url) {
|
|
731
|
+
if (typeof url === 'function') {
|
|
732
|
+
return await url();
|
|
733
|
+
}
|
|
734
|
+
return url;
|
|
735
|
+
}
|
package/lib/audits/server.mjs
CHANGED
|
@@ -16,7 +16,7 @@ export function serverAudits(opts) {
|
|
|
16
16
|
audit(
|
|
17
17
|
// TODO: convert to MUST after watershed
|
|
18
18
|
'SHOULD accept application/graphql-response+json and match the content-type', async () => {
|
|
19
|
-
const res = await fetchFn(opts.url, {
|
|
19
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
20
20
|
method: 'POST',
|
|
21
21
|
headers: {
|
|
22
22
|
'content-type': 'application/json',
|
|
@@ -28,7 +28,7 @@ export function serverAudits(opts) {
|
|
|
28
28
|
assert('Content-Type header', res.headers.get('content-type')).toContain('application/graphql-response+json');
|
|
29
29
|
}),
|
|
30
30
|
audit('MUST accept application/json and match the content-type', async () => {
|
|
31
|
-
const res = await fetchFn(opts.url, {
|
|
31
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
32
32
|
method: 'POST',
|
|
33
33
|
headers: {
|
|
34
34
|
'content-type': 'application/json',
|
|
@@ -40,7 +40,7 @@ export function serverAudits(opts) {
|
|
|
40
40
|
assert('Content-Type header', res.headers.get('content-type')).toContain('application/json');
|
|
41
41
|
}),
|
|
42
42
|
audit('SHOULD accept */* and use application/json for the content-type', async () => {
|
|
43
|
-
const res = await fetchFn(opts.url, {
|
|
43
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
44
44
|
method: 'POST',
|
|
45
45
|
headers: {
|
|
46
46
|
'content-type': 'application/json',
|
|
@@ -52,14 +52,14 @@ export function serverAudits(opts) {
|
|
|
52
52
|
assert('Content-Type header', res.headers.get('content-type')).toContain('application/json');
|
|
53
53
|
}),
|
|
54
54
|
audit('SHOULD assume application/json content-type when accept is missing', async () => {
|
|
55
|
-
const url = new URL(opts.url);
|
|
55
|
+
const url = new URL(await getUrl(opts.url));
|
|
56
56
|
url.searchParams.set('query', '{ __typename }');
|
|
57
57
|
const res = await fetchFn(url.toString());
|
|
58
58
|
assert('Status code', res.status).toBe(200);
|
|
59
59
|
assert('Content-Type header', res.headers.get('content-type')).toContain('application/json');
|
|
60
60
|
}),
|
|
61
61
|
audit('MUST use utf-8 encoding when responding', async () => {
|
|
62
|
-
const res = await fetchFn(opts.url, {
|
|
62
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
63
63
|
method: 'POST',
|
|
64
64
|
headers: {
|
|
65
65
|
'content-type': 'application/json',
|
|
@@ -88,7 +88,7 @@ export function serverAudits(opts) {
|
|
|
88
88
|
}
|
|
89
89
|
}),
|
|
90
90
|
audit('MUST accept utf-8 encoding', async () => {
|
|
91
|
-
const res = await fetchFn(opts.url, {
|
|
91
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
92
92
|
method: 'POST',
|
|
93
93
|
headers: {
|
|
94
94
|
'content-type': 'application/json; charset=utf-8',
|
|
@@ -99,7 +99,7 @@ export function serverAudits(opts) {
|
|
|
99
99
|
assert('Content-Type header', res.headers.get('content-type')).toContain('utf-8');
|
|
100
100
|
}),
|
|
101
101
|
audit('MUST assume utf-8 if encoding is unspecified', async () => {
|
|
102
|
-
const res = await fetchFn(opts.url, {
|
|
102
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
103
103
|
method: 'POST',
|
|
104
104
|
headers: {
|
|
105
105
|
'content-type': 'application/json',
|
|
@@ -111,7 +111,7 @@ export function serverAudits(opts) {
|
|
|
111
111
|
}),
|
|
112
112
|
// Request
|
|
113
113
|
audit('MUST accept POST requests', async () => {
|
|
114
|
-
const res = await fetchFn(opts.url, {
|
|
114
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
115
115
|
method: 'POST',
|
|
116
116
|
headers: { 'content-type': 'application/json' },
|
|
117
117
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
@@ -119,7 +119,7 @@ export function serverAudits(opts) {
|
|
|
119
119
|
assert('Status code', res.status).toBe(200);
|
|
120
120
|
}),
|
|
121
121
|
audit('MAY accept application/x-www-form-urlencoded formatted GET requests', async () => {
|
|
122
|
-
const url = new URL(opts.url);
|
|
122
|
+
const url = new URL(await getUrl(opts.url));
|
|
123
123
|
url.searchParams.set('query', '{ __typename }');
|
|
124
124
|
const res = await fetchFn(url.toString());
|
|
125
125
|
assert('Status code', res.status).toBe(200);
|
|
@@ -127,7 +127,7 @@ export function serverAudits(opts) {
|
|
|
127
127
|
// Request GET
|
|
128
128
|
// TODO: this is a MUST if the server supports GET requests
|
|
129
129
|
audit('MAY NOT allow executing mutations on GET requests', async () => {
|
|
130
|
-
const url = new URL(opts.url);
|
|
130
|
+
const url = new URL(await getUrl(opts.url));
|
|
131
131
|
url.searchParams.set('query', 'mutation { __typename }');
|
|
132
132
|
const res = await fetchFn(url.toString(), {
|
|
133
133
|
headers: {
|
|
@@ -138,13 +138,13 @@ export function serverAudits(opts) {
|
|
|
138
138
|
}),
|
|
139
139
|
// Request POST
|
|
140
140
|
audit('SHOULD respond with 4xx status code if content-type is not supplied on POST requests', async () => {
|
|
141
|
-
const res = await fetchFn(opts.url, {
|
|
141
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
142
142
|
method: 'POST',
|
|
143
143
|
});
|
|
144
144
|
assert('Status code', res.status).toBeBetween(400, 499);
|
|
145
145
|
}),
|
|
146
146
|
audit('MUST accept application/json POST requests', async () => {
|
|
147
|
-
const res = await fetchFn(opts.url, {
|
|
147
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
148
148
|
method: 'POST',
|
|
149
149
|
headers: { 'content-type': 'application/json' },
|
|
150
150
|
body: JSON.stringify({ query: '{ __typename }' }),
|
|
@@ -153,7 +153,7 @@ export function serverAudits(opts) {
|
|
|
153
153
|
}),
|
|
154
154
|
audit('MUST require a request body on POST', async () => {
|
|
155
155
|
var _a;
|
|
156
|
-
const res = await fetchFn(opts.url, {
|
|
156
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
157
157
|
method: 'POST',
|
|
158
158
|
headers: { 'content-type': 'application/json' },
|
|
159
159
|
});
|
|
@@ -168,7 +168,7 @@ export function serverAudits(opts) {
|
|
|
168
168
|
audit(
|
|
169
169
|
// TODO: convert to MUST after watershed
|
|
170
170
|
'SHOULD use 400 status code on missing {query} parameter when accepting application/graphql-response+json', async () => {
|
|
171
|
-
const res = await fetchFn(opts.url, {
|
|
171
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
172
172
|
method: 'POST',
|
|
173
173
|
headers: {
|
|
174
174
|
'content-type': 'application/json',
|
|
@@ -179,7 +179,7 @@ export function serverAudits(opts) {
|
|
|
179
179
|
assert('Status code', res.status).toBe(400);
|
|
180
180
|
}),
|
|
181
181
|
audit('SHOULD use 200 status code with errors field on missing {query} parameter when accepting application/json', async () => {
|
|
182
|
-
const res = await fetchFn(opts.url, {
|
|
182
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
183
183
|
method: 'POST',
|
|
184
184
|
headers: {
|
|
185
185
|
'content-type': 'application/json',
|
|
@@ -193,7 +193,7 @@ export function serverAudits(opts) {
|
|
|
193
193
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(
|
|
194
194
|
// TODO: convert to MUST after watershed
|
|
195
195
|
`SHOULD use 400 status code on ${extendedTypeof(invalid)} {query} parameter when accepting application/graphql-response+json`, async () => {
|
|
196
|
-
const res = await fetchFn(opts.url, {
|
|
196
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
197
197
|
method: 'POST',
|
|
198
198
|
headers: {
|
|
199
199
|
'content-type': 'application/json',
|
|
@@ -206,7 +206,7 @@ export function serverAudits(opts) {
|
|
|
206
206
|
assert('Status code', res.status).toBe(400);
|
|
207
207
|
})),
|
|
208
208
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {query} parameter when accepting application/json`, async () => {
|
|
209
|
-
const res = await fetchFn(opts.url, {
|
|
209
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
210
210
|
method: 'POST',
|
|
211
211
|
headers: {
|
|
212
212
|
'content-type': 'application/json',
|
|
@@ -222,7 +222,7 @@ export function serverAudits(opts) {
|
|
|
222
222
|
audit(
|
|
223
223
|
// TODO: convert to MUST after watershed
|
|
224
224
|
'SHOULD allow string {query} parameter when accepting application/graphql-response+json', async () => {
|
|
225
|
-
const res = await fetchFn(opts.url, {
|
|
225
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
226
226
|
method: 'POST',
|
|
227
227
|
headers: {
|
|
228
228
|
'content-type': 'application/json',
|
|
@@ -235,7 +235,7 @@ export function serverAudits(opts) {
|
|
|
235
235
|
assert('Status code', res.status).toBe(200);
|
|
236
236
|
}),
|
|
237
237
|
audit('MUST allow string {query} parameter when accepting application/json', async () => {
|
|
238
|
-
const res = await fetchFn(opts.url, {
|
|
238
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
239
239
|
method: 'POST',
|
|
240
240
|
headers: {
|
|
241
241
|
'content-type': 'application/json',
|
|
@@ -251,7 +251,7 @@ export function serverAudits(opts) {
|
|
|
251
251
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(
|
|
252
252
|
// TODO: convert to MUST after watershed
|
|
253
253
|
`SHOULD use 400 status code on ${extendedTypeof(invalid)} {operationName} parameter when accepting application/graphql-response+json`, async () => {
|
|
254
|
-
const res = await fetchFn(opts.url, {
|
|
254
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
255
255
|
method: 'POST',
|
|
256
256
|
headers: {
|
|
257
257
|
'content-type': 'application/json',
|
|
@@ -265,7 +265,7 @@ export function serverAudits(opts) {
|
|
|
265
265
|
assert('Status code', res.status).toBe(400);
|
|
266
266
|
})),
|
|
267
267
|
...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {operationName} parameter when accepting application/json`, async () => {
|
|
268
|
-
const res = await fetchFn(opts.url, {
|
|
268
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
269
269
|
method: 'POST',
|
|
270
270
|
headers: {
|
|
271
271
|
'content-type': 'application/json',
|
|
@@ -282,7 +282,7 @@ export function serverAudits(opts) {
|
|
|
282
282
|
audit(
|
|
283
283
|
// TODO: convert to MUST after watershed
|
|
284
284
|
'SHOULD allow string {operationName} parameter when accepting application/graphql-response+json', async () => {
|
|
285
|
-
const res = await fetchFn(opts.url, {
|
|
285
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
286
286
|
method: 'POST',
|
|
287
287
|
headers: {
|
|
288
288
|
'content-type': 'application/json',
|
|
@@ -296,7 +296,7 @@ export function serverAudits(opts) {
|
|
|
296
296
|
assert('Status code', res.status).toBe(200);
|
|
297
297
|
}),
|
|
298
298
|
audit('MUST allow string {operationName} parameter when accepting application/json', async () => {
|
|
299
|
-
const res = await fetchFn(opts.url, {
|
|
299
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
300
300
|
method: 'POST',
|
|
301
301
|
headers: {
|
|
302
302
|
'content-type': 'application/json',
|
|
@@ -310,10 +310,44 @@ export function serverAudits(opts) {
|
|
|
310
310
|
assert('Status code', res.status).toBe(200);
|
|
311
311
|
assert('Execution result', await assertBodyAsExecutionResult(res)).notToHaveProperty('errors');
|
|
312
312
|
}),
|
|
313
|
+
...['variables', 'operationName', 'extensions'].flatMap((parameter) => [
|
|
314
|
+
audit(
|
|
315
|
+
// TODO: convert to MUST after watershed
|
|
316
|
+
`SHOULD allow null {${parameter}} parameter when accepting application/graphql-response+json`, async () => {
|
|
317
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
318
|
+
method: 'POST',
|
|
319
|
+
headers: {
|
|
320
|
+
'content-type': 'application/json',
|
|
321
|
+
accept: 'application/graphql-response+json',
|
|
322
|
+
},
|
|
323
|
+
body: JSON.stringify({
|
|
324
|
+
query: '{ __typename }',
|
|
325
|
+
[parameter]: null,
|
|
326
|
+
}),
|
|
327
|
+
});
|
|
328
|
+
assert('Status code', res.status).toBe(200);
|
|
329
|
+
assert('Execution result', await assertBodyAsExecutionResult(res)).notToHaveProperty('errors');
|
|
330
|
+
}),
|
|
331
|
+
audit(`MUST allow null {${parameter}} parameter when accepting application/json`, async () => {
|
|
332
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
333
|
+
method: 'POST',
|
|
334
|
+
headers: {
|
|
335
|
+
'content-type': 'application/json',
|
|
336
|
+
accept: 'application/json',
|
|
337
|
+
},
|
|
338
|
+
body: JSON.stringify({
|
|
339
|
+
query: '{ __typename }',
|
|
340
|
+
[parameter]: null,
|
|
341
|
+
}),
|
|
342
|
+
});
|
|
343
|
+
assert('Status code', res.status).toBe(200);
|
|
344
|
+
assert('Execution result', await assertBodyAsExecutionResult(res)).notToHaveProperty('errors');
|
|
345
|
+
}),
|
|
346
|
+
]),
|
|
313
347
|
...['string', 0, false, ['array']].map((invalid) => audit(
|
|
314
348
|
// TODO: convert to MUST after watershed
|
|
315
349
|
`SHOULD use 400 status code on ${extendedTypeof(invalid)} {variables} parameter when accepting application/graphql-response+json`, async () => {
|
|
316
|
-
const res = await fetchFn(opts.url, {
|
|
350
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
317
351
|
method: 'POST',
|
|
318
352
|
headers: {
|
|
319
353
|
'content-type': 'application/json',
|
|
@@ -327,7 +361,7 @@ export function serverAudits(opts) {
|
|
|
327
361
|
assert('Status code', res.status).toBe(400);
|
|
328
362
|
})),
|
|
329
363
|
...['string', 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {variables} parameter when accepting application/json`, async () => {
|
|
330
|
-
const res = await fetchFn(opts.url, {
|
|
364
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
331
365
|
method: 'POST',
|
|
332
366
|
headers: {
|
|
333
367
|
'content-type': 'application/json',
|
|
@@ -344,7 +378,7 @@ export function serverAudits(opts) {
|
|
|
344
378
|
audit(
|
|
345
379
|
// TODO: convert to MUST after watershed
|
|
346
380
|
'SHOULD allow map {variables} parameter when accepting application/graphql-response+json', async () => {
|
|
347
|
-
const res = await fetchFn(opts.url, {
|
|
381
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
348
382
|
method: 'POST',
|
|
349
383
|
headers: {
|
|
350
384
|
'content-type': 'application/json',
|
|
@@ -358,7 +392,7 @@ export function serverAudits(opts) {
|
|
|
358
392
|
assert('Status code', res.status).toBe(200);
|
|
359
393
|
}),
|
|
360
394
|
audit('MUST allow map {variables} parameter when accepting application/json', async () => {
|
|
361
|
-
const res = await fetchFn(opts.url, {
|
|
395
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
362
396
|
method: 'POST',
|
|
363
397
|
headers: {
|
|
364
398
|
'content-type': 'application/json',
|
|
@@ -373,7 +407,7 @@ export function serverAudits(opts) {
|
|
|
373
407
|
assert('Execution result', await assertBodyAsExecutionResult(res)).notToHaveProperty('errors');
|
|
374
408
|
}),
|
|
375
409
|
audit('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/graphql-response+json', async () => {
|
|
376
|
-
const url = new URL(opts.url);
|
|
410
|
+
const url = new URL(await getUrl(opts.url));
|
|
377
411
|
url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
|
|
378
412
|
url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
|
|
379
413
|
const res = await fetchFn(url.toString(), {
|
|
@@ -385,7 +419,7 @@ export function serverAudits(opts) {
|
|
|
385
419
|
assert('Status code', res.status).toBe(200);
|
|
386
420
|
}),
|
|
387
421
|
audit('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/json', async () => {
|
|
388
|
-
const url = new URL(opts.url);
|
|
422
|
+
const url = new URL(await getUrl(opts.url));
|
|
389
423
|
url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
|
|
390
424
|
url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
|
|
391
425
|
const res = await fetchFn(url.toString(), {
|
|
@@ -400,7 +434,7 @@ export function serverAudits(opts) {
|
|
|
400
434
|
...['string', 0, false, ['array']].map((invalid) => audit(
|
|
401
435
|
// TODO: convert to MUST after watershed
|
|
402
436
|
`SHOULD use 400 status code on ${extendedTypeof(invalid)} {extensions} parameter when accepting application/graphql-response+json`, async () => {
|
|
403
|
-
const res = await fetchFn(opts.url, {
|
|
437
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
404
438
|
method: 'POST',
|
|
405
439
|
headers: {
|
|
406
440
|
'content-type': 'application/json',
|
|
@@ -414,7 +448,7 @@ export function serverAudits(opts) {
|
|
|
414
448
|
assert('Status code', res.status).toBe(400);
|
|
415
449
|
})),
|
|
416
450
|
...['string', 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {extensions} parameter when accepting application/json`, async () => {
|
|
417
|
-
const res = await fetchFn(opts.url, {
|
|
451
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
418
452
|
method: 'POST',
|
|
419
453
|
headers: {
|
|
420
454
|
'content-type': 'application/json',
|
|
@@ -431,7 +465,7 @@ export function serverAudits(opts) {
|
|
|
431
465
|
audit(
|
|
432
466
|
// TODO: convert to MUST after watershed
|
|
433
467
|
'SHOULD allow map {extensions} parameter when accepting application/graphql-response+json', async () => {
|
|
434
|
-
const res = await fetchFn(opts.url, {
|
|
468
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
435
469
|
method: 'POST',
|
|
436
470
|
headers: {
|
|
437
471
|
'content-type': 'application/json',
|
|
@@ -445,7 +479,7 @@ export function serverAudits(opts) {
|
|
|
445
479
|
assert('Status code', res.status).toBe(200);
|
|
446
480
|
}),
|
|
447
481
|
audit('MUST allow map {extensions} parameter when accepting application/json', async () => {
|
|
448
|
-
const res = await fetchFn(opts.url, {
|
|
482
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
449
483
|
method: 'POST',
|
|
450
484
|
headers: {
|
|
451
485
|
'content-type': 'application/json',
|
|
@@ -462,7 +496,7 @@ export function serverAudits(opts) {
|
|
|
462
496
|
// TODO: audit('MUST accept a map for the {extensions} parameter'),
|
|
463
497
|
// Response application/json
|
|
464
498
|
audit('SHOULD use 200 status code on JSON parsing failure when accepting application/json', async () => {
|
|
465
|
-
const res = await fetchFn(opts.url, {
|
|
499
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
466
500
|
method: 'POST',
|
|
467
501
|
headers: {
|
|
468
502
|
'content-type': 'application/json',
|
|
@@ -473,7 +507,7 @@ export function serverAudits(opts) {
|
|
|
473
507
|
assert('Status code', res.status).toBe(200);
|
|
474
508
|
}),
|
|
475
509
|
audit('SHOULD use 200 status code if parameters are invalid when accepting application/json', async () => {
|
|
476
|
-
const res = await fetchFn(opts.url, {
|
|
510
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
477
511
|
method: 'POST',
|
|
478
512
|
headers: {
|
|
479
513
|
'content-type': 'application/json',
|
|
@@ -486,7 +520,7 @@ export function serverAudits(opts) {
|
|
|
486
520
|
assert('Status code', res.status).toBe(200);
|
|
487
521
|
}),
|
|
488
522
|
audit('SHOULD use 200 status code on document parsing failure when accepting application/json', async () => {
|
|
489
|
-
const res = await fetchFn(opts.url, {
|
|
523
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
490
524
|
method: 'POST',
|
|
491
525
|
headers: {
|
|
492
526
|
'content-type': 'application/json',
|
|
@@ -497,7 +531,7 @@ export function serverAudits(opts) {
|
|
|
497
531
|
assert('Status code', res.status).toBe(200);
|
|
498
532
|
}),
|
|
499
533
|
audit('SHOULD use 200 status code on document validation failure when accepting application/json', async () => {
|
|
500
|
-
const res = await fetchFn(opts.url, {
|
|
534
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
501
535
|
method: 'POST',
|
|
502
536
|
headers: {
|
|
503
537
|
'content-type': 'application/json',
|
|
@@ -513,7 +547,7 @@ export function serverAudits(opts) {
|
|
|
513
547
|
audit(
|
|
514
548
|
// TODO: convert to MUST after watershed
|
|
515
549
|
'SHOULD use 4xx or 5xx status codes on JSON parsing failure when accepting application/graphql-response+json', async () => {
|
|
516
|
-
const res = await fetchFn(opts.url, {
|
|
550
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
517
551
|
method: 'POST',
|
|
518
552
|
headers: {
|
|
519
553
|
'content-type': 'application/json',
|
|
@@ -524,7 +558,7 @@ export function serverAudits(opts) {
|
|
|
524
558
|
assert('Status code', res.status).toBeBetween(400, 499);
|
|
525
559
|
}),
|
|
526
560
|
audit('SHOULD use 400 status code on JSON parsing failure when accepting application/graphql-response+json', async () => {
|
|
527
|
-
const res = await fetchFn(opts.url, {
|
|
561
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
528
562
|
method: 'POST',
|
|
529
563
|
headers: {
|
|
530
564
|
'content-type': 'application/json',
|
|
@@ -535,7 +569,7 @@ export function serverAudits(opts) {
|
|
|
535
569
|
assert('Status code', res.status).toBe(400);
|
|
536
570
|
}),
|
|
537
571
|
audit('SHOULD not contain the data entry on JSON parsing failure when accepting application/graphql-response+json', async () => {
|
|
538
|
-
const res = await fetchFn(opts.url, {
|
|
572
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
539
573
|
method: 'POST',
|
|
540
574
|
headers: {
|
|
541
575
|
'content-type': 'application/json',
|
|
@@ -548,7 +582,7 @@ export function serverAudits(opts) {
|
|
|
548
582
|
audit(
|
|
549
583
|
// TODO: convert to MUST after watershed
|
|
550
584
|
'SHOULD use 4xx or 5xx status codes if parameters are invalid when accepting application/graphql-response+json', async () => {
|
|
551
|
-
const res = await fetchFn(opts.url, {
|
|
585
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
552
586
|
method: 'POST',
|
|
553
587
|
headers: {
|
|
554
588
|
'content-type': 'application/json',
|
|
@@ -561,7 +595,7 @@ export function serverAudits(opts) {
|
|
|
561
595
|
assert('Status code', res.status).toBeBetween(400, 599);
|
|
562
596
|
}),
|
|
563
597
|
audit('SHOULD use 400 status code if parameters are invalid when accepting application/graphql-response+json', async () => {
|
|
564
|
-
const res = await fetchFn(opts.url, {
|
|
598
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
565
599
|
method: 'POST',
|
|
566
600
|
headers: {
|
|
567
601
|
'content-type': 'application/json',
|
|
@@ -574,7 +608,7 @@ export function serverAudits(opts) {
|
|
|
574
608
|
assert('Status code', res.status).toBe(400);
|
|
575
609
|
}),
|
|
576
610
|
audit('SHOULD not contain the data entry if parameters are invalid when accepting application/graphql-response+json', async () => {
|
|
577
|
-
const res = await fetchFn(opts.url, {
|
|
611
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
578
612
|
method: 'POST',
|
|
579
613
|
headers: {
|
|
580
614
|
'content-type': 'application/json',
|
|
@@ -589,7 +623,7 @@ export function serverAudits(opts) {
|
|
|
589
623
|
audit(
|
|
590
624
|
// TODO: convert to MUST after watershed
|
|
591
625
|
'SHOULD use 4xx or 5xx status codes on document parsing failure when accepting application/graphql-response+json', async () => {
|
|
592
|
-
const res = await fetchFn(opts.url, {
|
|
626
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
593
627
|
method: 'POST',
|
|
594
628
|
headers: {
|
|
595
629
|
'content-type': 'application/json',
|
|
@@ -602,7 +636,7 @@ export function serverAudits(opts) {
|
|
|
602
636
|
assert('Status code', res.status).toBeBetween(400, 599);
|
|
603
637
|
}),
|
|
604
638
|
audit('SHOULD use 400 status code on document parsing failure when accepting application/graphql-response+json', async () => {
|
|
605
|
-
const res = await fetchFn(opts.url, {
|
|
639
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
606
640
|
method: 'POST',
|
|
607
641
|
headers: {
|
|
608
642
|
'content-type': 'application/json',
|
|
@@ -615,7 +649,7 @@ export function serverAudits(opts) {
|
|
|
615
649
|
assert('Status code', res.status).toBe(400);
|
|
616
650
|
}),
|
|
617
651
|
audit('SHOULD not contain the data entry on document parsing failure when accepting application/graphql-response+json', async () => {
|
|
618
|
-
const res = await fetchFn(opts.url, {
|
|
652
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
619
653
|
method: 'POST',
|
|
620
654
|
headers: {
|
|
621
655
|
'content-type': 'application/json',
|
|
@@ -630,7 +664,7 @@ export function serverAudits(opts) {
|
|
|
630
664
|
audit(
|
|
631
665
|
// TODO: convert to MUST after watershed
|
|
632
666
|
'SHOULD use 4xx or 5xx status codes on document validation failure when accepting application/graphql-response+json', async () => {
|
|
633
|
-
const res = await fetchFn(opts.url, {
|
|
667
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
634
668
|
method: 'POST',
|
|
635
669
|
headers: {
|
|
636
670
|
'content-type': 'application/json',
|
|
@@ -643,7 +677,7 @@ export function serverAudits(opts) {
|
|
|
643
677
|
assert('Status code', res.status).toBeBetween(400, 599);
|
|
644
678
|
}),
|
|
645
679
|
audit('SHOULD use 400 status code on document validation failure when accepting application/graphql-response+json', async () => {
|
|
646
|
-
const res = await fetchFn(opts.url, {
|
|
680
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
647
681
|
method: 'POST',
|
|
648
682
|
headers: {
|
|
649
683
|
'content-type': 'application/json',
|
|
@@ -656,7 +690,7 @@ export function serverAudits(opts) {
|
|
|
656
690
|
assert('Status code', res.status).toBe(400);
|
|
657
691
|
}),
|
|
658
692
|
audit('SHOULD not contain the data entry on document validation failure when accepting application/graphql-response+json', async () => {
|
|
659
|
-
const res = await fetchFn(opts.url, {
|
|
693
|
+
const res = await fetchFn(await getUrl(opts.url), {
|
|
660
694
|
method: 'POST',
|
|
661
695
|
headers: {
|
|
662
696
|
'content-type': 'application/json',
|
|
@@ -687,3 +721,10 @@ export async function auditServer(opts) {
|
|
|
687
721
|
// audit tests will throw only on fatal errors, tests are contained within the AuditResult
|
|
688
722
|
return await Promise.all(audits.map(({ fn }) => fn()));
|
|
689
723
|
}
|
|
724
|
+
/** @private */
|
|
725
|
+
async function getUrl(url) {
|
|
726
|
+
if (typeof url === 'function') {
|
|
727
|
+
return await url();
|
|
728
|
+
}
|
|
729
|
+
return url;
|
|
730
|
+
}
|
package/package.json
CHANGED
|
Binary file
|