@sap/approuter
Advanced tools
Comparing version 17.0.0 to 17.1.0
@@ -8,2 +8,15 @@ # Change Log | ||
## 17.1.0 - 2024-11-12 | ||
### Added | ||
- Caching support in get applications API | ||
- Max size check for cache | ||
- Use of XS_BIND_ADDRESS env var for address binding. | ||
- Support for backward compatible cookie validation | ||
### Fixed | ||
- Dynamic log level setting in managed approuter | ||
- Get tenant name correctly for ias authentication | ||
## 17.0.0 - 2024-10-28 | ||
@@ -10,0 +23,0 @@ |
@@ -178,3 +178,5 @@ 'use strict'; | ||
enableCSPheaders: loadJsonVar('ENABLE_FRAME_ANCESTORS_CSP_HEADERS'), | ||
ownSapCloudService: loadJsonVar('OWN_SAP_CLOUD_SERVICE') | ||
ownSapCloudService: loadJsonVar('OWN_SAP_CLOUD_SERVICE'), | ||
appsCacheExpiration: loadJsonVar('APPS_CACHE_EXPIRATION'), | ||
cookieBackwardCompatibility: loadJsonVar('COOKIE_BACKWARD_COMPATIBILITY') | ||
}; | ||
@@ -181,0 +183,0 @@ |
@@ -11,2 +11,3 @@ { | ||
"minimumTokenValidity": { "type": "integer", "minimum": 0 }, | ||
"appsCacheExpiration": { "type": "integer", "minimum": 0 }, | ||
"sendXFrameOptions": { "type": "boolean" }, | ||
@@ -18,2 +19,3 @@ "tenantHostPattern": { "type": "string", "minLength": 1, "format": "regexWithCapture" }, | ||
"http2Support": { "type": "boolean"}, | ||
"cookieBackwardCompatibility": { "type": "boolean" }, | ||
"directRoutingUriPatterns": {"type": "array", "items": {"type": "string", "minLength": 1}, "minItems": 1}, | ||
@@ -20,0 +22,0 @@ "ownSapCloudService": {"type": "array", "items": {"type": "string", "minLength": 1}, "minItems": 1}, |
@@ -68,7 +68,7 @@ 'use strict'; | ||
}; | ||
let locationAfterLogin = cookie.serialize(redirectCookieName, '', cookieOptions); | ||
let locationAfterLogin = cookieUtils.serialize(req, redirectCookieName, '', cookieOptions); | ||
cookieUtils.setCookie(res, cookieUtils.addAttributes(locationAfterLogin, req)); | ||
// Fragment may be empty string, which is casted to false. Therefore check presence of key. | ||
if (cookies.hasOwnProperty(fragmentCookieName)) { | ||
let fragmentAfterLogin = cookie.serialize(fragmentCookieName, '', cookieOptions); | ||
let fragmentAfterLogin = cookieUtils.serialize(req, fragmentCookieName, '', cookieOptions); | ||
cookieUtils.setCookie(res, cookieUtils.addAttributes(fragmentAfterLogin, req)); | ||
@@ -78,3 +78,3 @@ } | ||
if (cookies.hasOwnProperty('signature')) { | ||
let signature = cookie.serialize('signature', '', cookieOptions); | ||
let signature = cookieUtils.serialize(req, 'signature', '', cookieOptions); | ||
cookieUtils.setCookie(res, cookieUtils.addAttributes(signature, req)); | ||
@@ -81,0 +81,0 @@ } |
@@ -126,3 +126,3 @@ 'use strict'; | ||
if (!redirectCookie) { | ||
let locationAfterLogin = cookie.serialize(redirectCookieName, req.url, {path: '/', httpOnly: true}); | ||
let locationAfterLogin = cookieUtils.serialize(req, redirectCookieName, req.url, {path: '/', httpOnly: true}); | ||
cookieUtils.setCookie(res, locationAfterLogin); | ||
@@ -129,0 +129,0 @@ } |
'use strict'; | ||
const bsUtils = require('../utils/business-service-utils'); | ||
const logRequestInfo = require('../utils/application-logs-utils').logRequestInfo; | ||
const urlUtils = require('../utils/url-utils'); | ||
const urijs = require('urijs'); | ||
const svc2Approuter = require('./service-to-approuter-middleware'); | ||
const jwtDecode = require('jwt-decode'); | ||
const applicationLogUtils = require('../utils/application-logs-utils'); | ||
const logRequestInfo = require('../utils/application-logs-utils').logRequestInfo; | ||
const urlUtils = require('../utils/url-utils'); | ||
const urijs = require('urijs'); | ||
const svc2Approuter = require('./service-to-approuter-middleware'); | ||
const jwtDecode = require('jwt-decode'); | ||
const applicationLogUtils = require('../utils/application-logs-utils'); | ||
const passportUtils = require('../passport/utils'); | ||
const html5RepoUtils = require('../utils/html5-repo-utils'); | ||
const drUtils = require('../utils/dynamic-routing-utils'); | ||
const cookie = require('cookie'); | ||
const { promisify } = require('util'); | ||
const svc2ApprouterProm = promisify(svc2Approuter); | ||
const getDestHTML5Applications = promisify(bsUtils.getHTML5Applications); | ||
const cacheBSDestinationsProm = promisify(bsUtils.cacheBSDestinations); | ||
const html5RepoUtils = require('../utils/html5-repo-utils'); | ||
let getHTML5Applications = html5RepoUtils.getHTML5Applications; // let for testing | ||
const drUtils = require('../utils/dynamic-routing-utils'); | ||
const cookie = require('cookie'); | ||
const { promisify } = require('util'); | ||
const appCacheUtils = require('../utils/app-cache-utils'); | ||
let svc2ApprouterProm = promisify(svc2Approuter); // let for testing | ||
let getDestHTML5Applications = promisify(bsUtils.getHTML5Applications); // let for testing | ||
const cacheBSDestinationsProm = promisify(bsUtils.cacheBSDestinations); | ||
const GET_APPLICATIONS_API = '/applications'; | ||
@@ -58,2 +60,23 @@ const DOWNLOAD_APPLICATIONS_API = '/applications/content'; | ||
async function getApplications(req, res) { | ||
const skipExternalCache = req.headers['x-approuter-authorization'] && req.headers['x-skip-external-cache']; | ||
const excludeHTML5RepoCreds = isHTML5RepoCredsExcluded(req); | ||
const compactResponse = req.headers['x-compact-response'] === 'true'; | ||
const queryParams = req.url.split('?')[1]; | ||
let cacheKey = queryParams ? `${req.tenant}_${queryParams}` : `${req.tenant}`; | ||
if (excludeHTML5RepoCreds) { | ||
cacheKey = cacheKey + '_excludeHTML5RepoCreds'; | ||
} else if (req.disabledDestCred) { | ||
cacheKey = cacheKey + '_excludeDestCreds'; | ||
} | ||
const cachedApps = !skipExternalCache && await appCacheUtils.getAppsCache(cacheKey, req); | ||
if (cachedApps) { | ||
res && res.setHeader('Content-Type', 'application/json'); | ||
res && res.setHeader('x-app-cache', 'hit'); | ||
let enrichedApps; | ||
if (!compactResponse) { | ||
enrichedApps = await html5RepoUtils.enrichWithHTML5RepoData(cachedApps, queryParams, req); | ||
} | ||
return res ? res.end(JSON.stringify(enrichedApps || cachedApps), null, 4) : null; | ||
} | ||
const html5Applications = !req.disabledDestCred && await getDestHTML5Applications(req); | ||
@@ -63,4 +86,4 @@ let html5ApplicationsRepo = { | ||
}; | ||
if (!isHTML5RepoCredsExcluded(req)) { | ||
html5ApplicationsRepo = await html5RepoUtils.getHTML5Applications(req); | ||
if (!excludeHTML5RepoCreds) { | ||
html5ApplicationsRepo = await getHTML5Applications(req); | ||
} | ||
@@ -70,4 +93,31 @@ if (html5ApplicationsRepo.applications.length > 0 && html5Applications){ | ||
} | ||
res.setHeader('Content-Type', 'application/json'); | ||
res.end(JSON.stringify((html5Applications || html5ApplicationsRepo), null, 4)); | ||
const apps = html5Applications || html5ApplicationsRepo; | ||
const compactApps = {}; | ||
if (apps && apps.applications) { | ||
apps.applications = apps.applications.filter(app => (app.applicationType !== html5RepoUtils.technicalCacheBusterApplication)); | ||
if (excludeHTML5RepoCreds) { | ||
apps.applications = apps.applications.filter(app => (app.destinationId || app.destinationName)); | ||
} | ||
apps.applications.forEach(app => { | ||
if (!compactApps[app.appHostId]) { | ||
compactApps[app.appHostId] = { | ||
destId: app.destinationId, | ||
destName: app.destinationName, | ||
sapCloudService: app.sapCloudService, | ||
appIds: [], | ||
}; | ||
app.subscribedAppName && (compactApps[app.appHostId].subscribedAppName = app.subscribedAppName); | ||
app.subscribedCommercialAppName && (compactApps[app.appHostId].subscribedCommercialAppName = app.subscribedCommercialAppName); | ||
} | ||
compactApps[app.appHostId].appIds.push(app.applicationId); | ||
}); | ||
} | ||
await appCacheUtils.setAppsCache(cacheKey, compactApps, req); | ||
res && res.setHeader('Content-Type', 'application/json'); | ||
res && res.setHeader('x-app-cache', 'miss'); | ||
if (compactResponse) { | ||
res && res.end(JSON.stringify((compactApps), null, 4)); | ||
} else { | ||
res && res.end(JSON.stringify((apps), null, 4)); | ||
} | ||
} | ||
@@ -74,0 +124,0 @@ |
@@ -5,4 +5,5 @@ 'use strict'; | ||
const cookieModule = require('cookie'); | ||
const cookieUtils = require('../utils/cookie-utils'); | ||
function serializeCookies(cookies, sessionCookieName) { | ||
function serializeCookies(req, cookies, sessionCookieName) { | ||
let cookieNames = Object.keys(cookies); | ||
@@ -16,3 +17,3 @@ let filteredCookies = cookieNames.filter(function (name) { | ||
return filteredCookies.map(function (name) { | ||
return cookieModule.serialize(name, cookies[name], { encode: same }); | ||
return cookieUtils.serialize(req, name, cookies[name], { encode: same }); | ||
}).join('; '); | ||
@@ -36,3 +37,3 @@ } | ||
let cookiesMap = cookieModule.parse(req.headers.cookie, { decode: same }); | ||
req.headers.cookie = serializeCookies(cookiesMap, sessionCookieName); | ||
req.headers.cookie = serializeCookies(req, cookiesMap, sessionCookieName); | ||
if (req.headers.cookie === null) { | ||
@@ -39,0 +40,0 @@ delete req.headers.cookie; |
@@ -62,3 +62,3 @@ 'use strict'; | ||
scopes: scopes, | ||
tenant: token.ext_attr && token.ext_attr.zdn, | ||
tenant: (token.ext_attr && token.ext_attr.zdn) || options.urlTenant, | ||
urlTenant: options.urlTenant, | ||
@@ -65,0 +65,0 @@ xsuaaToken: options.xsuaaToken |
@@ -30,2 +30,3 @@ 'use strict'; | ||
let server; | ||
let xsBindAddress = process.env.XS_BIND_ADDRESS; | ||
if (routerConfig.http2Support){ | ||
@@ -50,9 +51,12 @@ server = http2.createServer(app); | ||
wsServer.listen(server); | ||
server.on('error', callback); | ||
const listenArgs = [routerConfig.serverPort]; | ||
if (xsBindAddress) { | ||
listenArgs.push(xsBindAddress); | ||
} | ||
server.on('error', callback); | ||
server.listen(routerConfig.serverPort, function () { | ||
app.logger.info('Application router is listening on port: ' + | ||
server.address().port); | ||
server.listen(...listenArgs, () => { | ||
app.logger.info('Application router is listening on port: ' + server.address().port); | ||
callback(undefined, new Server(server, wsServer)); | ||
}); | ||
}; | ||
}; |
@@ -96,3 +96,3 @@ /* eslint-disable camelcase */ | ||
if (dynamicLogLevel){ | ||
req.logger.setDynamicLoggingLevel(dynamicLogLevel); | ||
req.logger.setLoggingLevel(dynamicLogLevel); | ||
} | ||
@@ -99,0 +99,0 @@ } |
@@ -9,5 +9,8 @@ 'use strict'; | ||
const iasUtils = require('./ias-utils'); | ||
const cookie = require('cookie'); | ||
const SESSION_SECRET_LENGTH = 64; | ||
let __toString = Object.prototype.toString; | ||
exports.generateSessionSecret = function () { | ||
@@ -191,2 +194,138 @@ return crypto.randomBytes(SESSION_SECRET_LENGTH).toString('hex'); | ||
exports.serialize = function (req, name, val, options) { | ||
if (req && req.routerConfig && req.routerConfig.cookieBackwardCompatibility) { | ||
return exports.internalSerialize(name, val, options); | ||
} | ||
else { | ||
return cookie.serialize(name, val, options); | ||
} | ||
}; | ||
function encode (val) { | ||
return encodeURIComponent(val); | ||
} | ||
function isDate (val) { | ||
return __toString.call(val) === '[object Date]' || | ||
val instanceof Date; | ||
} | ||
exports.internalSerialize = function (name, val, options) { | ||
// eslint-disable-next-line no-control-regex | ||
const fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/; | ||
let opt = options || {}; | ||
let enc = opt.encode || encode; | ||
if (typeof enc !== 'function') { | ||
throw new TypeError('option encode is invalid'); | ||
} | ||
if (!fieldContentRegExp.test(name)) { | ||
throw new TypeError('argument name is invalid'); | ||
} | ||
let value = enc(val); | ||
if (value && !fieldContentRegExp.test(value)) { | ||
throw new TypeError('argument val is invalid'); | ||
} | ||
let str = name + '=' + value; | ||
// eslint-disable-next-line eqeqeq | ||
if (null != opt.maxAge) { | ||
let maxAge = opt.maxAge - 0; | ||
if (isNaN(maxAge) || !isFinite(maxAge)) { | ||
throw new TypeError('option maxAge is invalid'); | ||
} | ||
str += '; Max-Age=' + Math.floor(maxAge); | ||
} | ||
if (opt.domain) { | ||
if (!fieldContentRegExp.test(opt.domain)) { | ||
throw new TypeError('option domain is invalid'); | ||
} | ||
str += '; Domain=' + opt.domain; | ||
} | ||
if (opt.path) { | ||
if (!fieldContentRegExp.test(opt.path)) { | ||
throw new TypeError('option path is invalid'); | ||
} | ||
str += '; Path=' + opt.path; | ||
} | ||
if (opt.expires) { | ||
let expires = opt.expires; | ||
if (!isDate(expires) || isNaN(expires.valueOf())) { | ||
throw new TypeError('option expires is invalid'); | ||
} | ||
str += '; Expires=' + expires.toUTCString(); | ||
} | ||
if (opt.httpOnly) { | ||
str += '; HttpOnly'; | ||
} | ||
if (opt.secure) { | ||
str += '; Secure'; | ||
} | ||
if (opt.partitioned) { | ||
str += '; Partitioned'; | ||
} | ||
if (opt.priority) { | ||
let priority = typeof opt.priority === 'string' | ||
? opt.priority.toLowerCase() | ||
: opt.priority; | ||
switch (priority) { | ||
case 'low': | ||
str += '; Priority=Low'; | ||
break; | ||
case 'medium': | ||
str += '; Priority=Medium'; | ||
break; | ||
case 'high': | ||
str += '; Priority=High'; | ||
break; | ||
default: | ||
throw new TypeError('option priority is invalid'); | ||
} | ||
} | ||
if (opt.sameSite) { | ||
let sameSite = typeof opt.sameSite === 'string' | ||
? opt.sameSite.toLowerCase() : opt.sameSite; | ||
switch (sameSite) { | ||
case true: | ||
str += '; SameSite=Strict'; | ||
break; | ||
case 'lax': | ||
str += '; SameSite=Lax'; | ||
break; | ||
case 'strict': | ||
str += '; SameSite=Strict'; | ||
break; | ||
case 'none': | ||
str += '; SameSite=None'; | ||
break; | ||
default: | ||
throw new TypeError('option sameSite is invalid'); | ||
} | ||
} | ||
return str; | ||
}; | ||
function encrypt(value, options, cb) { | ||
@@ -193,0 +332,0 @@ if (!options || (!options.clientsecret && !options.clientid)) { |
@@ -11,2 +11,3 @@ 'use strict'; | ||
const html5AppsCache = new NodeCache({stdTTL: stdTTL, checkperiod: checkPeriod}); | ||
const enrichedHTML5AppsCache = new NodeCache({stdTTL: stdTTL, checkperiod: checkPeriod}); | ||
const logUtil = require('./application-logs-utils'); | ||
@@ -26,2 +27,4 @@ const { promisify } = require('util'); | ||
module.exports.getHTML5RepoToken = getHTML5RepoToken; | ||
module.exports.enrichWithHTML5RepoData = enrichWithHTML5RepoData; | ||
module.exports.getApplicationsMetadata = getApplicationsMetadata; | ||
module.exports.html5AppsCache = html5AppsCache; | ||
@@ -45,18 +48,9 @@ | ||
// Get applications metadata | ||
let requestOptions = await _getHTML5RepoOptions(req, `/applications/metadata/${queries}`); | ||
logUtil.logRequestInfo(req,`Fetching HTML5 applications metadata for tenant ${req.tenant}`); | ||
let result = await request.axiosRequest('get',requestOptions); | ||
if (result.error){ | ||
throw new Error('Failed to fetch applications metadata ' + result.error | ||
+ (result.response && result.response.statusCode ? `response status ${result.response.statusCode}` : '')); | ||
} | ||
const applicationsMetadata = result.body && JSON.parse(result.body); | ||
let appsFound = applicationsMetadata && applicationsMetadata.length > 0 ? applicationsMetadata.length : 0; | ||
logUtil.logRequestInfo(req,`${appsFound} HTML5 applications retrieved for tenant ${req.tenant}`); | ||
appsFound > 0 && _addHTML5ApplicationsToCache(applicationsMetadata, req); | ||
const applicationsMetadata = await exports.getApplicationsMetadata(req, queries); | ||
applicationsMetadata && applicationsMetadata.length > 0 && _addHTML5ApplicationsToCache(applicationsMetadata, req); | ||
// Get subscribed applications metadata | ||
requestOptions = await _getHTML5RepoOptions(req, '/applications/subscriptions'); | ||
const requestOptions = await _getHTML5RepoOptions(req, '/applications/subscriptions'); | ||
logUtil.logRequestInfo(req,`Fetching subscribed HTML5 applications metadata for tenant ${req.tenant}`); | ||
result = await request.axiosRequest('get',requestOptions); | ||
const result = await request.axiosRequest('get',requestOptions); | ||
if (result.error){ | ||
@@ -67,3 +61,3 @@ throw new Error('Failed to fetch applications metadata ' + result.error | ||
const subscribedApplications = result.body && JSON.parse(result.body); | ||
appsFound = subscribedApplications && subscribedApplications.length > 0 ? subscribedApplications.length : 0; | ||
const appsFound = subscribedApplications && subscribedApplications.length > 0 ? subscribedApplications.length : 0; | ||
logUtil.logRequestInfo(req,`${appsFound} subscribed HTML5 applications retrieved for tenant ${req.tenant}`); | ||
@@ -90,2 +84,3 @@ appsFound > 0 && _addHTML5ApplicationsToCache(subscribedApplications, req, true); | ||
if (sapCloudServiceKeys) { | ||
const appKeys = {}; | ||
for (let sapCloudServiceKey in sapCloudServiceKeys) { | ||
@@ -95,2 +90,3 @@ sapCloudServiceKeys[sapCloudServiceKey].applications.forEach((app) => { | ||
const baseAppKey = serviceKeyComponents.serviceName + '.' + app.applicationName + '-' + app.applicationVersion; | ||
const uniqueAppKey = app.appHostId + '.' + serviceKeyComponents.serviceName + '.' + app.applicationName + '-' + app.applicationVersion; | ||
app['url'] = approuterHost + '/' + baseAppKey; | ||
@@ -100,3 +96,6 @@ app['uniqueUrl'] = approuterHost + '/' + app.appHostId + '.' + baseAppKey; | ||
app['businessServices'] = _getBoundBusinessServices(sapCloudServiceKeys[sapCloudServiceKey].credentials); | ||
result.applications.push(app); | ||
if (!appKeys[uniqueAppKey]) { | ||
appKeys[uniqueAppKey] = uniqueAppKey; | ||
result.applications.push(app); | ||
} | ||
}); | ||
@@ -108,2 +107,81 @@ } | ||
async function enrichWithHTML5RepoData(cachedApps,queryParams,req) { | ||
const cacheKey = queryParams ? `${req.tenant}_?${queryParams}` : `${req.tenant}`; | ||
let cachedResult = enrichedHTML5AppsCache.get(cacheKey); | ||
if (cachedResult) { | ||
return cachedResult; | ||
} | ||
const runtimeHost = req.HTML5AppHost.indexOf('https://') !== 0 ? 'https://' + req.HTML5AppHost + '/' : req.HTML5AppHost + '/'; | ||
const result = { | ||
applications: [], | ||
errors: [] | ||
}; | ||
// Get local apps | ||
const appsMetadata = await exports.getApplicationsMetadata(req, queryParams, null); | ||
appsMetadata.forEach(app => { | ||
_addAppToResult(app, result, cachedApps, runtimeHost); | ||
}); | ||
// Try to get business services apps pointed by destinations, and subscribed apps | ||
const fetchApplicationsPromises = []; | ||
for (let appHostId in cachedApps) { | ||
if (!cachedApps[appHostId].appFound) { | ||
fetchApplicationsPromises.push(new Promise((resolve) => { | ||
resolve(exports.getApplicationsMetadata(req, queryParams, appHostId)); | ||
})); | ||
} | ||
} | ||
const html5RepoResult = await Promise.all(fetchApplicationsPromises); | ||
const bsappsMetadata = html5RepoResult.flat(); | ||
bsappsMetadata.forEach(app => { | ||
_addAppToResult(app, result, cachedApps, runtimeHost); | ||
}); | ||
// Check for missing apps | ||
for (let appHostId in cachedApps) { | ||
const cachedAppElement = cachedApps[appHostId]; | ||
if (!cachedAppElement.appFound) { | ||
const destinationIdentifier = cachedAppElement.destId || cachedAppElement.destName; | ||
if (destinationIdentifier) { | ||
result.errors.push(`Destination ${destinationIdentifier} with app-host ID ${appHostId} is not mapped to any application, | ||
this app-host might not exist in html5 apps repo or no application was uploaded to it. Check in BTP cockpit if an html5-apps-repo instance with plan app-host and this ID exists, | ||
if not, delete the destination. If it exists check your mta and try to re-deploy the applications`); | ||
} else { | ||
result.errors.push(`No applications found for appHostId ${appHostId}, this app-host might not exist in html5 apps repo or no application was uploaded to it.`); | ||
} | ||
} | ||
} | ||
enrichedHTML5AppsCache.set(cacheKey, result); | ||
return result; | ||
} | ||
function _addAppToResult(app, result, cachedApps, runtimeHost) { | ||
const cachedAppElement = cachedApps[app.appHostId]; | ||
// Managed Approuter needs a runtime url with sap.cloud.service, it may come from manifest.json or destination | ||
if (cachedAppElement && (app.sapCloudService || cachedAppElement.destId || cachedAppElement.destName)) { | ||
const credentials = app.configuration && app.configuration[CONFIG_CREDENTIALS]; | ||
const html5RuntimeEnabled = app.configuration && app.configuration['HTML5Runtime_enabled']; | ||
const embeddedCredsApp = !!((credentials && (credentials.xsuaa || credentials.identity)) || html5RuntimeEnabled); | ||
app.destinationName = cachedAppElement.destName; | ||
app.destinationId = cachedAppElement.destId; | ||
app.subscribedAppName = cachedAppElement.subscribedAppName; | ||
app.subscribedCommercialAppName = cachedAppElement.subscribedCommercialAppName; | ||
app.identityZone = app.subdomain; | ||
app['app-host-id'] = app.appHostId; | ||
const sapCloudService = cachedAppElement.sapCloudService || app.sapCloudService; | ||
app['sap.cloud.service'] = sapCloudService; | ||
const sapCloudServiceNoDots = sapCloudService && sapCloudService.replace(/\./g, ''); | ||
app.destinations = app.configuration && app.configuration.destinations; | ||
app.businessServices = credentials && _getBoundBusinessServices(credentials); | ||
const destIdSection = app.destinationId ? app.destinationId + '.' : ''; | ||
app.url = `${runtimeHost}${destIdSection}${sapCloudServiceNoDots}.${app.applicationName}-${app.applicationVersion}`; | ||
app.uniqueUrl = embeddedCredsApp && `${runtimeHost}${app.appHostId}.${sapCloudServiceNoDots}.${app.applicationName}-${app.applicationVersion}`; | ||
delete app.configuration; | ||
embeddedCredsApp && (app.configuration = true); | ||
cachedApps[app.appHostId].appFound = true; | ||
result.applications.push(app); | ||
} | ||
} | ||
async function downloadHTML5Application(req, res){ | ||
@@ -215,4 +293,2 @@ const dynamicRoutingUtils = require('./dynamic-routing-utils'); | ||
function _addHTML5ApplicationsToCache(applicationsMetadata, req, addApps) { | ||
@@ -328,1 +404,16 @@ const tenant = req.tenant; | ||
} | ||
async function getApplicationsMetadata(req, queries, appHostId) { | ||
const requestOptions = await _getHTML5RepoOptions(req, `/applications/metadata/${queries}`, appHostId); | ||
logUtil.logRequestInfo(req,`Fetching HTML5 applications metadata for tenant ${req.tenant}`); | ||
const result = await request.axiosRequest('get',requestOptions); | ||
const responseStatus = (result.response && result.response.statusCode) || 'unknown'; | ||
if (result.error || (responseStatus !== 200 && responseStatus !== 404)){ | ||
const message = result.error || (result.response && result.response.message) || 'No applications metadata response body'; | ||
throw new Error(`Failed to fetch applications metadata from html5 apps repo ${message}, html5 apps repo response status: ${responseStatus}`); | ||
} | ||
const applicationsMetadata = result.body && JSON.parse(result.body); | ||
const appsFound = applicationsMetadata && applicationsMetadata.length > 0 ? applicationsMetadata.length : 0; | ||
logUtil.logRequestInfo(req,`${appsFound} HTML5 applications retrieved for tenant ${req.tenant}`); | ||
return applicationsMetadata; | ||
} |
@@ -14,3 +14,3 @@ 'use strict'; | ||
function addSubscribedApplications(req, html5AppsResponse, cb) { | ||
if (!process.env.RETURN_SUBSCRIBED_APPLICATIONS) { | ||
if (!process.env.RETURN_SUBSCRIBED_APPLICATIONS || process.env.RETURN_SUBSCRIBED_APPLICATIONS === 'false') { | ||
return cb(null, html5AppsResponse); | ||
@@ -17,0 +17,0 @@ } |
{ | ||
"name": "@sap/approuter", | ||
"description": "Node.js based application router", | ||
"version": "17.0.0", | ||
"version": "17.1.0", | ||
"repository": {}, | ||
@@ -6,0 +6,0 @@ "main": "approuter.js", |
Sorry, the diff of this file is too big to display
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
762961
139
12464
2447
135