-
Notifications
You must be signed in to change notification settings - Fork 145
Expand file tree
/
Copy pathcurl.ts
More file actions
143 lines (114 loc) · 3.74 KB
/
Copy pathcurl.ts
File metadata and controls
143 lines (114 loc) · 3.74 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
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
131
132
133
134
135
136
137
138
139
140
141
142
143
import { Kind, parse, print, type DocumentNode } from 'graphql';
import { handleTemplate } from './operations.utils';
import type { LaboratorySettings } from './settings';
export type BuildCurlCommandInput = {
endpoint: string;
query: string;
/** JSON strings straight off the operation, before template substitution. */
variables?: string | null;
headers?: string | null;
extensions?: string | null;
operationName?: string | null;
/** Headers produced by preflight; operation headers win on conflict. */
preflightHeaders?: Record<string, string>;
env?: Record<string, unknown>;
pluginsState?: Record<string, unknown>;
settings?: Pick<LaboratorySettings, 'fetch'>;
};
/** Single-quoted shell literal: end the quote, escape, reopen. */
const shellQuote = (value: string) => `'${value.replaceAll("'", `'\\''`)}'`;
const parseTemplatedJson = (
value: string | null | undefined,
scope: Record<string, unknown>,
): Record<string, unknown> => {
if (!value) {
return {};
}
try {
return JSON.parse(handleTemplate(value, scope)) ?? {};
} catch {
return {};
}
};
/**
* Keeps the selected operation and every fragment, dropping the other operations,
* mirroring what the executor sends.
*/
const selectOperation = (query: string, operationName?: string | null): string => {
let document: DocumentNode;
try {
document = parse(query);
} catch {
return query;
}
const definitions = document.definitions.filter(definition => {
if (
definition.kind === Kind.OPERATION_DEFINITION &&
operationName &&
definition.name?.value !== operationName
) {
return false;
}
return true;
});
return print({ kind: Kind.DOCUMENT, definitions });
};
/**
* Renders the active operation as a cURL command.
*
* Mirrors the request the laboratory itself sends (see runActiveOperation in
* lib/operations.ts) so the copied command reproduces it, rather than being a
* plausible-looking approximation.
*/
export function buildCurlCommand(input: BuildCurlCommandInput): string {
const scope = { ...input.env, plugins: input.pluginsState ?? {} };
const headers: Record<string, string> = {
'content-type': 'application/json',
...input.preflightHeaders,
...(parseTemplatedJson(input.headers, scope) as Record<string, string>),
};
const body: Record<string, unknown> = {
query: selectOperation(input.query, input.operationName),
};
if (input.operationName) {
body.operationName = input.operationName;
}
const variables = parseTemplatedJson(input.variables, scope);
if (Object.keys(variables).length > 0) {
body.variables = variables;
}
const extensions = parseTemplatedJson(input.extensions, scope);
if (Object.keys(extensions).length > 0) {
body.extensions = extensions;
}
const isGet = input.settings?.fetch?.useGETForQueries === true;
const parts: string[] = ['curl'];
let url = input.endpoint;
if (isGet) {
const params = new URLSearchParams({ query: body.query as string });
if (input.operationName) {
params.set('operationName', input.operationName);
}
if (body.variables) {
params.set('variables', JSON.stringify(body.variables));
}
if (body.extensions) {
params.set('extensions', JSON.stringify(body.extensions));
}
url = `${input.endpoint}?${params.toString()}`;
delete headers['content-type'];
} else {
parts.push('-X POST');
}
parts.push(shellQuote(url));
if (input.settings?.fetch?.credentials === 'include') {
parts.push('--cookie-jar /dev/null');
}
for (const [name, value] of Object.entries(headers)) {
parts.push(`-H ${shellQuote(`${name}: ${value}`)}`);
}
if (!isGet) {
parts.push(`-d ${shellQuote(JSON.stringify(body))}`);
}
return parts.join(' \\\n ');
}