UNPKG

5.47 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.getGot = void 0;
4const js_lib_1 = require("@naturalcycles/js-lib");
5const got_1 = require("got");
6const __1 = require("..");
7const colors_1 = require("../colors");
8/**
9 * Returns instance of Got with "reasonable defaults":
10 *
11 * 1. Error handler hook that prints helpful errors.
12 * 2. Hooks that log start/end of request (optional, false by default).
13 * 3. Reasonable defaults(tm), e.g non-infinite Timeout
14 */
15function getGot(opt = {}) {
16 return got_1.default.extend({
17 // Most-important is to set to anything non-empty (so, requests don't "hang" by default).
18 // Should be long enough to handle for slow responses from scaled cloud APIs in times of spikes
19 // Ideally should be LESS than default Request timeout in backend-lib (so, it has a chance to error
20 // before server times out with 503).
21 timeout: 90000,
22 ...opt,
23 hooks: {
24 ...opt.hooks,
25 beforeError: [
26 ...(opt.hooks?.beforeError || []),
27 // User hooks go BEFORE
28 gotErrorHook(opt),
29 ],
30 beforeRequest: [
31 gotBeforeRequestHook(opt),
32 // User hooks go AFTER
33 ...(opt.hooks?.beforeRequest || []),
34 ],
35 afterResponse: [
36 ...(opt.hooks?.afterResponse || []),
37 // User hooks go BEFORE
38 gotAfterResponseHook(opt),
39 ],
40 },
41 });
42}
43exports.getGot = getGot;
44/**
45 * Without this hook (default behaviour):
46 *
47 * HTTPError: Response code 422 (Unprocessable Entity)
48 * at EventEmitter.<anonymous> (.../node_modules/got/dist/source/as-promise.js:118:31)
49 * at processTicksAndRejections (internal/process/task_queues.js:97:5) {
50 * name: 'HTTPError'
51 *
52 *
53 * With this hook:
54 *
55 * HTTPError 422 GET http://a.com/err?q=1 in 8 ms
56 * {
57 * message: 'Reference already exists',
58 * documentation_url: 'https://developer.github.com/v3/git/refs/#create-a-reference'
59 * }
60 *
61 * Features:
62 * 1. Includes original method and URL (including e.g searchParams) in the error message.
63 * 2. Includes response.body in the error message (limited length).
64 * 3. Auto-detects and parses JSON response body (limited length).
65 * 4. Includes time spent (gotBeforeRequestHook must also be enabled).
66 * UPD: excluded now to allow automatic Sentry error grouping
67 */
68function gotErrorHook(opt = {}) {
69 const { maxResponseLength = 10000 } = opt;
70 return err => {
71 if (err instanceof got_1.HTTPError) {
72 const { statusCode } = err.response;
73 const { method, url, prefixUrl } = err.options;
74 const shortUrl = getShortUrl(opt, url, prefixUrl);
75 // const { started } = context as GotRequestContext
76 const body = (0, __1.inspectAny)(err.response.body, {
77 maxLen: maxResponseLength,
78 colors: false,
79 });
80 // timings are not part of err.message to allow automatic error grouping in Sentry
81 err.message = [[statusCode, method, shortUrl].filter(Boolean).join(' '), body]
82 .filter(Boolean)
83 .join('\n');
84 }
85 return err;
86 };
87}
88function gotBeforeRequestHook(opt) {
89 return options => {
90 options.context = {
91 ...options.context,
92 started: Date.now(),
93 };
94 if (opt.logStart) {
95 const shortUrl = getShortUrl(opt, options.url, options.prefixUrl);
96 console.log([(0, colors_1.dimGrey)(' >>'), (0, colors_1.dimGrey)(options.method), (0, colors_1.grey)(shortUrl)].join(' '));
97 }
98 };
99}
100function gotAfterResponseHook(opt = {}) {
101 return resp => {
102 const success = resp.statusCode >= 200 && resp.statusCode < 400;
103 if (opt.logFinished) {
104 const { started } = resp.request.options.context;
105 const { url, prefixUrl, method } = resp.request.options;
106 const shortUrl = getShortUrl(opt, url, prefixUrl);
107 console.log([
108 (0, colors_1.dimGrey)(' <<'),
109 coloredHttpCode(resp.statusCode),
110 (0, colors_1.dimGrey)(method),
111 (0, colors_1.grey)(shortUrl),
112 started && (0, colors_1.dimGrey)('in ' + (0, js_lib_1._since)(started)),
113 ]
114 .filter(Boolean)
115 .join(' '));
116 // console.log(`afterResp! ${resp.request.options.method} ${resp.url}`, { context: resp.request.options.context })
117 }
118 // Error responses are not logged, cause they're included in Error message already
119 if (opt.logResponse && success) {
120 console.log((0, __1.inspectAny)((0, js_lib_1._jsonParseIfPossible)(resp.body), { maxLen: opt.maxResponseLength }));
121 }
122 return resp;
123 };
124}
125function coloredHttpCode(statusCode) {
126 if (statusCode < 400)
127 return (0, colors_1.dimGrey)(statusCode); // default
128 if (statusCode < 500)
129 return (0, colors_1.yellow)(statusCode);
130 return (0, colors_1.red)(statusCode);
131}
132function getShortUrl(opt, url, prefixUrl) {
133 let shortUrl = url.toString();
134 if (opt.logWithSearchParams === false) {
135 shortUrl = shortUrl.split('?')[0];
136 }
137 if (opt.logWithPrefixUrl === false && prefixUrl && shortUrl.startsWith(prefixUrl)) {
138 shortUrl = shortUrl.slice(prefixUrl.length);
139 }
140 return shortUrl;
141}