1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.cloneObject = exports.processHTTPRequest = exports.runHttpQuery = exports.throwHttpGraphQLError = exports.HttpQueryError = void 0;
|
4 | const apollo_server_env_1 = require("apollo-server-env");
|
5 | const graphqlOptions_1 = require("./graphqlOptions");
|
6 | const apollo_server_errors_1 = require("apollo-server-errors");
|
7 | const requestPipeline_1 = require("./requestPipeline");
|
8 | const cachePolicy_1 = require("./cachePolicy");
|
9 | class HttpQueryError extends Error {
|
10 | constructor(statusCode, message, isGraphQLError = false, headers) {
|
11 | super(message);
|
12 | this.name = 'HttpQueryError';
|
13 | this.statusCode = statusCode;
|
14 | this.isGraphQLError = isGraphQLError;
|
15 | this.headers = headers;
|
16 | }
|
17 | }
|
18 | exports.HttpQueryError = HttpQueryError;
|
19 | function throwHttpGraphQLError(statusCode, errors, options, extensions, headers) {
|
20 | const allHeaders = {
|
21 | 'Content-Type': 'application/json',
|
22 | };
|
23 | if (headers) {
|
24 | for (const [name, value] of headers) {
|
25 | allHeaders[name] = value;
|
26 | }
|
27 | }
|
28 | const result = {
|
29 | errors: options
|
30 | ? apollo_server_errors_1.formatApolloErrors(errors, {
|
31 | debug: options.debug,
|
32 | formatter: options.formatError,
|
33 | })
|
34 | : errors,
|
35 | };
|
36 | if (extensions) {
|
37 | result.extensions = extensions;
|
38 | }
|
39 | throw new HttpQueryError(statusCode, prettyJSONStringify(result), true, allHeaders);
|
40 | }
|
41 | exports.throwHttpGraphQLError = throwHttpGraphQLError;
|
42 | async function runHttpQuery(handlerArguments, request) {
|
43 | function debugFromNodeEnv(nodeEnv) {
|
44 | return nodeEnv !== 'production' && nodeEnv !== 'test';
|
45 | }
|
46 | let options;
|
47 | try {
|
48 | options = await graphqlOptions_1.resolveGraphqlOptions(request.options, ...handlerArguments);
|
49 | }
|
50 | catch (e) {
|
51 | return throwHttpGraphQLError(500, [e], {
|
52 | debug: debugFromNodeEnv(process.env.NODE_ENV),
|
53 | });
|
54 | }
|
55 | if (options.debug === undefined) {
|
56 | const nodeEnv = '__testing_nodeEnv__' in options
|
57 | ? options.__testing_nodeEnv__
|
58 | : process.env.NODE_ENV;
|
59 | options.debug = debugFromNodeEnv(nodeEnv);
|
60 | }
|
61 | if (typeof options.context === 'function') {
|
62 | try {
|
63 | options.context();
|
64 | }
|
65 | catch (e) {
|
66 | e.message = `Context creation failed: ${e.message}`;
|
67 | if (e.extensions &&
|
68 | e.extensions.code &&
|
69 | e.extensions.code !== 'INTERNAL_SERVER_ERROR') {
|
70 | return throwHttpGraphQLError(400, [e], options);
|
71 | }
|
72 | else {
|
73 | return throwHttpGraphQLError(500, [e], options);
|
74 | }
|
75 | }
|
76 | }
|
77 | const config = {
|
78 | schema: options.schema,
|
79 | schemaHash: options.schemaHash,
|
80 | logger: options.logger,
|
81 | rootValue: options.rootValue,
|
82 | context: options.context || {},
|
83 | validationRules: options.validationRules,
|
84 | executor: options.executor,
|
85 | fieldResolver: options.fieldResolver,
|
86 | cache: options.cache,
|
87 | dataSources: options.dataSources,
|
88 | documentStore: options.documentStore,
|
89 | persistedQueries: options.persistedQueries,
|
90 | formatError: options.formatError,
|
91 | formatResponse: options.formatResponse,
|
92 | debug: options.debug,
|
93 | plugins: options.plugins || [],
|
94 | };
|
95 | return processHTTPRequest(config, request);
|
96 | }
|
97 | exports.runHttpQuery = runHttpQuery;
|
98 | async function processHTTPRequest(options, httpRequest) {
|
99 | var _a, _b;
|
100 | let requestPayload;
|
101 | switch (httpRequest.method) {
|
102 | case 'POST':
|
103 | if (!httpRequest.query ||
|
104 | typeof httpRequest.query === 'string' ||
|
105 | Buffer.isBuffer(httpRequest.query) ||
|
106 | Object.keys(httpRequest.query).length === 0) {
|
107 | throw new HttpQueryError(400, 'POST body missing, invalid Content-Type, or JSON object has no keys.');
|
108 | }
|
109 | requestPayload = httpRequest.query;
|
110 | break;
|
111 | case 'GET':
|
112 | if (!httpRequest.query || Object.keys(httpRequest.query).length === 0) {
|
113 | throw new HttpQueryError(400, 'GET query missing.');
|
114 | }
|
115 | requestPayload = httpRequest.query;
|
116 | break;
|
117 | default:
|
118 | throw new HttpQueryError(405, 'Apollo Server supports only GET/POST requests.', false, {
|
119 | Allow: 'GET, POST',
|
120 | });
|
121 | }
|
122 | options = {
|
123 | ...options,
|
124 | plugins: [checkOperationPlugin, ...options.plugins],
|
125 | };
|
126 | function buildRequestContext(request) {
|
127 | const context = cloneObject(options.context);
|
128 | return {
|
129 | logger: options.logger || console,
|
130 | schema: options.schema,
|
131 | schemaHash: options.schemaHash,
|
132 | request,
|
133 | response: {
|
134 | http: {
|
135 | headers: new apollo_server_env_1.Headers(),
|
136 | },
|
137 | },
|
138 | context,
|
139 | cache: options.cache,
|
140 | debug: options.debug,
|
141 | metrics: {},
|
142 | overallCachePolicy: cachePolicy_1.newCachePolicy(),
|
143 | };
|
144 | }
|
145 | const responseInit = {
|
146 | headers: {
|
147 | 'Content-Type': 'application/json',
|
148 | },
|
149 | };
|
150 | let body;
|
151 | try {
|
152 | if (Array.isArray(requestPayload)) {
|
153 | const requests = requestPayload.map((requestParams) => parseGraphQLRequest(httpRequest.request, requestParams));
|
154 | const responses = await Promise.all(requests.map(async (request) => {
|
155 | try {
|
156 | const requestContext = buildRequestContext(request);
|
157 | const response = await requestPipeline_1.processGraphQLRequest(options, requestContext);
|
158 | if (response.http) {
|
159 | for (const [name, value] of response.http.headers) {
|
160 | responseInit.headers[name] = value;
|
161 | }
|
162 | if (response.http.status) {
|
163 | responseInit.status = response.http.status;
|
164 | }
|
165 | }
|
166 | return response;
|
167 | }
|
168 | catch (error) {
|
169 | return {
|
170 | errors: apollo_server_errors_1.formatApolloErrors([error], options),
|
171 | };
|
172 | }
|
173 | }));
|
174 | body = prettyJSONStringify(responses.map(serializeGraphQLResponse));
|
175 | }
|
176 | else {
|
177 | const request = parseGraphQLRequest(httpRequest.request, requestPayload);
|
178 | const requestContext = buildRequestContext(request);
|
179 | const response = await requestPipeline_1.processGraphQLRequest(options, requestContext);
|
180 | if (response.errors && typeof response.data === 'undefined') {
|
181 | return throwHttpGraphQLError(((_a = response.http) === null || _a === void 0 ? void 0 : _a.status) || 400, response.errors, undefined, response.extensions, (_b = response.http) === null || _b === void 0 ? void 0 : _b.headers);
|
182 | }
|
183 | if (response.http) {
|
184 | for (const [name, value] of response.http.headers) {
|
185 | responseInit.headers[name] = value;
|
186 | }
|
187 | if (response.http.status) {
|
188 | responseInit.status = response.http.status;
|
189 | }
|
190 | }
|
191 | body = prettyJSONStringify(serializeGraphQLResponse(response));
|
192 | }
|
193 | }
|
194 | catch (error) {
|
195 | if (error instanceof HttpQueryError) {
|
196 | throw error;
|
197 | }
|
198 | return throwHttpGraphQLError(500, [error], options);
|
199 | }
|
200 | responseInit.headers['Content-Length'] = Buffer.byteLength(body, 'utf8').toString();
|
201 | return {
|
202 | graphqlResponse: body,
|
203 | responseInit,
|
204 | };
|
205 | }
|
206 | exports.processHTTPRequest = processHTTPRequest;
|
207 | function parseGraphQLRequest(httpRequest, requestParams) {
|
208 | let queryString = requestParams.query;
|
209 | let extensions = requestParams.extensions;
|
210 | if (typeof extensions === 'string' && extensions !== '') {
|
211 | try {
|
212 | extensions = JSON.parse(extensions);
|
213 | }
|
214 | catch (error) {
|
215 | throw new HttpQueryError(400, 'Extensions are invalid JSON.');
|
216 | }
|
217 | }
|
218 | if (queryString && typeof queryString !== 'string') {
|
219 | if (queryString.kind === 'Document') {
|
220 | throw new HttpQueryError(400, "GraphQL queries must be strings. It looks like you're sending the " +
|
221 | 'internal graphql-js representation of a parsed query in your ' +
|
222 | 'request instead of a request in the GraphQL query language. You ' +
|
223 | 'can convert an AST to a string using the `print` function from ' +
|
224 | '`graphql`, or use a client like `apollo-client` which converts ' +
|
225 | 'the internal representation to a string for you.');
|
226 | }
|
227 | else {
|
228 | throw new HttpQueryError(400, 'GraphQL queries must be strings.');
|
229 | }
|
230 | }
|
231 | const operationName = requestParams.operationName;
|
232 | let variables = requestParams.variables;
|
233 | if (typeof variables === 'string' && variables !== '') {
|
234 | try {
|
235 | variables = JSON.parse(variables);
|
236 | }
|
237 | catch (error) {
|
238 | throw new HttpQueryError(400, 'Variables are invalid JSON.');
|
239 | }
|
240 | }
|
241 | return {
|
242 | query: queryString,
|
243 | operationName,
|
244 | variables,
|
245 | extensions,
|
246 | http: httpRequest,
|
247 | };
|
248 | }
|
249 | const checkOperationPlugin = {
|
250 | async requestDidStart() {
|
251 | return {
|
252 | async didResolveOperation({ request, operation }) {
|
253 | if (!request.http)
|
254 | return;
|
255 | if (request.http.method === 'GET' && operation.operation !== 'query') {
|
256 | throw new HttpQueryError(405, `GET supports only query operation`, false, {
|
257 | Allow: 'POST',
|
258 | });
|
259 | }
|
260 | },
|
261 | };
|
262 | },
|
263 | };
|
264 | function serializeGraphQLResponse(response) {
|
265 | return {
|
266 | errors: response.errors,
|
267 | data: response.data,
|
268 | extensions: response.extensions,
|
269 | };
|
270 | }
|
271 | function prettyJSONStringify(value) {
|
272 | return JSON.stringify(value) + '\n';
|
273 | }
|
274 | function cloneObject(object) {
|
275 | return Object.assign(Object.create(Object.getPrototypeOf(object)), object);
|
276 | }
|
277 | exports.cloneObject = cloneObject;
|
278 |
|
\ | No newline at end of file |