UNPKG

@atlassian/atlassian-connect-express

Version:

Library for building Atlassian Add-ons on top of Express

120 lines (108 loc) 3.64 kB
const request = require("request"); const _ = require("lodash"); const URI = require("urijs"); const utils = require("./utils"); class ForgeHostRequest { constructor(addon, apiBaseUrl, token) { if (!addon) { throw new Error("addon must be defined"); } if (!token || typeof token !== "string") { throw new Error( "Forge token is not available. Double check your manifest and ensure this route is protected by authenticateForge()." ); } if (!apiBaseUrl || typeof apiBaseUrl !== "string") { throw new Error( "Forge base URL is missing. Ensure this route is protected by authenticateForge()." ); } this.addon = addon; this.token = token; this.apiBaseUrl = apiBaseUrl; } static _crossProtocolRedirectGuard(response) { if (!response.headers["location"]) { return true; } if ( !response.request || !response.request.uri || !response.request.uri.protocol ) { return true; } const locationUri = new URI(response.headers["location"]); const requestUri = new URI(response.request.uri.href); if (!locationUri || !locationUri.protocol || !locationUri.protocol()) { return true; } return locationUri.protocol() === requestUri.protocol(); } _makeVerb(method) { const safeRequestDefaults = { followRedirect: ForgeHostRequest._crossProtocolRedirectGuard }; return function (options, callback) { const self = this; const augmentHeaders = function (headers) { const appId = self.addon.config.appId(); headers["User-Agent"] = self.addon.config.userAgent( `forgeAppId=${appId}` ); headers.authorization = `Bearer ${self.token}`; }; let args; try { args = utils.modifyArgs( self.addon, options || {}, augmentHeaders, callback, self.apiBaseUrl ); const opts = args[0]; /*For GET method, drop request body: To be compliant with https://developer.atlassian.com/cloud/jira/platform/changelog/#CHANGE-2328 and https://developer.atlassian.com/cloud/confluence/changelog/#CHANGE-2328 */ const multipartFormData = method === "get" ? null : opts.multipartFormData; delete opts.multipartFormData; if (method === "get") { delete opts.body; delete opts.formData; // If options.json is true and there is no other body, it only adds the "accept: application/json" header. so we keep it. // When options.json is an object or any type other than boolean, it will be used as the request body, so we need to remove it. if (opts.json !== true) { delete opts.json; } } const req = request .defaults(safeRequestDefaults) [method].apply(null, args); if (multipartFormData) { const form = req.form(); _.forOwn(multipartFormData, (value, key) => { if (Array.isArray(value)) { form.append.apply(form, [key].concat(value)); } else { form.append.apply(form, [key, value]); } }); } return req; } catch (err) { self.addon.logger.error(err); if (callback) { return callback(err); } throw err; } }; } } ["get", "post", "put", "del", "head", "patch"].forEach(method => { ForgeHostRequest.prototype[method] = ForgeHostRequest.prototype._makeVerb(method); }); module.exports = ForgeHostRequest;