-
-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathrunner.ts
141 lines (127 loc) · 4.68 KB
/
runner.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
131
132
133
134
135
136
137
138
139
140
141
import debug from 'debug'
import { parse } from './parser'
import prependHttp from 'prepend-http'
import { GotQL } from '../types/generics'
import { QueryType } from '../types/QueryType'
import { UserOptions } from '../types/UserOptions'
import { RunnerError } from '../errors/RunnerError'
import { Got as GotInstance, Response, Options } from 'got'
import { inspect } from 'util'
const shout = debug('gotql:errors')
const info = debug('gotql:info:runner')
/**
* Extract the custom header object and mounts it together with the default objects
*
* @param {Object.<string, string>} headers Custom header Object
*/
function getHeaders(headers: Record<string, string> = {}) {
info('Mounting headers using "%o" as provided headers', headers)
const defaultHeaders = {
'X-Powered-By': 'GotQL - The server-side GraphQL query engine',
'User-Agent': `GotQL ${require('../../package.json').version}`,
'Accept-Encoding': 'gzip, deflate',
'Response-Type': 'application/json'
}
const returnObj = {
...defaultHeaders,
...headers
}
info('Mounted header object: %O', returnObj)
return returnObj
}
/**
* Extract variables from the JSON-like query
*
* @param {Object.<string, { type: string, value: string }>} variables Variable object
*/
function getQueryVariables(variables?: QueryType['variables']) {
info('Parsing query variables')
if (!variables) return null
const newVars: Record<string, string> = {}
for (const varName in variables) {
info('Parsing var "%s"', varName)
newVars[varName] = variables[varName].value
}
info('Variable object created: %O', newVars)
return newVars
}
/**
* Creates the Got body object to be sent to the GraphQL endpoint
*
* @param {Object.<string, string>} headers Custom header list
* @param {queryType} query JSON-like query type
* @param {string} parsedQuery String-parsed query
*/
function getPayload(headers: UserOptions['headers'], options: UserOptions, query: QueryType, parsedQuery: string) {
info('Generating final payload')
const returnObject: Pick<Options, 'json' | 'headers' | 'http2'> = {
headers: getHeaders(headers),
json: {
query: parsedQuery,
operationName: query.name || null,
variables: getQueryVariables(query.variables) || null
},
http2: options.useHttp2 || false
}
info('Payload to be sent: %O', returnObject)
return returnObject
}
/**
* Handles Got response object
*
* Treats GraphQL errors and messages
* @param {object} response Got response
* @param {UserOptions} options User options
*/
function handleResponse(response: Response<any>, options?: UserOptions): GotQL.Response {
info('Response obtained: %O', { errors: response.body.errors, body: response.body, statusCode: response.statusCode })
if (response.body.errors) {
shout('Error on query: %O', response.body.errors)
response.statusCode = options && options.errorStatusCode ? options.errorStatusCode : 500
response.statusMessage = 'GraphQL Error'
}
const handledResponse = {
...JSON.parse(response.body),
endpoint: response.requestUrl,
statusCode: response.statusCode,
message: response.statusMessage
}
info('Final response: %O', handledResponse)
return handledResponse
}
/**
*
* @param {string} endPoint GraphQL endpoint to query on
* @param {queryType} query A JSON-like query type
* @param {userOpts} [options] User options
* @param {string} type Can be 'query' or 'mutation'
* @param {any} got The Got object as an injected dependency (for test modularity)
* @return {{data: object, statusCode: number, message: string}} Got handled response
*/
export async function run(
endPoint: string,
query: QueryType,
type: GotQL.ExecutionType,
got: GotInstance,
options: UserOptions = { useHttp2: false }
): Promise<GotQL.Response> {
try {
info('Invoking runner with query type %s', type)
if (!['query', 'mutation'].includes(type)) throw new Error('Query type must be either `query` or `mutation`')
info('Parsing query: %O', query)
const graphQuery = parse(query, type) // Parses JSON into GraphQL Query
info('Parsed query: %s', graphQuery)
info('Building payload object')
const headers = options ? options.headers : {}
const gotPayload = getPayload(headers, options, query, graphQuery)
info('Payload object: %O', gotPayload.json)
info('Sending request...')
const response = await got.post<Request>(prependHttp(endPoint), gotPayload)
info('Response: %O', response.body.toString())
return handleResponse(response, options)
} catch (error) {
shout('Error on runner: %O', error)
if (error instanceof Error) throw new RunnerError(`Error when executing query: ${error.message}`)
throw new RunnerError(`Unknown Error: ${inspect(error)}`)
}
}