diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0da2086..a9adc18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,6 +132,17 @@ jobs: data: '{ "key": "value" }' files: '{ "file": "${{ github.workspace }}/testfile.txt" }' + - name: Request Postman Echo POST and persist response + uses: ./ + with: + url: 'https://postman-echo.com/post' + method: 'POST' + file: "${{ github.workspace }}/testfile.txt" + responseFile: "${{ github.workspace }}/response.json" + - name: Output responseFile + run: | + cat "${{ github.workspace }}/response.json" + - name: Request Postman Echo POST Multipart without data uses: ./ with: diff --git a/README.md b/README.md index 5115531..62c7acf 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ jobs: |preventFailureOnNoResponse| Prevent this Action to fail if the request respond without an response. Use 'true' (string) as value to enable it || |ignoreStatusCodes| Prevent this Action to fail if the request respond with one of the configured Status Codes. Example: '404,401' || |httpsCA| Certificate authority as string in PEM format || +|responseFile| Persist the response data to the specified file path || ### Response diff --git a/action.yml b/action.yml index 5bed675..ce0c06f 100644 --- a/action.yml +++ b/action.yml @@ -50,6 +50,9 @@ inputs: httpsCA: description: 'Certificate authority as string in PEM format' required: false + responseFile: + description: 'Persist the response data to the specified file path' + required: false outputs: response: description: 'HTTP Response Content' diff --git a/dist/index.js b/dist/index.js index 77d6a91..b727ab5 100644 --- a/dist/index.js +++ b/dist/index.js @@ -4977,6 +4977,10 @@ class GithubActions { core.debug(message) } + info(message) { + core.info(message) + } + warning(message) { core.warning(message) } @@ -4991,6 +4995,10 @@ class GithubActions { } class LogActions { + info(message) { + console.info(message) + } + debug(message) { console.info(message) } @@ -5011,6 +5019,43 @@ class LogActions { module.exports = { GithubActions, LogActions } +/***/ }), + +/***/ 6733: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const axios = __nccwpck_require__(8757); +const fs = __nccwpck_require__(7147); +const { GithubActions } = __nccwpck_require__(8169); + +/** + * @param {string} filePath + * @param {GithubActions} actions + * + * @returns {(response: axios.AxiosResponse) => void} + */ +const createPersistHandler = (filePath, actions) => (response) => { + let data = response.data + + if (typeof data == 'object') { + data = JSON.stringify(data) + } + + fs.writeFile(filePath, data, err => { + if (!err) { + actions.info(`response persisted successfully at ${filePath}`) + return + } + + actions.warning(JSON.stringify({ message: error.message, data: response.data })) + }) +} + +module.exports = { createPersistHandler } + /***/ }), /***/ 9082: @@ -5023,6 +5068,7 @@ const axios = __nccwpck_require__(8757); const FormData = __nccwpck_require__(4334) const fs = __nccwpck_require__(7147) const url = __nccwpck_require__(7310); +const { GithubActions } = __nccwpck_require__(8169); const METHOD_GET = 'GET' const METHOD_POST = 'POST' @@ -5038,12 +5084,12 @@ const CONTENT_TYPE_URLENCODED = 'application/x-www-form-urlencoded' * @param {string} param0.data Request Body as string, default {} * @param {string} param0.files Map of Request Files (name: absolute path) as JSON String, default: {} * @param {string} param0.file Single request file (absolute path) - * @param {*} param0.actions + * @param {GithubActions} param0.actions * @param {number[]} param0.ignoredCodes Prevent Action to fail if the API response with one of this StatusCodes * @param {boolean} param0.preventFailureOnNoResponse Prevent Action to fail if the API respond without Response * @param {boolean} param0.escapeData Escape unescaped JSON content in data * - * @returns {Promise} + * @returns {Promise} */ const request = async({ method, instanceConfig, data, files, file, actions, ignoredCodes, preventFailureOnNoResponse, escapeData }) => { try { @@ -5066,7 +5112,7 @@ const request = async({ method, instanceConfig, data, files, file, actions, igno data = convertToFormData(dataJson, filesJson) instanceConfig = await updateConfig(instanceConfig, data, actions) } catch(error) { - actions.setFailed({ message: `Unable to convert Data and Files into FormData: ${error.message}`, data: dataJson, files: filesJson }) + actions.setFailed(JSON.stringify({ message: `Unable to convert Data and Files into FormData: ${error.message}`, data: dataJson, files: filesJson })) return } } @@ -5094,6 +5140,7 @@ const request = async({ method, instanceConfig, data, files, file, actions, igno actions.debug('Instance Configuration: ' + JSON.stringify(instanceConfig)) + /** @type {axios.AxiosInstance} */ const instance = axios.create(instanceConfig); actions.debug('Request Data: ' + JSON.stringify(requestData)) @@ -5103,6 +5150,8 @@ const request = async({ method, instanceConfig, data, files, file, actions, igno actions.setOutput('response', JSON.stringify(response.data)) actions.setOutput('headers', response.headers) + + return response } catch (error) { if ((typeof error === 'object') && (error.isAxiosError === true)) { const { name, message, code, response } = error @@ -5357,7 +5406,7 @@ module.exports = require("zlib"); /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -// Axios v1.1.3 Copyright (c) 2022 Matt Zabriskie and contributors +// Axios v1.3.2 Copyright (c) 2023 Matt Zabriskie and contributors const FormData$1 = __nccwpck_require__(4334); @@ -5365,6 +5414,7 @@ const url = __nccwpck_require__(7310); const proxyFromEnv = __nccwpck_require__(3329); const http = __nccwpck_require__(3685); const https = __nccwpck_require__(5687); +const util = __nccwpck_require__(3837); const followRedirects = __nccwpck_require__(7707); const zlib = __nccwpck_require__(9796); const stream = __nccwpck_require__(2781); @@ -5376,6 +5426,7 @@ const FormData__default = /*#__PURE__*/_interopDefaultLegacy(FormData$1); const url__default = /*#__PURE__*/_interopDefaultLegacy(url); const http__default = /*#__PURE__*/_interopDefaultLegacy(http); const https__default = /*#__PURE__*/_interopDefaultLegacy(https); +const util__default = /*#__PURE__*/_interopDefaultLegacy(util); const followRedirects__default = /*#__PURE__*/_interopDefaultLegacy(followRedirects); const zlib__default = /*#__PURE__*/_interopDefaultLegacy(zlib); const stream__default = /*#__PURE__*/_interopDefaultLegacy(stream); @@ -5613,7 +5664,7 @@ const trim = (str) => str.trim ? * @param {Function} fn The callback to invoke for each item * * @param {Boolean} [allOwnKeys = false] - * @returns {void} + * @returns {any} */ function forEach(obj, fn, {allOwnKeys = false} = {}) { // Don't bother if no value provided @@ -5648,6 +5699,28 @@ function forEach(obj, fn, {allOwnKeys = false} = {}) { } } +function findKey(obj, key) { + key = key.toLowerCase(); + const keys = Object.keys(obj); + let i = keys.length; + let _key; + while (i-- > 0) { + _key = keys[i]; + if (key === _key.toLowerCase()) { + return _key; + } + } + return null; +} + +const _global = (() => { + /*eslint no-undef:0*/ + if (typeof globalThis !== "undefined") return globalThis; + return typeof self !== "undefined" ? self : (typeof window !== 'undefined' ? window : global) +})(); + +const isContextDefined = (context) => !isUndefined(context) && context !== _global; + /** * Accepts varargs expecting each argument to be an object, then * immutably merges the properties of each object and returns result. @@ -5667,16 +5740,18 @@ function forEach(obj, fn, {allOwnKeys = false} = {}) { * @returns {Object} Result of all merge properties */ function merge(/* obj1, obj2, obj3, ... */) { + const {caseless} = isContextDefined(this) && this || {}; const result = {}; const assignValue = (val, key) => { - if (isPlainObject(result[key]) && isPlainObject(val)) { - result[key] = merge(result[key], val); + const targetKey = caseless && findKey(result, key) || key; + if (isPlainObject(result[targetKey]) && isPlainObject(val)) { + result[targetKey] = merge(result[targetKey], val); } else if (isPlainObject(val)) { - result[key] = merge({}, val); + result[targetKey] = merge({}, val); } else if (isArray(val)) { - result[key] = val.slice(); + result[targetKey] = val.slice(); } else { - result[key] = val; + result[targetKey] = val; } }; @@ -5873,7 +5948,7 @@ const matchAll = (regExp, str) => { const isHTMLForm = kindOfTest('HTMLFormElement'); const toCamelCase = str => { - return str.toLowerCase().replace(/[_-\s]([a-z\d])(\w*)/g, + return str.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g, function replacer(m, p1, p2) { return p1.toUpperCase() + p2; } @@ -5912,6 +5987,11 @@ const reduceDescriptors = (obj, reducer) => { const freezeMethods = (obj) => { reduceDescriptors(obj, (descriptor, name) => { + // skip restricted props in strict mode + if (isFunction(obj) && ['arguments', 'caller', 'callee'].indexOf(name) !== -1) { + return false; + } + const value = obj[name]; if (!isFunction(value)) return; @@ -5925,7 +6005,7 @@ const freezeMethods = (obj) => { if (!descriptor.set) { descriptor.set = () => { - throw Error('Can not read-only method \'' + name + '\''); + throw Error('Can not rewrite read-only method \'' + name + '\''); }; } }); @@ -5952,6 +6032,68 @@ const toFiniteNumber = (value, defaultValue) => { return Number.isFinite(value) ? value : defaultValue; }; +const ALPHA = 'abcdefghijklmnopqrstuvwxyz'; + +const DIGIT = '0123456789'; + +const ALPHABET = { + DIGIT, + ALPHA, + ALPHA_DIGIT: ALPHA + ALPHA.toUpperCase() + DIGIT +}; + +const generateString = (size = 16, alphabet = ALPHABET.ALPHA_DIGIT) => { + let str = ''; + const {length} = alphabet; + while (size--) { + str += alphabet[Math.random() * length|0]; + } + + return str; +}; + +/** + * If the thing is a FormData object, return true, otherwise return false. + * + * @param {unknown} thing - The thing to check. + * + * @returns {boolean} + */ +function isSpecCompliantForm(thing) { + return !!(thing && isFunction(thing.append) && thing[Symbol.toStringTag] === 'FormData' && thing[Symbol.iterator]); +} + +const toJSONObject = (obj) => { + const stack = new Array(10); + + const visit = (source, i) => { + + if (isObject(source)) { + if (stack.indexOf(source) >= 0) { + return; + } + + if(!('toJSON' in source)) { + stack[i] = source; + const target = isArray(source) ? [] : {}; + + forEach(source, (value, key) => { + const reducedValue = visit(value, i + 1); + !isUndefined(reducedValue) && (target[key] = reducedValue); + }); + + stack[i] = undefined; + + return target; + } + } + + return source; + }; + + return visit(obj, 0); +}; + const utils = { isArray, isArrayBuffer, @@ -5994,7 +6136,14 @@ const utils = { toObjectSet, toCamelCase, noop, - toFiniteNumber + toFiniteNumber, + findKey, + global: _global, + isContextDefined, + ALPHABET, + generateString, + isSpecCompliantForm, + toJSONObject }; /** @@ -6040,7 +6189,7 @@ utils.inherits(AxiosError, Error, { columnNumber: this.columnNumber, stack: this.stack, // Axios - config: this.config, + config: utils.toJSONObject(this.config), code: this.code, status: this.response && this.response.status ? this.response.status : null }; @@ -6147,17 +6296,6 @@ const predicates = utils.toFlatObject(utils, {}, null, function filter(prop) { return /^is[A-Z]/.test(prop); }); -/** - * If the thing is a FormData object, return true, otherwise return false. - * - * @param {unknown} thing - The thing to check. - * - * @returns {boolean} - */ -function isSpecCompliant(thing) { - return thing && utils.isFunction(thing.append) && thing[Symbol.toStringTag] === 'FormData' && thing[Symbol.iterator]; -} - /** * Convert a data object to FormData * @@ -6205,7 +6343,7 @@ function toFormData(obj, formData, options) { const dots = options.dots; const indexes = options.indexes; const _Blob = options.Blob || typeof Blob !== 'undefined' && Blob; - const useBlob = _Blob && isSpecCompliant(formData); + const useBlob = _Blob && utils.isSpecCompliantForm(formData); if (!utils.isFunction(visitor)) { throw new TypeError('visitor must be a function'); @@ -6250,7 +6388,7 @@ function toFormData(obj, formData, options) { value = JSON.stringify(value); } else if ( (utils.isArray(value) && isFlatArray(value)) || - (utils.isFileList(value) || utils.endsWith(key, '[]') && (arr = utils.toArray(value)) + ((utils.isFileList(value) || utils.endsWith(key, '[]')) && (arr = utils.toArray(value)) )) { // eslint-disable-next-line no-param-reassign key = removeBrackets(key); @@ -6492,6 +6630,8 @@ class InterceptorManager { } } +const InterceptorManager$1 = InterceptorManager; + const transitionalDefaults = { silentJSONParsing: true, forcedJSONParsing: true, @@ -6610,6 +6750,543 @@ function formDataToJSON(formData) { return null; } +const DEFAULT_CONTENT_TYPE = { + 'Content-Type': undefined +}; + +/** + * It takes a string, tries to parse it, and if it fails, it returns the stringified version + * of the input + * + * @param {any} rawValue - The value to be stringified. + * @param {Function} parser - A function that parses a string into a JavaScript object. + * @param {Function} encoder - A function that takes a value and returns a string. + * + * @returns {string} A stringified version of the rawValue. + */ +function stringifySafely(rawValue, parser, encoder) { + if (utils.isString(rawValue)) { + try { + (parser || JSON.parse)(rawValue); + return utils.trim(rawValue); + } catch (e) { + if (e.name !== 'SyntaxError') { + throw e; + } + } + } + + return (encoder || JSON.stringify)(rawValue); +} + +const defaults = { + + transitional: transitionalDefaults, + + adapter: ['xhr', 'http'], + + transformRequest: [function transformRequest(data, headers) { + const contentType = headers.getContentType() || ''; + const hasJSONContentType = contentType.indexOf('application/json') > -1; + const isObjectPayload = utils.isObject(data); + + if (isObjectPayload && utils.isHTMLForm(data)) { + data = new FormData(data); + } + + const isFormData = utils.isFormData(data); + + if (isFormData) { + if (!hasJSONContentType) { + return data; + } + return hasJSONContentType ? JSON.stringify(formDataToJSON(data)) : data; + } + + if (utils.isArrayBuffer(data) || + utils.isBuffer(data) || + utils.isStream(data) || + utils.isFile(data) || + utils.isBlob(data) + ) { + return data; + } + if (utils.isArrayBufferView(data)) { + return data.buffer; + } + if (utils.isURLSearchParams(data)) { + headers.setContentType('application/x-www-form-urlencoded;charset=utf-8', false); + return data.toString(); + } + + let isFileList; + + if (isObjectPayload) { + if (contentType.indexOf('application/x-www-form-urlencoded') > -1) { + return toURLEncodedForm(data, this.formSerializer).toString(); + } + + if ((isFileList = utils.isFileList(data)) || contentType.indexOf('multipart/form-data') > -1) { + const _FormData = this.env && this.env.FormData; + + return toFormData( + isFileList ? {'files[]': data} : data, + _FormData && new _FormData(), + this.formSerializer + ); + } + } + + if (isObjectPayload || hasJSONContentType ) { + headers.setContentType('application/json', false); + return stringifySafely(data); + } + + return data; + }], + + transformResponse: [function transformResponse(data) { + const transitional = this.transitional || defaults.transitional; + const forcedJSONParsing = transitional && transitional.forcedJSONParsing; + const JSONRequested = this.responseType === 'json'; + + if (data && utils.isString(data) && ((forcedJSONParsing && !this.responseType) || JSONRequested)) { + const silentJSONParsing = transitional && transitional.silentJSONParsing; + const strictJSONParsing = !silentJSONParsing && JSONRequested; + + try { + return JSON.parse(data); + } catch (e) { + if (strictJSONParsing) { + if (e.name === 'SyntaxError') { + throw AxiosError.from(e, AxiosError.ERR_BAD_RESPONSE, this, null, this.response); + } + throw e; + } + } + } + + return data; + }], + + /** + * A timeout in milliseconds to abort a request. If set to 0 (default) a + * timeout is not created. + */ + timeout: 0, + + xsrfCookieName: 'XSRF-TOKEN', + xsrfHeaderName: 'X-XSRF-TOKEN', + + maxContentLength: -1, + maxBodyLength: -1, + + env: { + FormData: platform.classes.FormData, + Blob: platform.classes.Blob + }, + + validateStatus: function validateStatus(status) { + return status >= 200 && status < 300; + }, + + headers: { + common: { + 'Accept': 'application/json, text/plain, */*' + } + } +}; + +utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { + defaults.headers[method] = {}; +}); + +utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { + defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); +}); + +const defaults$1 = defaults; + +// RawAxiosHeaders whose duplicates are ignored by node +// c.f. https://nodejs.org/api/http.html#http_message_headers +const ignoreDuplicateOf = utils.toObjectSet([ + 'age', 'authorization', 'content-length', 'content-type', 'etag', + 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since', + 'last-modified', 'location', 'max-forwards', 'proxy-authorization', + 'referer', 'retry-after', 'user-agent' +]); + +/** + * Parse headers into an object + * + * ``` + * Date: Wed, 27 Aug 2014 08:58:49 GMT + * Content-Type: application/json + * Connection: keep-alive + * Transfer-Encoding: chunked + * ``` + * + * @param {String} rawHeaders Headers needing to be parsed + * + * @returns {Object} Headers parsed into an object + */ +const parseHeaders = rawHeaders => { + const parsed = {}; + let key; + let val; + let i; + + rawHeaders && rawHeaders.split('\n').forEach(function parser(line) { + i = line.indexOf(':'); + key = line.substring(0, i).trim().toLowerCase(); + val = line.substring(i + 1).trim(); + + if (!key || (parsed[key] && ignoreDuplicateOf[key])) { + return; + } + + if (key === 'set-cookie') { + if (parsed[key]) { + parsed[key].push(val); + } else { + parsed[key] = [val]; + } + } else { + parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + } + }); + + return parsed; +}; + +const $internals = Symbol('internals'); + +function normalizeHeader(header) { + return header && String(header).trim().toLowerCase(); +} + +function normalizeValue(value) { + if (value === false || value == null) { + return value; + } + + return utils.isArray(value) ? value.map(normalizeValue) : String(value); +} + +function parseTokens(str) { + const tokens = Object.create(null); + const tokensRE = /([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g; + let match; + + while ((match = tokensRE.exec(str))) { + tokens[match[1]] = match[2]; + } + + return tokens; +} + +function isValidHeaderName(str) { + return /^[-_a-zA-Z]+$/.test(str.trim()); +} + +function matchHeaderValue(context, value, header, filter) { + if (utils.isFunction(filter)) { + return filter.call(this, value, header); + } + + if (!utils.isString(value)) return; + + if (utils.isString(filter)) { + return value.indexOf(filter) !== -1; + } + + if (utils.isRegExp(filter)) { + return filter.test(value); + } +} + +function formatHeader(header) { + return header.trim() + .toLowerCase().replace(/([a-z\d])(\w*)/g, (w, char, str) => { + return char.toUpperCase() + str; + }); +} + +function buildAccessors(obj, header) { + const accessorName = utils.toCamelCase(' ' + header); + + ['get', 'set', 'has'].forEach(methodName => { + Object.defineProperty(obj, methodName + accessorName, { + value: function(arg1, arg2, arg3) { + return this[methodName].call(this, header, arg1, arg2, arg3); + }, + configurable: true + }); + }); +} + +class AxiosHeaders { + constructor(headers) { + headers && this.set(headers); + } + + set(header, valueOrRewrite, rewrite) { + const self = this; + + function setHeader(_value, _header, _rewrite) { + const lHeader = normalizeHeader(_header); + + if (!lHeader) { + throw new Error('header name must be a non-empty string'); + } + + const key = utils.findKey(self, lHeader); + + if(!key || self[key] === undefined || _rewrite === true || (_rewrite === undefined && self[key] !== false)) { + self[key || _header] = normalizeValue(_value); + } + } + + const setHeaders = (headers, _rewrite) => + utils.forEach(headers, (_value, _header) => setHeader(_value, _header, _rewrite)); + + if (utils.isPlainObject(header) || header instanceof this.constructor) { + setHeaders(header, valueOrRewrite); + } else if(utils.isString(header) && (header = header.trim()) && !isValidHeaderName(header)) { + setHeaders(parseHeaders(header), valueOrRewrite); + } else { + header != null && setHeader(valueOrRewrite, header, rewrite); + } + + return this; + } + + get(header, parser) { + header = normalizeHeader(header); + + if (header) { + const key = utils.findKey(this, header); + + if (key) { + const value = this[key]; + + if (!parser) { + return value; + } + + if (parser === true) { + return parseTokens(value); + } + + if (utils.isFunction(parser)) { + return parser.call(this, value, key); + } + + if (utils.isRegExp(parser)) { + return parser.exec(value); + } + + throw new TypeError('parser must be boolean|regexp|function'); + } + } + } + + has(header, matcher) { + header = normalizeHeader(header); + + if (header) { + const key = utils.findKey(this, header); + + return !!(key && this[key] !== undefined && (!matcher || matchHeaderValue(this, this[key], key, matcher))); + } + + return false; + } + + delete(header, matcher) { + const self = this; + let deleted = false; + + function deleteHeader(_header) { + _header = normalizeHeader(_header); + + if (_header) { + const key = utils.findKey(self, _header); + + if (key && (!matcher || matchHeaderValue(self, self[key], key, matcher))) { + delete self[key]; + + deleted = true; + } + } + } + + if (utils.isArray(header)) { + header.forEach(deleteHeader); + } else { + deleteHeader(header); + } + + return deleted; + } + + clear(matcher) { + const keys = Object.keys(this); + let i = keys.length; + let deleted = false; + + while (i--) { + const key = keys[i]; + if(!matcher || matchHeaderValue(this, this[key], key, matcher)) { + delete this[key]; + deleted = true; + } + } + + return deleted; + } + + normalize(format) { + const self = this; + const headers = {}; + + utils.forEach(this, (value, header) => { + const key = utils.findKey(headers, header); + + if (key) { + self[key] = normalizeValue(value); + delete self[header]; + return; + } + + const normalized = format ? formatHeader(header) : String(header).trim(); + + if (normalized !== header) { + delete self[header]; + } + + self[normalized] = normalizeValue(value); + + headers[normalized] = true; + }); + + return this; + } + + concat(...targets) { + return this.constructor.concat(this, ...targets); + } + + toJSON(asStrings) { + const obj = Object.create(null); + + utils.forEach(this, (value, header) => { + value != null && value !== false && (obj[header] = asStrings && utils.isArray(value) ? value.join(', ') : value); + }); + + return obj; + } + + [Symbol.iterator]() { + return Object.entries(this.toJSON())[Symbol.iterator](); + } + + toString() { + return Object.entries(this.toJSON()).map(([header, value]) => header + ': ' + value).join('\n'); + } + + get [Symbol.toStringTag]() { + return 'AxiosHeaders'; + } + + static from(thing) { + return thing instanceof this ? thing : new this(thing); + } + + static concat(first, ...targets) { + const computed = new this(first); + + targets.forEach((target) => computed.set(target)); + + return computed; + } + + static accessor(header) { + const internals = this[$internals] = (this[$internals] = { + accessors: {} + }); + + const accessors = internals.accessors; + const prototype = this.prototype; + + function defineAccessor(_header) { + const lHeader = normalizeHeader(_header); + + if (!accessors[lHeader]) { + buildAccessors(prototype, _header); + accessors[lHeader] = true; + } + } + + utils.isArray(header) ? header.forEach(defineAccessor) : defineAccessor(header); + + return this; + } +} + +AxiosHeaders.accessor(['Content-Type', 'Content-Length', 'Accept', 'Accept-Encoding', 'User-Agent', 'Authorization']); + +utils.freezeMethods(AxiosHeaders.prototype); +utils.freezeMethods(AxiosHeaders); + +const AxiosHeaders$1 = AxiosHeaders; + +/** + * Transform the data for a request or a response + * + * @param {Array|Function} fns A single function or Array of functions + * @param {?Object} response The response object + * + * @returns {*} The resulting transformed data + */ +function transformData(fns, response) { + const config = this || defaults$1; + const context = response || config; + const headers = AxiosHeaders$1.from(context.headers); + let data = context.data; + + utils.forEach(fns, function transform(fn) { + data = fn.call(config, data, headers.normalize(), response ? response.status : undefined); + }); + + headers.normalize(); + + return data; +} + +function isCancel(value) { + return !!(value && value.__CANCEL__); +} + +/** + * A `CanceledError` is an object that is thrown when an operation is canceled. + * + * @param {string=} message The message. + * @param {Object=} config The config. + * @param {Object=} request The request. + * + * @returns {CanceledError} The created error. + */ +function CanceledError(message, config, request) { + // eslint-disable-next-line no-eq-null,eqeqeq + AxiosError.call(this, message == null ? 'canceled' : message, AxiosError.ERR_CANCELED, config, request); + this.name = 'CanceledError'; +} + +utils.inherits(CanceledError, AxiosError, { + __CANCEL__: true +}); + /** * Resolve or reject a Promise based on response status. * @@ -6679,26 +7356,7 @@ function buildFullPath(baseURL, requestedURL) { return requestedURL; } -const VERSION = "1.1.3"; - -/** - * A `CanceledError` is an object that is thrown when an operation is canceled. - * - * @param {string=} message The message. - * @param {Object=} config The config. - * @param {Object=} request The request. - * - * @returns {CanceledError} The created error. - */ -function CanceledError(message, config, request) { - // eslint-disable-next-line no-eq-null,eqeqeq - AxiosError.call(this, message == null ? 'canceled' : message, AxiosError.ERR_CANCELED, config, request); - this.name = 'CanceledError'; -} - -utils.inherits(CanceledError, AxiosError, { - __CANCEL__: true -}); +const VERSION = "1.3.2"; function parseProtocol(url) { const match = /^([-+\w]{1,25})(:?\/\/|:)/.exec(url); @@ -6753,320 +7411,6 @@ function fromDataURI(uri, asBlob, options) { throw new AxiosError('Unsupported protocol ' + protocol, AxiosError.ERR_NOT_SUPPORT); } -// RawAxiosHeaders whose duplicates are ignored by node -// c.f. https://nodejs.org/api/http.html#http_message_headers -const ignoreDuplicateOf = utils.toObjectSet([ - 'age', 'authorization', 'content-length', 'content-type', 'etag', - 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since', - 'last-modified', 'location', 'max-forwards', 'proxy-authorization', - 'referer', 'retry-after', 'user-agent' -]); - -/** - * Parse headers into an object - * - * ``` - * Date: Wed, 27 Aug 2014 08:58:49 GMT - * Content-Type: application/json - * Connection: keep-alive - * Transfer-Encoding: chunked - * ``` - * - * @param {String} rawHeaders Headers needing to be parsed - * - * @returns {Object} Headers parsed into an object - */ -const parseHeaders = rawHeaders => { - const parsed = {}; - let key; - let val; - let i; - - rawHeaders && rawHeaders.split('\n').forEach(function parser(line) { - i = line.indexOf(':'); - key = line.substring(0, i).trim().toLowerCase(); - val = line.substring(i + 1).trim(); - - if (!key || (parsed[key] && ignoreDuplicateOf[key])) { - return; - } - - if (key === 'set-cookie') { - if (parsed[key]) { - parsed[key].push(val); - } else { - parsed[key] = [val]; - } - } else { - parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; - } - }); - - return parsed; -}; - -const $internals = Symbol('internals'); -const $defaults = Symbol('defaults'); - -function normalizeHeader(header) { - return header && String(header).trim().toLowerCase(); -} - -function normalizeValue(value) { - if (value === false || value == null) { - return value; - } - - return utils.isArray(value) ? value.map(normalizeValue) : String(value); -} - -function parseTokens(str) { - const tokens = Object.create(null); - const tokensRE = /([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g; - let match; - - while ((match = tokensRE.exec(str))) { - tokens[match[1]] = match[2]; - } - - return tokens; -} - -function matchHeaderValue(context, value, header, filter) { - if (utils.isFunction(filter)) { - return filter.call(this, value, header); - } - - if (!utils.isString(value)) return; - - if (utils.isString(filter)) { - return value.indexOf(filter) !== -1; - } - - if (utils.isRegExp(filter)) { - return filter.test(value); - } -} - -function formatHeader(header) { - return header.trim() - .toLowerCase().replace(/([a-z\d])(\w*)/g, (w, char, str) => { - return char.toUpperCase() + str; - }); -} - -function buildAccessors(obj, header) { - const accessorName = utils.toCamelCase(' ' + header); - - ['get', 'set', 'has'].forEach(methodName => { - Object.defineProperty(obj, methodName + accessorName, { - value: function(arg1, arg2, arg3) { - return this[methodName].call(this, header, arg1, arg2, arg3); - }, - configurable: true - }); - }); -} - -function findKey(obj, key) { - key = key.toLowerCase(); - const keys = Object.keys(obj); - let i = keys.length; - let _key; - while (i-- > 0) { - _key = keys[i]; - if (key === _key.toLowerCase()) { - return _key; - } - } - return null; -} - -function AxiosHeaders(headers, defaults) { - headers && this.set(headers); - this[$defaults] = defaults || null; -} - -Object.assign(AxiosHeaders.prototype, { - set: function(header, valueOrRewrite, rewrite) { - const self = this; - - function setHeader(_value, _header, _rewrite) { - const lHeader = normalizeHeader(_header); - - if (!lHeader) { - throw new Error('header name must be a non-empty string'); - } - - const key = findKey(self, lHeader); - - if (key && _rewrite !== true && (self[key] === false || _rewrite === false)) { - return; - } - - self[key || _header] = normalizeValue(_value); - } - - if (utils.isPlainObject(header)) { - utils.forEach(header, (_value, _header) => { - setHeader(_value, _header, valueOrRewrite); - }); - } else { - setHeader(valueOrRewrite, header, rewrite); - } - - return this; - }, - - get: function(header, parser) { - header = normalizeHeader(header); - - if (!header) return undefined; - - const key = findKey(this, header); - - if (key) { - const value = this[key]; - - if (!parser) { - return value; - } - - if (parser === true) { - return parseTokens(value); - } - - if (utils.isFunction(parser)) { - return parser.call(this, value, key); - } - - if (utils.isRegExp(parser)) { - return parser.exec(value); - } - - throw new TypeError('parser must be boolean|regexp|function'); - } - }, - - has: function(header, matcher) { - header = normalizeHeader(header); - - if (header) { - const key = findKey(this, header); - - return !!(key && (!matcher || matchHeaderValue(this, this[key], key, matcher))); - } - - return false; - }, - - delete: function(header, matcher) { - const self = this; - let deleted = false; - - function deleteHeader(_header) { - _header = normalizeHeader(_header); - - if (_header) { - const key = findKey(self, _header); - - if (key && (!matcher || matchHeaderValue(self, self[key], key, matcher))) { - delete self[key]; - - deleted = true; - } - } - } - - if (utils.isArray(header)) { - header.forEach(deleteHeader); - } else { - deleteHeader(header); - } - - return deleted; - }, - - clear: function() { - return Object.keys(this).forEach(this.delete.bind(this)); - }, - - normalize: function(format) { - const self = this; - const headers = {}; - - utils.forEach(this, (value, header) => { - const key = findKey(headers, header); - - if (key) { - self[key] = normalizeValue(value); - delete self[header]; - return; - } - - const normalized = format ? formatHeader(header) : String(header).trim(); - - if (normalized !== header) { - delete self[header]; - } - - self[normalized] = normalizeValue(value); - - headers[normalized] = true; - }); - - return this; - }, - - toJSON: function(asStrings) { - const obj = Object.create(null); - - utils.forEach(Object.assign({}, this[$defaults] || null, this), - (value, header) => { - if (value == null || value === false) return; - obj[header] = asStrings && utils.isArray(value) ? value.join(', ') : value; - }); - - return obj; - } -}); - -Object.assign(AxiosHeaders, { - from: function(thing) { - if (utils.isString(thing)) { - return new this(parseHeaders(thing)); - } - return thing instanceof this ? thing : new this(thing); - }, - - accessor: function(header) { - const internals = this[$internals] = (this[$internals] = { - accessors: {} - }); - - const accessors = internals.accessors; - const prototype = this.prototype; - - function defineAccessor(_header) { - const lHeader = normalizeHeader(_header); - - if (!accessors[lHeader]) { - buildAccessors(prototype, _header); - accessors[lHeader] = true; - } - } - - utils.isArray(header) ? header.forEach(defineAccessor) : defineAccessor(header); - - return this; - } -}); - -AxiosHeaders.accessor(['Content-Type', 'Content-Length', 'Accept', 'Accept-Encoding', 'User-Agent']); - -utils.freezeMethods(AxiosHeaders.prototype); -utils.freezeMethods(AxiosHeaders); - /** * Throttle decorator * @param {Function} fn @@ -7145,7 +7489,7 @@ function speedometer(samplesCount, min) { const passed = startedAt && now - startedAt; - return passed ? Math.round(bytesCount * 1000 / passed) : undefined; + return passed ? Math.round(bytesCount * 1000 / passed) : undefined; }; } @@ -7332,6 +7676,166 @@ class AxiosTransformStream extends stream__default["default"].Transform{ } } +const AxiosTransformStream$1 = AxiosTransformStream; + +const {asyncIterator} = Symbol; + +const readBlob = async function* (blob) { + if (blob.stream) { + yield* blob.stream(); + } else if (blob.arrayBuffer) { + yield await blob.arrayBuffer(); + } else if (blob[asyncIterator]) { + yield* blob[asyncIterator](); + } else { + yield blob; + } +}; + +const readBlob$1 = readBlob; + +const BOUNDARY_ALPHABET = utils.ALPHABET.ALPHA_DIGIT + '-_'; + +const textEncoder = new util.TextEncoder(); + +const CRLF = '\r\n'; +const CRLF_BYTES = textEncoder.encode(CRLF); +const CRLF_BYTES_COUNT = 2; + +class FormDataPart { + constructor(name, value) { + const {escapeName} = this.constructor; + const isStringValue = utils.isString(value); + + let headers = `Content-Disposition: form-data; name="${escapeName(name)}"${ + !isStringValue && value.name ? `; filename="${escapeName(value.name)}"` : '' + }${CRLF}`; + + if (isStringValue) { + value = textEncoder.encode(String(value).replace(/\r?\n|\r\n?/g, CRLF)); + } else { + headers += `Content-Type: ${value.type || "application/octet-stream"}${CRLF}`; + } + + this.headers = textEncoder.encode(headers + CRLF); + + this.contentLength = isStringValue ? value.byteLength : value.size; + + this.size = this.headers.byteLength + this.contentLength + CRLF_BYTES_COUNT; + + this.name = name; + this.value = value; + } + + async *encode(){ + yield this.headers; + + const {value} = this; + + if(utils.isTypedArray(value)) { + yield value; + } else { + yield* readBlob$1(value); + } + + yield CRLF_BYTES; + } + + static escapeName(name) { + return String(name).replace(/[\r\n"]/g, (match) => ({ + '\r' : '%0D', + '\n' : '%0A', + '"' : '%22', + }[match])); + } +} + +const formDataToStream = (form, headersHandler, options) => { + const { + tag = 'form-data-boundary', + size = 25, + boundary = tag + '-' + utils.generateString(size, BOUNDARY_ALPHABET) + } = options || {}; + + if(!utils.isFormData(form)) { + throw TypeError('FormData instance required'); + } + + if (boundary.length < 1 || boundary.length > 70) { + throw Error('boundary must be 10-70 characters long') + } + + const boundaryBytes = textEncoder.encode('--' + boundary + CRLF); + const footerBytes = textEncoder.encode('--' + boundary + '--' + CRLF + CRLF); + let contentLength = footerBytes.byteLength; + + const parts = Array.from(form.entries()).map(([name, value]) => { + const part = new FormDataPart(name, value); + contentLength += part.size; + return part; + }); + + contentLength += boundaryBytes.byteLength * parts.length; + + contentLength = utils.toFiniteNumber(contentLength); + + const computedHeaders = { + 'Content-Type': `multipart/form-data; boundary=${boundary}` + }; + + if (Number.isFinite(contentLength)) { + computedHeaders['Content-Length'] = contentLength; + } + + headersHandler && headersHandler(computedHeaders); + + return stream.Readable.from((async function *() { + for(const part of parts) { + yield boundaryBytes; + yield* part.encode(); + } + + yield footerBytes; + })()); +}; + +const formDataToStream$1 = formDataToStream; + +class ZlibHeaderTransformStream extends stream__default["default"].Transform { + __transform(chunk, encoding, callback) { + this.push(chunk); + callback(); + } + + _transform(chunk, encoding, callback) { + if (chunk.length !== 0) { + this._transform = this.__transform; + + // Add Default Compression headers if no zlib headers are present + if (chunk[0] !== 120) { // Hex: 78 + const header = Buffer.alloc(2); + header[0] = 120; // Hex: 78 + header[1] = 156; // Hex: 9C + this.push(header, encoding); + } + } + + this.__transform(chunk, encoding, callback); + } +} + +const ZlibHeaderTransformStream$1 = ZlibHeaderTransformStream; + +const zlibOptions = { + flush: zlib__default["default"].constants.Z_SYNC_FLUSH, + finishFlush: zlib__default["default"].constants.Z_SYNC_FLUSH +}; + +const brotliOptions = { + flush: zlib__default["default"].constants.BROTLI_OPERATION_FLUSH, + finishFlush: zlib__default["default"].constants.BROTLI_OPERATION_FLUSH +}; + const isBrotliSupported = utils.isFunction(zlib__default["default"].createBrotliDecompress); const {http: httpFollow, https: httpsFollow} = followRedirects__default["default"]; @@ -7412,9 +7916,12 @@ function setProxy(options, configProxy, location) { }; } +const isHttpAdapterSupported = typeof process !== 'undefined' && utils.kindOf(process) === 'process'; + /*eslint consistent-return:0*/ -function httpAdapter(config) { - return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) { +const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { + /*eslint no-async-promise-executor:0*/ + return new Promise(async function dispatchHttpRequest(resolvePromise, rejectPromise) { let data = config.data; const responseType = config.responseType; const responseEncoding = config.responseEncoding; @@ -7478,7 +7985,7 @@ function httpAdapter(config) { // Parse url const fullPath = buildFullPath(config.baseURL, config.url); - const parsed = new URL(fullPath); + const parsed = new URL(fullPath, 'http://localhost'); const protocol = parsed.protocol || supportedProtocols[0]; if (protocol === 'data:') { @@ -7505,7 +8012,7 @@ function httpAdapter(config) { convertedData = convertedData.toString(responseEncoding); if (!responseEncoding || responseEncoding === 'utf8') { - data = utils.stripBOM(convertedData); + convertedData = utils.stripBOM(convertedData); } } else if (responseType === 'stream') { convertedData = stream__default["default"].Readable.from(convertedData); @@ -7515,7 +8022,7 @@ function httpAdapter(config) { data: convertedData, status: 200, statusText: 'OK', - headers: {}, + headers: new AxiosHeaders$1(), config }); } @@ -7528,7 +8035,7 @@ function httpAdapter(config) { )); } - const headers = AxiosHeaders.from(config.headers).normalize(); + const headers = AxiosHeaders$1.from(config.headers).normalize(); // Set User-Agent (required by some servers) // See https://github.com/axios/axios/issues/69 @@ -7542,9 +8049,32 @@ function httpAdapter(config) { let maxUploadRate = undefined; let maxDownloadRate = undefined; - // support for https://www.npmjs.com/package/form-data api - if (utils.isFormData(data) && utils.isFunction(data.getHeaders)) { + // support for spec compliant FormData objects + if (utils.isSpecCompliantForm(data)) { + const userBoundary = headers.getContentType(/boundary=([-_\w\d]{10,70})/i); + + data = formDataToStream$1(data, (formHeaders) => { + headers.set(formHeaders); + }, { + tag: `axios-${VERSION}-boundary`, + boundary: userBoundary && userBoundary[1] || undefined + }); + // support for https://www.npmjs.com/package/form-data api + } else if (utils.isFormData(data) && utils.isFunction(data.getHeaders)) { headers.set(data.getHeaders()); + + if (!headers.hasContentLength()) { + try { + const knownLength = await util__default["default"].promisify(data.getLength).call(data); + headers.setContentLength(knownLength); + /*eslint no-empty:0*/ + } catch (e) { + } + } + } else if (utils.isBlob(data)) { + data.size && headers.setContentType(data.type || 'application/octet-stream'); + headers.setContentLength(data.size || 0); + data = stream__default["default"].Readable.from(readBlob$1(data)); } else if (data && !utils.isStream(data)) { if (Buffer.isBuffer(data)) ; else if (utils.isArrayBuffer(data)) { data = Buffer.from(new Uint8Array(data)); @@ -7559,7 +8089,7 @@ function httpAdapter(config) { } // Add Content-Length header if data exists - headers.set('Content-Length', data.length, false); + headers.setContentLength(data.length, false); if (config.maxBodyLength > -1 && data.length > config.maxBodyLength) { return reject(new AxiosError( @@ -7570,7 +8100,7 @@ function httpAdapter(config) { } } - const contentLength = +headers.getContentLength(); + const contentLength = utils.toFiniteNumber(headers.getContentLength()); if (utils.isArray(maxRate)) { maxUploadRate = maxRate[0]; @@ -7584,8 +8114,8 @@ function httpAdapter(config) { data = stream__default["default"].Readable.from(data, {objectMode: false}); } - data = stream__default["default"].pipeline([data, new AxiosTransformStream({ - length: utils.toFiniteNumber(contentLength), + data = stream__default["default"].pipeline([data, new AxiosTransformStream$1({ + length: contentLength, maxRate: utils.toFiniteNumber(maxUploadRate) })], utils.noop); @@ -7628,7 +8158,10 @@ function httpAdapter(config) { return reject(customErr); } - headers.set('Accept-Encoding', 'gzip, deflate, br', false); + headers.set( + 'Accept-Encoding', + 'gzip, compress, deflate' + (isBrotliSupported ? ', br' : ''), false + ); const options = { path, @@ -7683,43 +8216,10 @@ function httpAdapter(config) { const streams = [res]; - // uncompress the response body transparently if required - let responseStream = res; - - // return the last request in case of redirects - const lastRequest = res.req || req; - - // if decompress disabled we should not decompress - if (config.decompress !== false) { - // if no content, but headers still say that it is encoded, - // remove the header not confuse downstream operations - if (data && data.length === 0 && res.headers['content-encoding']) { - delete res.headers['content-encoding']; - } - - switch (res.headers['content-encoding']) { - /*eslint default-case:0*/ - case 'gzip': - case 'compress': - case 'deflate': - // add the unzipper to the body stream processing pipeline - streams.push(zlib__default["default"].createUnzip()); - - // remove the content-encoding in order to not confuse downstream operations - delete res.headers['content-encoding']; - break; - case 'br': - if (isBrotliSupported) { - streams.push(zlib__default["default"].createBrotliDecompress()); - delete res.headers['content-encoding']; - } - } - } + const responseLength = +res.headers['content-length']; if (onDownloadProgress) { - const responseLength = +res.headers['content-length']; - - const transformStream = new AxiosTransformStream({ + const transformStream = new AxiosTransformStream$1({ length: utils.toFiniteNumber(responseLength), maxRate: utils.toFiniteNumber(maxDownloadRate) }); @@ -7733,6 +8233,49 @@ function httpAdapter(config) { streams.push(transformStream); } + // decompress the response body transparently if required + let responseStream = res; + + // return the last request in case of redirects + const lastRequest = res.req || req; + + // if decompress disabled we should not decompress + if (config.decompress !== false && res.headers['content-encoding']) { + // if no content, but headers still say that it is encoded, + // remove the header not confuse downstream operations + if (method === 'HEAD' || res.statusCode === 204) { + delete res.headers['content-encoding']; + } + + switch (res.headers['content-encoding']) { + /*eslint default-case:0*/ + case 'gzip': + case 'x-gzip': + case 'compress': + case 'x-compress': + // add the unzipper to the body stream processing pipeline + streams.push(zlib__default["default"].createUnzip(zlibOptions)); + + // remove the content-encoding in order to not confuse downstream operations + delete res.headers['content-encoding']; + break; + case 'deflate': + streams.push(new ZlibHeaderTransformStream$1()); + + // add the unzipper to the body stream processing pipeline + streams.push(zlib__default["default"].createUnzip(zlibOptions)); + + // remove the content-encoding in order to not confuse downstream operations + delete res.headers['content-encoding']; + break; + case 'br': + if (isBrotliSupported) { + streams.push(zlib__default["default"].createBrotliDecompress(brotliOptions)); + delete res.headers['content-encoding']; + } + } + } + responseStream = streams.length > 1 ? stream__default["default"].pipeline(streams, utils.noop) : streams[0]; const offListeners = stream__default["default"].finished(responseStream, () => { @@ -7743,7 +8286,7 @@ function httpAdapter(config) { const response = { status: res.statusCode, statusText: res.statusMessage, - headers: new AxiosHeaders(res.headers), + headers: new AxiosHeaders$1(res.headers), config, request: lastRequest }; @@ -7896,7 +8439,7 @@ function httpAdapter(config) { req.end(data); } }); -} +}; const cookies = platform.isStandardBrowserEnv ? @@ -8028,7 +8571,8 @@ function progressEventReducer(listener, isDownloadStream) { progress: total ? (loaded / total) : undefined, bytes: progressBytes, rate: rate ? rate : undefined, - estimated: rate && total && inRange ? (total - loaded) / rate : undefined + estimated: rate && total && inRange ? (total - loaded) / rate : undefined, + event: e }; data[isDownloadStream ? 'download' : 'upload'] = true; @@ -8037,10 +8581,12 @@ function progressEventReducer(listener, isDownloadStream) { }; } -function xhrAdapter(config) { +const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined'; + +const xhrAdapter = isXHRAdapterSupported && function (config) { return new Promise(function dispatchXhrRequest(resolve, reject) { let requestData = config.data; - const requestHeaders = AxiosHeaders.from(config.headers).normalize(); + const requestHeaders = AxiosHeaders$1.from(config.headers).normalize(); const responseType = config.responseType; let onCanceled; function done() { @@ -8053,7 +8599,7 @@ function xhrAdapter(config) { } } - if (utils.isFormData(requestData) && platform.isStandardBrowserEnv) { + if (utils.isFormData(requestData) && (platform.isStandardBrowserEnv || platform.isStandardBrowserWebWorkerEnv)) { requestHeaders.setContentType(false); // Let the browser set it } @@ -8078,10 +8624,10 @@ function xhrAdapter(config) { return; } // Prepare the response - const responseHeaders = AxiosHeaders.from( + const responseHeaders = AxiosHeaders$1.from( 'getAllResponseHeaders' in request && request.getAllResponseHeaders() ); - const responseData = !responseType || responseType === 'text' || responseType === 'json' ? + const responseData = !responseType || responseType === 'text' || responseType === 'json' ? request.responseText : request.response; const response = { data: responseData, @@ -8238,238 +8784,63 @@ function xhrAdapter(config) { // Send the request request.send(requestData || null); }); -} +}; -const adapters = { +const knownAdapters = { http: httpAdapter, xhr: xhrAdapter }; -const adapters$1 = { - getAdapter: (nameOrAdapter) => { - if(utils.isString(nameOrAdapter)){ - const adapter = adapters[nameOrAdapter]; +utils.forEach(knownAdapters, (fn, value) => { + if(fn) { + try { + Object.defineProperty(fn, 'name', {value}); + } catch (e) { + // eslint-disable-next-line no-empty + } + Object.defineProperty(fn, 'adapterName', {value}); + } +}); - if (!nameOrAdapter) { - throw Error( - utils.hasOwnProp(nameOrAdapter) ? - `Adapter '${nameOrAdapter}' is not available in the build` : - `Can not resolve adapter '${nameOrAdapter}'` +const adapters = { + getAdapter: (adapters) => { + adapters = utils.isArray(adapters) ? adapters : [adapters]; + + const {length} = adapters; + let nameOrAdapter; + let adapter; + + for (let i = 0; i < length; i++) { + nameOrAdapter = adapters[i]; + if((adapter = utils.isString(nameOrAdapter) ? knownAdapters[nameOrAdapter.toLowerCase()] : nameOrAdapter)) { + break; + } + } + + if (!adapter) { + if (adapter === false) { + throw new AxiosError( + `Adapter ${nameOrAdapter} is not supported by the environment`, + 'ERR_NOT_SUPPORT' ); } - return adapter + throw new Error( + utils.hasOwnProp(knownAdapters, nameOrAdapter) ? + `Adapter '${nameOrAdapter}' is not available in the build` : + `Unknown adapter '${nameOrAdapter}'` + ); } - if (!utils.isFunction(nameOrAdapter)) { + if (!utils.isFunction(adapter)) { throw new TypeError('adapter is not a function'); } - return nameOrAdapter; + return adapter; }, - adapters + adapters: knownAdapters }; -const DEFAULT_CONTENT_TYPE = { - 'Content-Type': 'application/x-www-form-urlencoded' -}; - -/** - * If the browser has an XMLHttpRequest object, use the XHR adapter, otherwise use the HTTP - * adapter - * - * @returns {Function} - */ -function getDefaultAdapter() { - let adapter; - if (typeof XMLHttpRequest !== 'undefined') { - // For browsers use XHR adapter - adapter = adapters$1.getAdapter('xhr'); - } else if (typeof process !== 'undefined' && utils.kindOf(process) === 'process') { - // For node use HTTP adapter - adapter = adapters$1.getAdapter('http'); - } - return adapter; -} - -/** - * It takes a string, tries to parse it, and if it fails, it returns the stringified version - * of the input - * - * @param {any} rawValue - The value to be stringified. - * @param {Function} parser - A function that parses a string into a JavaScript object. - * @param {Function} encoder - A function that takes a value and returns a string. - * - * @returns {string} A stringified version of the rawValue. - */ -function stringifySafely(rawValue, parser, encoder) { - if (utils.isString(rawValue)) { - try { - (parser || JSON.parse)(rawValue); - return utils.trim(rawValue); - } catch (e) { - if (e.name !== 'SyntaxError') { - throw e; - } - } - } - - return (encoder || JSON.stringify)(rawValue); -} - -const defaults = { - - transitional: transitionalDefaults, - - adapter: getDefaultAdapter(), - - transformRequest: [function transformRequest(data, headers) { - const contentType = headers.getContentType() || ''; - const hasJSONContentType = contentType.indexOf('application/json') > -1; - const isObjectPayload = utils.isObject(data); - - if (isObjectPayload && utils.isHTMLForm(data)) { - data = new FormData(data); - } - - const isFormData = utils.isFormData(data); - - if (isFormData) { - if (!hasJSONContentType) { - return data; - } - return hasJSONContentType ? JSON.stringify(formDataToJSON(data)) : data; - } - - if (utils.isArrayBuffer(data) || - utils.isBuffer(data) || - utils.isStream(data) || - utils.isFile(data) || - utils.isBlob(data) - ) { - return data; - } - if (utils.isArrayBufferView(data)) { - return data.buffer; - } - if (utils.isURLSearchParams(data)) { - headers.setContentType('application/x-www-form-urlencoded;charset=utf-8', false); - return data.toString(); - } - - let isFileList; - - if (isObjectPayload) { - if (contentType.indexOf('application/x-www-form-urlencoded') > -1) { - return toURLEncodedForm(data, this.formSerializer).toString(); - } - - if ((isFileList = utils.isFileList(data)) || contentType.indexOf('multipart/form-data') > -1) { - const _FormData = this.env && this.env.FormData; - - return toFormData( - isFileList ? {'files[]': data} : data, - _FormData && new _FormData(), - this.formSerializer - ); - } - } - - if (isObjectPayload || hasJSONContentType ) { - headers.setContentType('application/json', false); - return stringifySafely(data); - } - - return data; - }], - - transformResponse: [function transformResponse(data) { - const transitional = this.transitional || defaults.transitional; - const forcedJSONParsing = transitional && transitional.forcedJSONParsing; - const JSONRequested = this.responseType === 'json'; - - if (data && utils.isString(data) && ((forcedJSONParsing && !this.responseType) || JSONRequested)) { - const silentJSONParsing = transitional && transitional.silentJSONParsing; - const strictJSONParsing = !silentJSONParsing && JSONRequested; - - try { - return JSON.parse(data); - } catch (e) { - if (strictJSONParsing) { - if (e.name === 'SyntaxError') { - throw AxiosError.from(e, AxiosError.ERR_BAD_RESPONSE, this, null, this.response); - } - throw e; - } - } - } - - return data; - }], - - /** - * A timeout in milliseconds to abort a request. If set to 0 (default) a - * timeout is not created. - */ - timeout: 0, - - xsrfCookieName: 'XSRF-TOKEN', - xsrfHeaderName: 'X-XSRF-TOKEN', - - maxContentLength: -1, - maxBodyLength: -1, - - env: { - FormData: platform.classes.FormData, - Blob: platform.classes.Blob - }, - - validateStatus: function validateStatus(status) { - return status >= 200 && status < 300; - }, - - headers: { - common: { - 'Accept': 'application/json, text/plain, */*' - } - } -}; - -utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { - defaults.headers[method] = {}; -}); - -utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { - defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE); -}); - -/** - * Transform the data for a request or a response - * - * @param {Array|Function} fns A single function or Array of functions - * @param {?Object} response The response object - * - * @returns {*} The resulting transformed data - */ -function transformData(fns, response) { - const config = this || defaults; - const context = response || config; - const headers = AxiosHeaders.from(context.headers); - let data = context.data; - - utils.forEach(fns, function transform(fn) { - data = fn.call(config, data, headers.normalize(), response ? response.status : undefined); - }); - - headers.normalize(); - - return data; -} - -function isCancel(value) { - return !!(value && value.__CANCEL__); -} - /** * Throws a `CanceledError` if cancellation has been requested. * @@ -8483,7 +8854,7 @@ function throwIfCancellationRequested(config) { } if (config.signal && config.signal.aborted) { - throw new CanceledError(); + throw new CanceledError(null, config); } } @@ -8497,7 +8868,7 @@ function throwIfCancellationRequested(config) { function dispatchRequest(config) { throwIfCancellationRequested(config); - config.headers = AxiosHeaders.from(config.headers); + config.headers = AxiosHeaders$1.from(config.headers); // Transform request data config.data = transformData.call( @@ -8505,7 +8876,11 @@ function dispatchRequest(config) { config.transformRequest ); - const adapter = config.adapter || defaults.adapter; + if (['post', 'put', 'patch'].indexOf(config.method) !== -1) { + config.headers.setContentType('application/x-www-form-urlencoded', false); + } + + const adapter = adapters.getAdapter(config.adapter || defaults$1.adapter); return adapter(config).then(function onAdapterResolution(response) { throwIfCancellationRequested(config); @@ -8517,7 +8892,7 @@ function dispatchRequest(config) { response ); - response.headers = AxiosHeaders.from(response.headers); + response.headers = AxiosHeaders$1.from(response.headers); return response; }, function onAdapterRejection(reason) { @@ -8531,7 +8906,7 @@ function dispatchRequest(config) { config.transformResponse, reason.response ); - reason.response.headers = AxiosHeaders.from(reason.response.headers); + reason.response.headers = AxiosHeaders$1.from(reason.response.headers); } } @@ -8539,6 +8914,8 @@ function dispatchRequest(config) { }); } +const headersToObject = (thing) => thing instanceof AxiosHeaders$1 ? thing.toJSON() : thing; + /** * Config-specific merge-function which creates a new config-object * by merging two configuration objects together. @@ -8553,9 +8930,9 @@ function mergeConfig(config1, config2) { config2 = config2 || {}; const config = {}; - function getMergedValue(target, source) { + function getMergedValue(target, source, caseless) { if (utils.isPlainObject(target) && utils.isPlainObject(source)) { - return utils.merge(target, source); + return utils.merge.call({caseless}, target, source); } else if (utils.isPlainObject(source)) { return utils.merge({}, source); } else if (utils.isArray(source)) { @@ -8565,72 +8942,73 @@ function mergeConfig(config1, config2) { } // eslint-disable-next-line consistent-return - function mergeDeepProperties(prop) { - if (!utils.isUndefined(config2[prop])) { - return getMergedValue(config1[prop], config2[prop]); - } else if (!utils.isUndefined(config1[prop])) { - return getMergedValue(undefined, config1[prop]); + function mergeDeepProperties(a, b, caseless) { + if (!utils.isUndefined(b)) { + return getMergedValue(a, b, caseless); + } else if (!utils.isUndefined(a)) { + return getMergedValue(undefined, a, caseless); } } // eslint-disable-next-line consistent-return - function valueFromConfig2(prop) { - if (!utils.isUndefined(config2[prop])) { - return getMergedValue(undefined, config2[prop]); + function valueFromConfig2(a, b) { + if (!utils.isUndefined(b)) { + return getMergedValue(undefined, b); } } // eslint-disable-next-line consistent-return - function defaultToConfig2(prop) { - if (!utils.isUndefined(config2[prop])) { - return getMergedValue(undefined, config2[prop]); - } else if (!utils.isUndefined(config1[prop])) { - return getMergedValue(undefined, config1[prop]); + function defaultToConfig2(a, b) { + if (!utils.isUndefined(b)) { + return getMergedValue(undefined, b); + } else if (!utils.isUndefined(a)) { + return getMergedValue(undefined, a); } } // eslint-disable-next-line consistent-return - function mergeDirectKeys(prop) { + function mergeDirectKeys(a, b, prop) { if (prop in config2) { - return getMergedValue(config1[prop], config2[prop]); + return getMergedValue(a, b); } else if (prop in config1) { - return getMergedValue(undefined, config1[prop]); + return getMergedValue(undefined, a); } } const mergeMap = { - 'url': valueFromConfig2, - 'method': valueFromConfig2, - 'data': valueFromConfig2, - 'baseURL': defaultToConfig2, - 'transformRequest': defaultToConfig2, - 'transformResponse': defaultToConfig2, - 'paramsSerializer': defaultToConfig2, - 'timeout': defaultToConfig2, - 'timeoutMessage': defaultToConfig2, - 'withCredentials': defaultToConfig2, - 'adapter': defaultToConfig2, - 'responseType': defaultToConfig2, - 'xsrfCookieName': defaultToConfig2, - 'xsrfHeaderName': defaultToConfig2, - 'onUploadProgress': defaultToConfig2, - 'onDownloadProgress': defaultToConfig2, - 'decompress': defaultToConfig2, - 'maxContentLength': defaultToConfig2, - 'maxBodyLength': defaultToConfig2, - 'beforeRedirect': defaultToConfig2, - 'transport': defaultToConfig2, - 'httpAgent': defaultToConfig2, - 'httpsAgent': defaultToConfig2, - 'cancelToken': defaultToConfig2, - 'socketPath': defaultToConfig2, - 'responseEncoding': defaultToConfig2, - 'validateStatus': mergeDirectKeys + url: valueFromConfig2, + method: valueFromConfig2, + data: valueFromConfig2, + baseURL: defaultToConfig2, + transformRequest: defaultToConfig2, + transformResponse: defaultToConfig2, + paramsSerializer: defaultToConfig2, + timeout: defaultToConfig2, + timeoutMessage: defaultToConfig2, + withCredentials: defaultToConfig2, + adapter: defaultToConfig2, + responseType: defaultToConfig2, + xsrfCookieName: defaultToConfig2, + xsrfHeaderName: defaultToConfig2, + onUploadProgress: defaultToConfig2, + onDownloadProgress: defaultToConfig2, + decompress: defaultToConfig2, + maxContentLength: defaultToConfig2, + maxBodyLength: defaultToConfig2, + beforeRedirect: defaultToConfig2, + transport: defaultToConfig2, + httpAgent: defaultToConfig2, + httpsAgent: defaultToConfig2, + cancelToken: defaultToConfig2, + socketPath: defaultToConfig2, + responseEncoding: defaultToConfig2, + validateStatus: mergeDirectKeys, + headers: (a, b) => mergeDeepProperties(headersToObject(a), headersToObject(b), true) }; utils.forEach(Object.keys(config1).concat(Object.keys(config2)), function computeConfigValue(prop) { const merge = mergeMap[prop] || mergeDeepProperties; - const configValue = merge(prop); + const configValue = merge(config1[prop], config2[prop], prop); (utils.isUndefined(configValue) && merge !== mergeDirectKeys) || (config[prop] = configValue); }); @@ -8737,8 +9115,8 @@ class Axios { constructor(instanceConfig) { this.defaults = instanceConfig; this.interceptors = { - request: new InterceptorManager(), - response: new InterceptorManager() + request: new InterceptorManager$1(), + response: new InterceptorManager$1() }; } @@ -8762,7 +9140,7 @@ class Axios { config = mergeConfig(this.defaults, config); - const {transitional, paramsSerializer} = config; + const {transitional, paramsSerializer, headers} = config; if (transitional !== undefined) { validator.assertOptions(transitional, { @@ -8782,20 +9160,22 @@ class Axios { // Set config.method config.method = (config.method || this.defaults.method || 'get').toLowerCase(); + let contextHeaders; + // Flatten headers - const defaultHeaders = config.headers && utils.merge( - config.headers.common, - config.headers[config.method] + contextHeaders = headers && utils.merge( + headers.common, + headers[config.method] ); - defaultHeaders && utils.forEach( + contextHeaders && utils.forEach( ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], - function cleanHeaderConfig(method) { - delete config.headers[method]; + (method) => { + delete headers[method]; } ); - config.headers = new AxiosHeaders(config.headers, defaultHeaders); + config.headers = AxiosHeaders$1.concat(contextHeaders, headers); // filter out skipped interceptors const requestInterceptorChain = []; @@ -8907,6 +9287,8 @@ utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { Axios.prototype[method + 'Form'] = generateHTTPMethod(true); }); +const Axios$1 = Axios; + /** * A `CancelToken` is an object that can be used to request cancellation of an operation. * @@ -9023,6 +9405,8 @@ class CancelToken { } } +const CancelToken$1 = CancelToken; + /** * Syntactic sugar for invoking a function and expanding an array for arguments. * @@ -9061,6 +9445,78 @@ function isAxiosError(payload) { return utils.isObject(payload) && (payload.isAxiosError === true); } +const HttpStatusCode = { + Continue: 100, + SwitchingProtocols: 101, + Processing: 102, + EarlyHints: 103, + Ok: 200, + Created: 201, + Accepted: 202, + NonAuthoritativeInformation: 203, + NoContent: 204, + ResetContent: 205, + PartialContent: 206, + MultiStatus: 207, + AlreadyReported: 208, + ImUsed: 226, + MultipleChoices: 300, + MovedPermanently: 301, + Found: 302, + SeeOther: 303, + NotModified: 304, + UseProxy: 305, + Unused: 306, + TemporaryRedirect: 307, + PermanentRedirect: 308, + BadRequest: 400, + Unauthorized: 401, + PaymentRequired: 402, + Forbidden: 403, + NotFound: 404, + MethodNotAllowed: 405, + NotAcceptable: 406, + ProxyAuthenticationRequired: 407, + RequestTimeout: 408, + Conflict: 409, + Gone: 410, + LengthRequired: 411, + PreconditionFailed: 412, + PayloadTooLarge: 413, + UriTooLong: 414, + UnsupportedMediaType: 415, + RangeNotSatisfiable: 416, + ExpectationFailed: 417, + ImATeapot: 418, + MisdirectedRequest: 421, + UnprocessableEntity: 422, + Locked: 423, + FailedDependency: 424, + TooEarly: 425, + UpgradeRequired: 426, + PreconditionRequired: 428, + TooManyRequests: 429, + RequestHeaderFieldsTooLarge: 431, + UnavailableForLegalReasons: 451, + InternalServerError: 500, + NotImplemented: 501, + BadGateway: 502, + ServiceUnavailable: 503, + GatewayTimeout: 504, + HttpVersionNotSupported: 505, + VariantAlsoNegotiates: 506, + InsufficientStorage: 507, + LoopDetected: 508, + NotExtended: 510, + NetworkAuthenticationRequired: 511, +}; + +Object.entries(HttpStatusCode).forEach(([key, value]) => { + HttpStatusCode[value] = key; +}); + +const HttpStatusCode$1 = HttpStatusCode; + /** * Create an instance of Axios * @@ -9069,11 +9525,11 @@ function isAxiosError(payload) { * @returns {Axios} A new instance of Axios */ function createInstance(defaultConfig) { - const context = new Axios(defaultConfig); - const instance = bind(Axios.prototype.request, context); + const context = new Axios$1(defaultConfig); + const instance = bind(Axios$1.prototype.request, context); // Copy axios.prototype to instance - utils.extend(instance, Axios.prototype, context, {allOwnKeys: true}); + utils.extend(instance, Axios$1.prototype, context, {allOwnKeys: true}); // Copy context to instance utils.extend(instance, context, null, {allOwnKeys: true}); @@ -9087,14 +9543,14 @@ function createInstance(defaultConfig) { } // Create the default instance to be exported -const axios = createInstance(defaults); +const axios = createInstance(defaults$1); // Expose Axios class to allow class inheritance -axios.Axios = Axios; +axios.Axios = Axios$1; // Expose Cancel & CancelToken axios.CanceledError = CanceledError; -axios.CancelToken = CancelToken; +axios.CancelToken = CancelToken$1; axios.isCancel = isCancel; axios.VERSION = VERSION; axios.toFormData = toFormData; @@ -9115,9 +9571,16 @@ axios.spread = spread; // Expose isAxiosError axios.isAxiosError = isAxiosError; -axios.formToJSON = thing => { - return formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing); -}; +// Expose mergeConfig +axios.mergeConfig = mergeConfig; + +axios.AxiosHeaders = AxiosHeaders$1; + +axios.formToJSON = thing => formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing); + +axios.HttpStatusCode = HttpStatusCode$1; + +axios.default = axios; module.exports = axios; //# sourceMappingURL=axios.cjs.map @@ -9182,6 +9645,7 @@ const axios = __nccwpck_require__(8757); const https = __nccwpck_require__(5687); const { request, METHOD_POST } = __nccwpck_require__(9082); const { GithubActions } = __nccwpck_require__(8169); +const { createPersistHandler } = __nccwpck_require__(6733); let customHeaders = {} @@ -9222,18 +9686,30 @@ if (!!core.getInput('username') || !!core.getInput('password')) { const data = core.getInput('data') || '{}'; const files = core.getInput('files') || '{}'; const file = core.getInput('file') +const responseFile = core.getInput('responseFile') const method = core.getInput('method') || METHOD_POST; const preventFailureOnNoResponse = core.getInput('preventFailureOnNoResponse') === 'true'; const escapeData = core.getInput('escapeData') === 'true'; -const ignoreStatusCodes = core.getInput('ignoreStatusCodes') -let ignoredCodes = [] +const ignoreStatusCodes = core.getInput('ignoreStatusCodes'); +let ignoredCodes = []; if (typeof ignoreStatusCodes === 'string' && ignoreStatusCodes.length > 0) { ignoredCodes = ignoreStatusCodes.split(',').map(statusCode => parseInt(statusCode.trim())) } -request({ data, method, instanceConfig, preventFailureOnNoResponse, escapeData, files, file, ignoredCodes, actions: new GithubActions() }) +const handler = []; +const actions = new GithubActions(); + +if (!!responseFile) { + handler.push(createPersistHandler(responseFile, actions)) +} + +request({ data, method, instanceConfig, preventFailureOnNoResponse, escapeData, files, file, ignoredCodes, actions }).then(response => { + if (typeof response == 'object') { + handler.forEach(h => h(response)) + } +}) })(); diff --git a/package-lock.json b/package-lock.json index ed8b93d..f32f5a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@vercel/ncc": "^0.36.1", - "axios": "^1.2.6", + "axios": "^1.2", "form-data": "^4.0.0", "yargs": "^17.6.2" }, @@ -78,9 +78,9 @@ "dev": true }, "node_modules/axios": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.6.tgz", - "integrity": "sha512-rC/7F08XxZwjMV4iuWv+JpD3E0Ksqg9nac4IIg6RwNuF0JTeWoCo/mBNG54+tNhhI11G3/VDRbdDQTs9hGp4pQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.2.tgz", + "integrity": "sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==", "dev": true, "dependencies": { "follow-redirects": "^1.15.0", @@ -386,9 +386,9 @@ "dev": true }, "axios": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.6.tgz", - "integrity": "sha512-rC/7F08XxZwjMV4iuWv+JpD3E0Ksqg9nac4IIg6RwNuF0JTeWoCo/mBNG54+tNhhI11G3/VDRbdDQTs9hGp4pQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.2.tgz", + "integrity": "sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw==", "dev": true, "requires": { "follow-redirects": "^1.15.0", diff --git a/package.json b/package.json index 187ae4a..9b4936a 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "homepage": "https://github.com/fjogeleit/http-request-action#readme", "devDependencies": { "@vercel/ncc": "^0.36.1", - "axios": "^1.2.6", + "axios": "^1.2", "form-data": "^4.0.0", "yargs": "^17.6.2" }, diff --git a/src/githubActions.js b/src/githubActions.js index 7bde98b..6af6360 100644 --- a/src/githubActions.js +++ b/src/githubActions.js @@ -7,6 +7,10 @@ class GithubActions { core.debug(message) } + info(message) { + core.info(message) + } + warning(message) { core.warning(message) } @@ -21,6 +25,10 @@ class GithubActions { } class LogActions { + info(message) { + console.info(message) + } + debug(message) { console.info(message) } diff --git a/src/handler/persist.js b/src/handler/persist.js new file mode 100644 index 0000000..58ffb38 --- /dev/null +++ b/src/handler/persist.js @@ -0,0 +1,30 @@ +'use strict' + +const axios = require('axios'); +const fs = require('fs'); +const { GithubActions } = require('../githubActions'); + +/** + * @param {string} filePath + * @param {GithubActions} actions + * + * @returns {(response: axios.AxiosResponse) => void} + */ +const createPersistHandler = (filePath, actions) => (response) => { + let data = response.data + + if (typeof data == 'object') { + data = JSON.stringify(data) + } + + fs.writeFile(filePath, data, err => { + if (!err) { + actions.info(`response persisted successfully at ${filePath}`) + return + } + + actions.warning(JSON.stringify({ message: error.message, data: response.data })) + }) +} + +module.exports = { createPersistHandler } \ No newline at end of file diff --git a/src/httpClient.js b/src/httpClient.js index 0a3b0fe..bea553c 100644 --- a/src/httpClient.js +++ b/src/httpClient.js @@ -4,6 +4,7 @@ const axios = require('axios'); const FormData = require('form-data') const fs = require('fs') const url = require('url'); +const { GithubActions } = require('./githubActions'); const METHOD_GET = 'GET' const METHOD_POST = 'POST' @@ -19,12 +20,12 @@ const CONTENT_TYPE_URLENCODED = 'application/x-www-form-urlencoded' * @param {string} param0.data Request Body as string, default {} * @param {string} param0.files Map of Request Files (name: absolute path) as JSON String, default: {} * @param {string} param0.file Single request file (absolute path) - * @param {*} param0.actions + * @param {GithubActions} param0.actions * @param {number[]} param0.ignoredCodes Prevent Action to fail if the API response with one of this StatusCodes * @param {boolean} param0.preventFailureOnNoResponse Prevent Action to fail if the API respond without Response * @param {boolean} param0.escapeData Escape unescaped JSON content in data * - * @returns {Promise} + * @returns {Promise} */ const request = async({ method, instanceConfig, data, files, file, actions, ignoredCodes, preventFailureOnNoResponse, escapeData }) => { try { @@ -47,7 +48,7 @@ const request = async({ method, instanceConfig, data, files, file, actions, igno data = convertToFormData(dataJson, filesJson) instanceConfig = await updateConfig(instanceConfig, data, actions) } catch(error) { - actions.setFailed({ message: `Unable to convert Data and Files into FormData: ${error.message}`, data: dataJson, files: filesJson }) + actions.setFailed(JSON.stringify({ message: `Unable to convert Data and Files into FormData: ${error.message}`, data: dataJson, files: filesJson })) return } } @@ -75,6 +76,7 @@ const request = async({ method, instanceConfig, data, files, file, actions, igno actions.debug('Instance Configuration: ' + JSON.stringify(instanceConfig)) + /** @type {axios.AxiosInstance} */ const instance = axios.create(instanceConfig); actions.debug('Request Data: ' + JSON.stringify(requestData)) @@ -84,6 +86,8 @@ const request = async({ method, instanceConfig, data, files, file, actions, igno actions.setOutput('response', JSON.stringify(response.data)) actions.setOutput('headers', response.headers) + + return response } catch (error) { if ((typeof error === 'object') && (error.isAxiosError === true)) { const { name, message, code, response } = error diff --git a/src/index.js b/src/index.js index 2752e87..af722ba 100644 --- a/src/index.js +++ b/src/index.js @@ -5,6 +5,7 @@ const axios = require('axios'); const https = require('https'); const { request, METHOD_POST } = require('./httpClient'); const { GithubActions } = require('./githubActions'); +const { createPersistHandler } = require('./handler/persist'); let customHeaders = {} @@ -45,15 +46,27 @@ if (!!core.getInput('username') || !!core.getInput('password')) { const data = core.getInput('data') || '{}'; const files = core.getInput('files') || '{}'; const file = core.getInput('file') +const responseFile = core.getInput('responseFile') const method = core.getInput('method') || METHOD_POST; const preventFailureOnNoResponse = core.getInput('preventFailureOnNoResponse') === 'true'; const escapeData = core.getInput('escapeData') === 'true'; -const ignoreStatusCodes = core.getInput('ignoreStatusCodes') -let ignoredCodes = [] +const ignoreStatusCodes = core.getInput('ignoreStatusCodes'); +let ignoredCodes = []; if (typeof ignoreStatusCodes === 'string' && ignoreStatusCodes.length > 0) { ignoredCodes = ignoreStatusCodes.split(',').map(statusCode => parseInt(statusCode.trim())) } -request({ data, method, instanceConfig, preventFailureOnNoResponse, escapeData, files, file, ignoredCodes, actions: new GithubActions() }) +const handler = []; +const actions = new GithubActions(); + +if (!!responseFile) { + handler.push(createPersistHandler(responseFile, actions)) +} + +request({ data, method, instanceConfig, preventFailureOnNoResponse, escapeData, files, file, ignoredCodes, actions }).then(response => { + if (typeof response == 'object') { + handler.forEach(h => h(response)) + } +})