@sap/html5-app-deployer
Advanced tools
Comparing version
@@ -8,2 +8,13 @@ # Change Log | ||
## 7.1.0 - 2025-07-24 | ||
### Added | ||
- xssec retry support | ||
### Updated dependencies | ||
- deps: form-data@4.0.4 | ||
- deps: xssec@4.8.0 | ||
- deps: xsenv@5.6.1 | ||
- deps: axios@1.10.0 | ||
## 7.0.2 - 2025-05-20 | ||
@@ -10,0 +21,0 @@ |
@@ -1,4 +0,3 @@ | ||
/* eslint-disable no-console */ | ||
/* eslint-disable no-undef */ | ||
/* Copyright © 2017 SAP SE or an affiliate company. All rights reserved.*/ | ||
'use strict'; | ||
@@ -14,3 +13,2 @@ | ||
const GACDHandler = require('./gacd-handler'); | ||
const xssec = require('@sap/xssec'); | ||
@@ -41,3 +39,3 @@ exports.startDeployer = startDeployer; | ||
} | ||
log.logMessage('info', 'Application Deployer started ..', {'CODE': '2000'}); | ||
log.logMessage('info', 'Application Deployer started ..', { 'CODE': '2000' }); | ||
@@ -77,3 +75,3 @@ // Get service | ||
function executeUpload(service, resourcesFolder, cb) { | ||
async function executeUpload(service, resourcesFolder, cb) { | ||
let appsZips = []; | ||
@@ -91,54 +89,56 @@ let cwd = utils.getCwd(); | ||
// Get Token, Archive and Upload | ||
xssec.requests.requestClientCredentialsToken(null, service.credentials.uaa, null, null, (err, token) => { | ||
let token; | ||
try { | ||
token = await utils.getClientCredentialsToken(null, service.credentials.uaa, 'Error getting token'); | ||
} catch (err) { | ||
return cb(err); | ||
} | ||
async.eachSeries(folderEntries, (appDirName, iterateCb) => { | ||
let appResourcesPath = path.join(resourcesPath, appDirName); | ||
utils.isZipFile(appResourcesPath).then(isZip => { | ||
if (isZip) { | ||
appsZips.push(appResourcesPath); | ||
return iterateCb(); | ||
} else { | ||
let applicationZip = path.join(buildDirectory, appDirName + '.zip'); | ||
utils.archive(appResourcesPath, buildDirectory, applicationZip, (err) => { | ||
if (err) { | ||
return iterateCb(err); | ||
} | ||
log.logMessage('info', 'Archiver has been finalized and the output file ' + appDirName + '.zip descriptor has closed', { 'code': '2001' }); | ||
appsZips.push(applicationZip); | ||
iterateCb(); | ||
}); | ||
} | ||
}).catch(err => iterateCb(err)); | ||
}, (err) => { // After finishing all entries | ||
if (err) { | ||
return cb(err); | ||
} | ||
async.eachSeries(folderEntries, (appDirName, iterateCb) => { | ||
let appResourcesPath = path.join(resourcesPath, appDirName); | ||
utils.isZipFile(appResourcesPath).then(isZip => { | ||
if (isZip) { | ||
appsZips.push(appResourcesPath); | ||
return iterateCb(); | ||
} else { | ||
let applicationZip = path.join(buildDirectory, appDirName + '.zip'); | ||
utils.archive(appResourcesPath, buildDirectory, applicationZip, (err) => { | ||
if (err) { | ||
return iterateCb(err); | ||
} | ||
log.logMessage('info', 'Archiver has been finalized and the output file ' + appDirName + '.zip descriptor has closed', {'code': '2001'}); | ||
appsZips.push(applicationZip); | ||
iterateCb(); | ||
}); | ||
} | ||
}).catch(err => iterateCb(err)); | ||
}, (err) => { // After finishing all entries | ||
if (err) { | ||
return cb(err); | ||
} | ||
let contentZipPath = path.join(buildDirectory, 'content.zip'); | ||
let gacdHandler = new GACDHandler(service, token, contentZipPath); | ||
let contentZipPath = path.join(buildDirectory, 'content.zip'); | ||
let gacdHandler = new GACDHandler(service, token, contentZipPath); | ||
if (process.env.ASYNC_UPLOAD) { | ||
utils.archiveFiles(appsZips, buildDirectory, contentZipPath, cdmFilePath) | ||
.then(() => gacdHandler.upload()) | ||
.then(() => gacdHandler.deploy()) | ||
.then(() => { | ||
gacdHandler.pollDeployStatus(); | ||
cb(); | ||
}) | ||
.catch((err) => { | ||
cb(err); | ||
}); | ||
} else { | ||
utils.upload(service, token, appsZips, cdmFilePath, function (err) { | ||
if (process.env.ASYNC_UPLOAD) { | ||
utils.archiveFiles(appsZips, buildDirectory, contentZipPath, cdmFilePath) | ||
.then(() => gacdHandler.upload()) | ||
.then(() => gacdHandler.deploy()) | ||
.then(() => { | ||
gacdHandler.pollDeployStatus(); | ||
cb(); | ||
}) | ||
.catch((err) => { | ||
cb(err); | ||
}); | ||
} | ||
}); | ||
} else { | ||
utils.upload(service, token, appsZips, cdmFilePath, function (err) { | ||
cb(err); | ||
}); | ||
} | ||
}); | ||
} | ||
function endProcess(err,destinationLogs, cb) { | ||
function endProcess(err, destinationLogs, cb) { | ||
let server; | ||
@@ -150,7 +150,7 @@ let deployId = process.env.DEPLOY_ID || 'none'; | ||
if (err) { | ||
console.log('Upload of html5 applications content failed ',err); | ||
console.log('Upload of html5 applications content failed ', err); | ||
if (err.message) { | ||
log.logMessage('error', '%s', err.message, {'CODE': '2004'}); | ||
log.logMessage('error', '%s', err.message, { 'CODE': '2004' }); | ||
} | ||
log.logMessage('error', 'Application Deployer failed', {'CODE': '2005'}); | ||
log.logMessage('error', 'Application Deployer failed', { 'CODE': '2005' }); | ||
if (shouldExitProcess) { | ||
@@ -163,3 +163,3 @@ process.exit(1); | ||
destinationLogs.forEach((log) => { | ||
if (log.iasDepAppName){ | ||
if (log.iasDepAppName) { | ||
console.log('Destination ' + log.name + ' created with ias dependency: ' + log.iasDepAppName); | ||
@@ -171,4 +171,4 @@ } else { | ||
} | ||
log.logMessage('info', 'Resources were successfully uploaded to Server', {'CODE': '2002'}); | ||
log.logMessage('info', 'Application Deployer finished ..', {'CODE': '2003'}); | ||
log.logMessage('info', 'Resources were successfully uploaded to Server', { 'CODE': '2002' }); | ||
log.logMessage('info', 'Application Deployer finished ..', { 'CODE': '2003' }); | ||
if (shouldExitProcess) { | ||
@@ -181,19 +181,18 @@ process.exit(); | ||
if (err) { | ||
log.logMessage('error', 'Deployment of html5 application content failed [Deployment Id: %s]', deployId, {'CODE': '2007'}); | ||
log.logMessage('error', 'Deployment of html5 application content failed [Deployment Id: %s]', deployId, { 'CODE': '2007' }); | ||
console.error('Deployment of html5 application content failed [Deployment Id: ' + deployId + '] ' + err); | ||
} else { | ||
log.logMessage('info', 'Deployment of html5 application content done [Deployment Id: %s]', deployId, {'CODE': '2006'}); | ||
log.logMessage('info', 'Deployment of html5 application content done [Deployment Id: %s]', deployId, { 'CODE': '2006' }); | ||
console.log('Deployment of html5 application content done [Deployment Id: ' + deployId + ']'); | ||
} | ||
if (process.env.TEST_MODE){ | ||
if (process.env.TEST_MODE) { | ||
return cb(err); | ||
} | ||
setInterval(function () { | ||
log.logMessage('info', 'Waiting for deploy service to stop the application', {'CODE': '2008'}); | ||
log.logMessage('info', 'Waiting for deploy service to stop the application', { 'CODE': '2008' }); | ||
}, 30000); | ||
} | ||
else { // Scenario of pushing with manifest | ||
} else { // Scenario of pushing with manifest | ||
if (!err) { // don't leave app started if failed | ||
// For hanging the process | ||
if (process.env.TEST_MODE){ | ||
if (process.env.TEST_MODE) { | ||
return cb(); | ||
@@ -200,0 +199,0 @@ } |
@@ -5,4 +5,4 @@ /* eslint-disable camelcase */ | ||
const request = require('./request-utils'); | ||
const xsenv = require('@sap/xsenv'); | ||
const log = require('cf-nodejs-logging-support'); | ||
const xsenv = require('@sap/xsenv'); | ||
const log = require('cf-nodejs-logging-support'); | ||
const https = require('https'); | ||
@@ -17,6 +17,6 @@ | ||
this._destinationLogs = []; | ||
if (process.env.BACKEND_DESTINATIONS){ | ||
if (process.env.BACKEND_DESTINATIONS) { | ||
try { | ||
this._backendDestinations = JSON.parse(process.env.BACKEND_DESTINATIONS); | ||
} catch (err){ | ||
} catch (err) { | ||
throw new Error('Failed to parse backend destinations ' + err); | ||
@@ -38,3 +38,3 @@ } | ||
log.logMessage('Destinations created succesfully'); | ||
cb(null,this._destinationLogs); | ||
cb(null, this._destinationLogs); | ||
}) | ||
@@ -47,7 +47,7 @@ .catch((err) => { | ||
_upsertXSUAADestination(){ | ||
_upsertXSUAADestination() { | ||
return new Promise((resolve) => { | ||
log.logMessage('Creating xsuaa destination'); | ||
let credentials = this._getCredentials('xsuaa'); | ||
if (!credentials){ | ||
if (!credentials) { | ||
log.logMessage('No XSUAA service instance bound'); | ||
@@ -58,3 +58,3 @@ return resolve(); | ||
Type: 'HTTP', | ||
Name: this._sapCloudService.replace(/\./g,'') + '-xsuaa', | ||
Name: this._sapCloudService.replace(/\./g, '') + '-xsuaa', | ||
Description: 'xsuaa destination', | ||
@@ -75,10 +75,10 @@ clientSecret: credentials.clientsecret, | ||
_upsertAppHostDestination(){ | ||
_upsertAppHostDestination() { | ||
return new Promise((resolve, reject) => { | ||
log.logMessage('Creating app-host destination'); | ||
let credentials = this._getCredentials('html5-apps-repo-dt'); | ||
if (!credentials){ | ||
if (!credentials) { | ||
credentials = this._getCredentials('html5-apps-repo'); | ||
} | ||
if (!credentials){ | ||
if (!credentials) { | ||
return reject('No html5-apps-repo/app-host service instance bound'); | ||
@@ -88,3 +88,3 @@ } | ||
Type: 'HTTP', | ||
Name: this._sapCloudService.replace(/\./g,'') + '-appHost', | ||
Name: this._sapCloudService.replace(/\./g, '') + '-appHost', | ||
Description: 'App-host destination', | ||
@@ -99,4 +99,4 @@ clientSecret: credentials.uaa.clientsecret, | ||
}; | ||
if (process.env.IAS_DEPENDENCY_NAME){ | ||
body['HTML5.IASDependencyName'] = process.env.IAS_DEPENDENCY_NAME; | ||
if (process.env.IAS_DEPENDENCY_NAME) { | ||
body[ 'HTML5.IASDependencyName' ] = process.env.IAS_DEPENDENCY_NAME; | ||
} | ||
@@ -107,3 +107,3 @@ resolve(this._upsertDestinationConfigurations(body)); | ||
_upsertBusinessServicesDestinations(){ | ||
_upsertBusinessServicesDestinations() { | ||
return new Promise((resolve) => { | ||
@@ -119,3 +119,3 @@ log.logMessage('Creating business services destination'); | ||
Type: 'HTTP', | ||
Name: this._sapCloudService.replace(/\./g,'') + `-${credentials['sap.cloud.service']}-${service.name}`, | ||
Name: this._sapCloudService.replace(/\./g, '') + `-${credentials[ 'sap.cloud.service' ]}-${service.name}`, | ||
Description: `Business service-${service.name} destination`, | ||
@@ -128,8 +128,8 @@ Authentication: 'OAuth2ClientCredentials', | ||
URL: 'https://dummy', | ||
'sap.cloud.service': credentials['sap.cloud.service'], | ||
'html5-apps-repo': JSON.stringify(credentials['html5-apps-repo']), | ||
endpoints :JSON.stringify(credentials.endpoints), | ||
'sap.cloud.service': credentials[ 'sap.cloud.service' ], | ||
'html5-apps-repo': JSON.stringify(credentials[ 'html5-apps-repo' ]), | ||
endpoints: JSON.stringify(credentials.endpoints), | ||
grant_type: credentials.grant_type | ||
}; | ||
if (credentials.saasregistryenabled){ | ||
if (credentials.saasregistryenabled) { | ||
body.saasregistryenabled = credentials.saasregistryenabled; | ||
@@ -143,6 +143,6 @@ } | ||
_upsertBackendDestinations(){ | ||
_upsertBackendDestinations() { | ||
return new Promise((resolve) => { | ||
log.logMessage('Creating backend destination'); | ||
if (!this._backendDestinations){ | ||
if (!this._backendDestinations) { | ||
log.logMessage('No backend destinations defined'); | ||
@@ -159,3 +159,3 @@ return resolve(); | ||
_upsertDestinationConfigurations(body){ | ||
_upsertDestinationConfigurations(body) { | ||
return new Promise((resolve, reject) => { | ||
@@ -168,3 +168,3 @@ let credentials = this._getCredentials('destination'); | ||
headers: { | ||
Authorization: 'Bearer ' + this._accessToken | ||
Authorization: 'Bearer ' + this._accessToken | ||
}, | ||
@@ -175,3 +175,3 @@ json: true, | ||
request.post(options, (err, res) => { | ||
if (res && res.statusCode === 409){ | ||
if (res && res.statusCode === 409) { | ||
// Destination already exists | ||
@@ -181,7 +181,7 @@ request.put(options, (err, res) => { | ||
message = 'Failed to update destination configuration with destination name ' + body.Name + | ||
res && res.body ? res.body.error + ' ' + res.body.description + ' status ' + res.statusCode : err ; | ||
res && res.body ? res.body.error + ' ' + res.body.description + ' status ' + res.statusCode : err; | ||
log.logMessage(message); | ||
return reject(message); | ||
} else { | ||
this._destinationLogs.push({name: body.Name}); | ||
this._destinationLogs.push({ name: body.Name }); | ||
return resolve(); | ||
@@ -195,12 +195,12 @@ } | ||
let bodyError = res && res.body ? ' ' + res.body.error + ' ' + res.body.description + ' status ' + res.statusCode + ' ' : ''; | ||
message = 'Failed to create destination configuration with destination name ' + body.Name + bodyError + err ; | ||
message = 'Failed to create destination configuration with destination name ' + body.Name + bodyError + err; | ||
log.logMessage(message); | ||
return reject(message); | ||
} else { | ||
if (body['HTML5.IASDependencyName']){ | ||
log.logMessage('Destination ' + body.Name + ' created with ias dependency: ' + body['HTML5.IASDependencyName']); | ||
this._destinationLogs.push({name: body.Name, iasDepAppName: body['HTML5.IASDependencyName']}); | ||
if (body[ 'HTML5.IASDependencyName' ]) { | ||
log.logMessage('Destination ' + body.Name + ' created with ias dependency: ' + body[ 'HTML5.IASDependencyName' ]); | ||
this._destinationLogs.push({ name: body.Name, iasDepAppName: body[ 'HTML5.IASDependencyName' ] }); | ||
} else { | ||
log.logMessage('Destination ' + body.Name + ' created succesfully'); | ||
this._destinationLogs.push({name: body.Name }); | ||
this._destinationLogs.push({ name: body.Name }); | ||
} | ||
@@ -213,7 +213,7 @@ resolve(); | ||
_getDestinationToken(){ | ||
_getDestinationToken() { | ||
return new Promise((resolve, reject) => { | ||
log.logMessage('Obtaining destination token'); | ||
let credentials = this._getCredentials('destination'); | ||
if (!credentials){ | ||
if (!credentials) { | ||
return reject('Failed to obtain destination credentials, check that destination service is bound'); | ||
@@ -234,3 +234,3 @@ } | ||
_getTokenRequestOptions(credentials) { | ||
const isCertificateCreds = credentials['credential-type'] === 'x509'; | ||
const isCertificateCreds = credentials[ 'credential-type' ] === 'x509'; | ||
let options = { | ||
@@ -246,3 +246,3 @@ url: `${isCertificateCreds ? credentials.certurl : credentials.url}/oauth/token/?grant_type=client_credentials`, | ||
options.data = `client_id=${credentials.clientid}`; | ||
options.httpsAgent = new https.Agent({cert: credentials.certificate, key: credentials.key}); | ||
options.httpsAgent = new https.Agent({ cert: credentials.certificate, key: credentials.key }); | ||
} else { | ||
@@ -258,11 +258,11 @@ options.auth = { | ||
_getCredentials(tag,label){ | ||
const html5Services = ['html5-apps-repo-rt' , 'html5-apps-repo-dt' , 'html5-apps-repo']; | ||
_getCredentials(tag, label) { | ||
const html5Services = [ 'html5-apps-repo-rt', 'html5-apps-repo-dt', 'html5-apps-repo' ]; | ||
let credentials; | ||
if (!tag && !label){ | ||
credentials = xsenv.filterServices(service => !!service.credentials['sap.cloud.service'] && !html5Services.includes(service.credentials['sap.cloud.service'])); | ||
} | ||
else { | ||
if (!tag && !label) { | ||
credentials = xsenv.filterServices(service => !!service.credentials[ 'sap.cloud.service' ] && !html5Services.includes(service.credentials[ 'sap.cloud.service' ])); | ||
} else { | ||
try { | ||
credentials = label ? xsenv.serviceCredentials({label: label}) : xsenv.serviceCredentials({tag: tag}); | ||
credentials = label ? xsenv.serviceCredentials({ label: label }) : xsenv.serviceCredentials({ tag: tag }); | ||
// eslint-disable-next-line no-unused-vars | ||
} catch (e) { | ||
@@ -275,2 +275,3 @@ return null; | ||
} | ||
module.exports = DestinationUtils; |
@@ -1,8 +0,7 @@ | ||
/* eslint-disable camelcase */ | ||
/* eslint-disable camelcase, no-undef */ | ||
'use strict'; | ||
const requestUtils = require('./request-utils'); | ||
const log = require('cf-nodejs-logging-support'); | ||
const log = require('cf-nodejs-logging-support'); | ||
const { v4: uuid } = require('uuid'); | ||
const fs = require('fs'); | ||
const fs = require('fs'); | ||
const FormData = require('form-data'); | ||
@@ -14,4 +13,4 @@ const xsenv = require('@sap/xsenv'); | ||
constructor(service, token, contentZipPath) { | ||
this.service = service; | ||
this.token = token; | ||
this.service = service; | ||
this.token = token; | ||
this.contentZipPath = contentZipPath; | ||
@@ -24,16 +23,15 @@ this.deployStatus = null; | ||
upload(){ | ||
log.logMessage('info','Starting upload'); | ||
return new Promise((resolve,reject) => { | ||
upload() { | ||
log.logMessage('info', 'Starting upload'); | ||
return new Promise((resolve, reject) => { | ||
let errMessage = null; | ||
log.logMessage('info','Sending rquest to upload'); | ||
log.logMessage('info', 'Sending rquest to upload'); | ||
const formData = new FormData(); | ||
formData.append('file', fs.createReadStream(this.contentZipPath), {'content-type': 'application/zip'}); | ||
requestUtils.post(this._getRequestOptions('/v2/files/upload',formData), (err, res, body) => { | ||
formData.append('file', fs.createReadStream(this.contentZipPath), { 'content-type': 'application/zip' }); | ||
requestUtils.post(this._getRequestOptions('/v2/files/upload', formData), (err, res, body) => { | ||
if (res && res.statusCode === 201) { | ||
log.logMessage('info','Upload completed'); | ||
log.logMessage('info', 'Upload completed'); | ||
this.uploadResponse = JSON.parse(body); | ||
resolve(); | ||
} | ||
else { | ||
} else { | ||
if (err) { | ||
@@ -43,5 +41,5 @@ errMessage = new Error('Error in upload request: ' + err.message); | ||
errMessage = new Error('Error while uploading resources to server; Status: ' + (res && res.statusCode) + | ||
' Response: ' + body); | ||
' Response: ' + body); | ||
} | ||
log.logMessage('error',errMessage); | ||
log.logMessage('error', errMessage); | ||
reject(errMessage); | ||
@@ -53,10 +51,10 @@ } | ||
deploy(){ | ||
deploy() { | ||
return new Promise((resolve) => { | ||
const configuration = this._getConfiguration(); | ||
const body = {contents: [{storageType: 'FILE', uri: this.uploadResponse.fileId}]}; | ||
const body = { contents: [ { storageType: 'FILE', uri: this.uploadResponse.fileId } ] }; | ||
configuration && (body.configuration = configuration); | ||
this._sendGACDRequest(this._getRequestOptions('/v2/deploys', body),'post', 201, 'deploy') | ||
.then ((deployResponse) => { | ||
log.logMessage('info','deployResponse from deploy request: ' + JSON.stringify(this.deployResponse)); | ||
this._sendGACDRequest(this._getRequestOptions('/v2/deploys', body), 'post', 201, 'deploy') | ||
.then((deployResponse) => { | ||
log.logMessage('info', 'deployResponse from deploy request: ' + JSON.stringify(this.deployResponse)); | ||
this.deployResponse = deployResponse; | ||
@@ -68,23 +66,23 @@ resolve(); | ||
pollDeployStatus(){ | ||
pollDeployStatus() { | ||
return new Promise((resolve, reject) => { | ||
let message = null; | ||
if (!this.deployResponse){ | ||
if (!this.deployResponse) { | ||
return reject('Failed to get deploy response'); | ||
} | ||
this.intervalId = setInterval(() => { | ||
log.logMessage('info','deployResponse before get deploy status: ' + JSON.stringify(this.deployResponse)); | ||
let deployResourceId = this.deployResponse.contents[0].uri; | ||
log.logMessage('info', 'deployResponse before get deploy status: ' + JSON.stringify(this.deployResponse)); | ||
let deployResourceId = this.deployResponse.contents[ 0 ].uri; | ||
this.getDeployStatus(deployResourceId) | ||
.then((result) => { | ||
log.logMessage('info','deployStatus response' + result); | ||
let deployStatus = result.contents[0].deployStatus; | ||
log.logMessage('info', 'deployStatus response' + result); | ||
let deployStatus = result.contents[ 0 ].deployStatus; | ||
log.logMessage('info', 'Deploy status: ' + deployStatus); | ||
if (deployStatus === 'SUCCESS' || deployStatus === 'WARNING'){ | ||
if (deployStatus === 'SUCCESS' || deployStatus === 'WARNING') { | ||
log.logMessage('info', 'HTML5 Applications succesfully deployed'); | ||
clearInterval(this.intervalId); | ||
return resolve(); | ||
} else if (deployStatus === 'CONTENT_ERROR' || deployStatus === 'SEVERE_ERROR'){ | ||
} else if (deployStatus === 'CONTENT_ERROR' || deployStatus === 'SEVERE_ERROR') { | ||
log.logMessage('error', 'Failed to deploy HTML5 Applications'); | ||
this.getDeployLogs(this.deployResponse.contents[0].uri) | ||
this.getDeployLogs(this.deployResponse.contents[ 0 ].uri) | ||
.then((logs) => { | ||
@@ -94,3 +92,3 @@ let message = JSON.stringify(logs, null, 4); | ||
clearInterval(this.intervalId); | ||
if (process.env.ASYNC_UPLOAD){ | ||
if (process.env.ASYNC_UPLOAD) { | ||
return resolve(); | ||
@@ -102,3 +100,3 @@ } else { | ||
.catch((err) => { | ||
message = 'Failed to get deploy logs for resource ' + this.deployResponse.contents[0].uri + ' ' + err; | ||
message = 'Failed to get deploy logs for resource ' + this.deployResponse.contents[ 0 ].uri + ' ' + err; | ||
log.logMessage(message); | ||
@@ -112,3 +110,3 @@ clearInterval(this.intervalId); | ||
.catch((err) => { | ||
message = 'Failed to get deploy status for resource ' + this.deployResponse.contents[0].uri + ' ' + err; | ||
message = 'Failed to get deploy status for resource ' + this.deployResponse.contents[ 0 ].uri + ' ' + err; | ||
log.logMessage(message); | ||
@@ -122,24 +120,24 @@ clearInterval(this.intervalId); | ||
getDeployLogs(deployResourceId){ | ||
getDeployLogs(deployResourceId) { | ||
return new Promise((resolve) => { | ||
resolve(this._sendGACDRequest(this._getRequestOptions('/v2/deploys/:' + deployResourceId + | ||
'/logs', null),'get', 200, 'get deploy logs')); | ||
'/logs', null), 'get', 200, 'get deploy logs')); | ||
}); | ||
} | ||
getDeployStatus(deployResourceId){ | ||
getDeployStatus(deployResourceId) { | ||
return new Promise((resolve) => { | ||
resolve(this._sendGACDRequest(this._getRequestOptions('/v2/deploys/' + | ||
deployResourceId, null), 'get', 200, 'get deploy status')); | ||
deployResourceId, null), 'get', 200, 'get deploy status')); | ||
}); | ||
} | ||
_sendGACDRequest(options, method, expectedStatus, api){ | ||
return new Promise((resolve,reject) => { | ||
_sendGACDRequest(options, method, expectedStatus, api) { | ||
return new Promise((resolve, reject) => { | ||
let errMessage = null; | ||
log.logMessage('info','Sending request to ' + api); | ||
log.logMessage('info', 'Sending request to ' + api); | ||
options.method = method; | ||
requestUtils.sendRequest(options, (err, res, body) => { | ||
if (res && res.statusCode === expectedStatus) { | ||
log.logMessage('info','Sending request to ' + api + ' completed succesfully'); | ||
log.logMessage('info', 'Sending request to ' + api + ' completed succesfully'); | ||
resolve(JSON.parse(body)); | ||
@@ -151,5 +149,5 @@ } else { | ||
errMessage = 'Error in request to ' + api + ' Status: ' + (res && res.statusCode) + | ||
' Response: ' + body; | ||
' Response: ' + body; | ||
} | ||
log.logMessage('error',errMessage); | ||
log.logMessage('error', errMessage); | ||
reject(errMessage); | ||
@@ -160,3 +158,4 @@ } | ||
} | ||
_getRequestOptions(path,body) { | ||
_getRequestOptions(path, body) { | ||
let corrID = uuid(); | ||
@@ -173,3 +172,3 @@ let requestOptions = { | ||
requestOptions.maxContentLength = Infinity; | ||
requestOptions.maxBodyLength = Infinity; | ||
requestOptions.maxBodyLength = Infinity; | ||
} | ||
@@ -184,17 +183,17 @@ return requestOptions; | ||
configuration = { | ||
['com.sap.service-credentials']: {} | ||
[ 'com.sap.service-credentials' ]: {} | ||
}; | ||
for (let service in services) { | ||
const serviceName = services[service].label || service; | ||
log.logMessage('info',`Adding credentials for service ${serviceName}`); | ||
configuration['com.sap.service-credentials'][serviceName] = [services[service]]; | ||
const serviceName = services[ service ].label || service; | ||
log.logMessage('info', `Adding credentials for service ${serviceName}`); | ||
configuration[ 'com.sap.service-credentials' ][ serviceName ] = [ services[ service ] ]; | ||
} | ||
} | ||
const destinations = process.env.BACKEND_DESTINATIONS || process.env.destinations; | ||
if (destinations){ | ||
if (destinations) { | ||
try { | ||
!configuration && (configuration = {}); | ||
configuration.destinations = JSON.parse(destinations); | ||
log.logMessage('info','Adding destinations configuration'); | ||
} catch (err){ | ||
log.logMessage('info', 'Adding destinations configuration'); | ||
} catch (err) { | ||
throw new Error('Failed to parse backend destinations ' + err); | ||
@@ -204,12 +203,12 @@ } | ||
const IASDependencyName = process.env.IAS_DEPENDENCY_NAME; | ||
if (IASDependencyName){ | ||
if (IASDependencyName) { | ||
!configuration && (configuration = {}); | ||
configuration.IASDependencyName = IASDependencyName; | ||
log.logMessage('info',`Adding IAS dependency name ${IASDependencyName}`); | ||
log.logMessage('info', `Adding IAS dependency name ${IASDependencyName}`); | ||
} | ||
const HTML5Runtime_enabled = process.env.HTML5Runtime_enabled; | ||
if (HTML5Runtime_enabled && HTML5Runtime_enabled === 'true'){ | ||
if (HTML5Runtime_enabled && HTML5Runtime_enabled === 'true') { | ||
!configuration && (configuration = {}); | ||
configuration.HTML5Runtime_enabled = true; | ||
log.logMessage('info','Adding HTML5Runtime_enabled configuration'); | ||
log.logMessage('info', 'Adding HTML5Runtime_enabled configuration'); | ||
} | ||
@@ -216,0 +215,0 @@ return configuration; |
'use strict'; | ||
const axios = require('axios'); | ||
const axios = require('axios'); | ||
const FormData = require('form-data'); | ||
// convert request options to 'axios' format | ||
const axiosRequestOptions = (method , request) => { | ||
const axiosRequestOptions = (method, request) => { | ||
const clonedRequestOpt = Object.assign({}, request); | ||
clonedRequestOpt.method = method; | ||
clonedRequestOpt.transformResponse = function (res){ // prevent JSON.parse | ||
clonedRequestOpt.transformResponse = function (res) { // prevent JSON.parse | ||
return res; | ||
}; | ||
if (clonedRequestOpt.body){ | ||
if (clonedRequestOpt.body instanceof FormData){ | ||
if (clonedRequestOpt.body) { | ||
if (clonedRequestOpt.body instanceof FormData) { | ||
clonedRequestOpt.headers = { | ||
@@ -27,3 +27,3 @@ ...clonedRequestOpt.headers, | ||
delete clonedRequestOpt.auth.user; | ||
delete clonedRequestOpt.auth.pass; | ||
delete clonedRequestOpt.auth.pass; | ||
} | ||
@@ -34,19 +34,19 @@ return clonedRequestOpt; | ||
const get = (requestOptions, callback) => { | ||
(async () => { | ||
const {error , response , body} = await axiosRequest('get',requestOptions); | ||
return callback(error ,response , body); | ||
(async() => { | ||
const { error, response, body } = await axiosRequest('get', requestOptions); | ||
return callback(error, response, body); | ||
})(); | ||
}; | ||
const post = (requestOptions,callback) => { | ||
(async () => { | ||
const {error , response , body} = await axiosRequest('post',requestOptions); | ||
return callback(error ,response , body); | ||
const post = (requestOptions, callback) => { | ||
(async() => { | ||
const { error, response, body } = await axiosRequest('post', requestOptions); | ||
return callback(error, response, body); | ||
})(); | ||
}; | ||
const put = (requestOptions,callback) => { | ||
(async () => { | ||
const {error , response , body} = await axiosRequest('put',requestOptions); | ||
return callback(error ,response , body); | ||
const put = (requestOptions, callback) => { | ||
(async() => { | ||
const { error, response, body } = await axiosRequest('put', requestOptions); | ||
return callback(error, response, body); | ||
})(); | ||
@@ -56,4 +56,4 @@ }; | ||
const sendRequest = (options, callback) => { | ||
(async () => { | ||
const {error, response, body} = await axiosRequest(options.method, options); | ||
(async() => { | ||
const { error, response, body } = await axiosRequest(options.method, options); | ||
return callback(error, response, body); | ||
@@ -63,3 +63,3 @@ })(); | ||
const axiosRequest = async (method, requestOptions) => { | ||
const axiosRequest = async(method, requestOptions) => { | ||
let error, response, body; | ||
@@ -71,3 +71,3 @@ const axiosRequestOpt = axiosRequestOptions(method, requestOptions); | ||
body = response.body; | ||
} catch (err){ | ||
} catch (err) { | ||
error = err; | ||
@@ -86,3 +86,3 @@ if (err.response) { | ||
response.statusCode = response.status; | ||
return {error, response, body}; | ||
return { error, response, body }; | ||
}; | ||
@@ -89,0 +89,0 @@ |
@@ -1,6 +0,4 @@ | ||
/* eslint-disable no-console */ | ||
/* eslint-disable no-undef */ | ||
/* Copyright © 2017 SAP SE or an affiliate company. All rights reserved.*/ | ||
'use strict'; | ||
const fs = require('fs'); | ||
@@ -14,2 +12,5 @@ const archiver = require('archiver'); | ||
const FormData = require('form-data'); | ||
const { XsuaaService } = require('@sap/xssec'); | ||
const logger = require('cf-nodejs-logging-support'); | ||
let requestsRetryConfig = null; | ||
@@ -19,2 +20,3 @@ exports.getDTRequestOptions = getDTRequestOptions; | ||
exports.isZipFile = isZipFile; | ||
exports.getClientCredentialsToken = getClientCredentialsToken; | ||
exports.CDM_JSON = 'cdm.json'; | ||
@@ -34,3 +36,3 @@ | ||
} | ||
return services[html5AppsRepoServiceName]; | ||
return services[ html5AppsRepoServiceName ]; | ||
}; | ||
@@ -57,6 +59,7 @@ | ||
await fs.promises.access(resourcesPath); | ||
// eslint-disable-next-line no-unused-vars | ||
} catch (error) { | ||
throw new Error(`The resources folder ${resourcesFolder} does not exist or is empty.`); | ||
} | ||
// eslint-disable-next-line no-useless-catch | ||
try { | ||
@@ -74,2 +77,3 @@ const stats = await fs.promises.lstat(resourcesPath); | ||
folderEntries = await fs.promises.readdir(resourcesPath); | ||
// eslint-disable-next-line no-unused-vars | ||
} catch (error) { | ||
@@ -122,7 +126,7 @@ throw new Error(`Unable to read the resources folder ${resourcesFolder}.`); | ||
let parts = appZip.split('/'); | ||
let fileName = parts[parts.length - 1]; | ||
archive.append(fs.createReadStream(appZip), {name: fileName}); | ||
let fileName = parts[ parts.length - 1 ]; | ||
archive.append(fs.createReadStream(appZip), { name: fileName }); | ||
}); | ||
if (cdmFilePath && fs.existsSync(cdmFilePath) && fs.lstatSync(cdmFilePath).isFile()) { | ||
archive.append(fs.createReadStream(cdmFilePath), {name: exports.CDM_JSON}); | ||
archive.append(fs.createReadStream(cdmFilePath), { name: exports.CDM_JSON }); | ||
} | ||
@@ -159,3 +163,3 @@ archive.finalize(); | ||
appsZips.map(function (zipFile) { | ||
formData.append('apps',fs.createReadStream(zipFile)); | ||
formData.append('apps', fs.createReadStream(zipFile)); | ||
}); | ||
@@ -167,3 +171,3 @@ if (fs.existsSync(cdmFile)) { | ||
requestOptions.maxContentLength = Infinity; | ||
requestOptions.maxBodyLength = Infinity; | ||
requestOptions.maxBodyLength = Infinity; | ||
request.put(requestOptions, function onResponse(err, res, body) { | ||
@@ -191,3 +195,3 @@ if (res && res.statusCode === 201) { | ||
for (let service in services) { | ||
if (services[service].label && services[service].label === 'application-logs') { | ||
if (services[ service ].label && services[ service ].label === 'application-logs') { | ||
applicationLogExist = true; | ||
@@ -223,6 +227,6 @@ break; | ||
for (let service in services) { | ||
if (services[service].tags) { | ||
services[service].tags.forEach(function (tag) { | ||
if (services[ service ].tags) { | ||
services[ service ].tags.forEach(function (tag) { | ||
if (tag === 'html5-apps-repo-dt' || tag === 'html5-apps-repo') { | ||
serviceNames[service] = service; | ||
serviceNames[ service ] = service; | ||
} | ||
@@ -242,3 +246,3 @@ }); | ||
if (namesLenght === 1) { | ||
return Object.keys(serviceNames)[0]; | ||
return Object.keys(serviceNames)[ 0 ]; | ||
} | ||
@@ -249,3 +253,3 @@ } | ||
const { fileTypeFromBuffer } = await import('file-type'); | ||
const { readChunk } = await import('read-chunk'); | ||
const { readChunk } = await import('read-chunk'); | ||
const fsModule = await import('fs'); // Corrected import statement | ||
@@ -270,1 +274,39 @@ const fsp = fsModule.promises; | ||
async function getClientCredentialsToken(token, credentials, errMsg) { | ||
try { | ||
const xsuaaService = new XsuaaService({ | ||
clientid: credentials.clientid, | ||
clientsecret: credentials.clientsecret, | ||
url: credentials.url | ||
}, { | ||
requests: { retry: _getXsuaaServiceRetryConfig() } | ||
}); | ||
const tokenResponse = await xsuaaService.fetchClientCredentialsToken(); | ||
return (tokenResponse.access_token || tokenResponse.token); | ||
} catch (err) { | ||
log.logMessage('error', '${errMsg}: ${err.message ? err.message : err}'); | ||
throw new Error(`${errMsg}: ${err.message ? err.message : err}`); | ||
} | ||
} | ||
function _getXsuaaServiceRetryConfig() { | ||
if (requestsRetryConfig !== null) { | ||
return requestsRetryConfig; | ||
} | ||
let xsuaaServiceRetryConfig = {}; | ||
try { | ||
if (process.env.CREDENTIALS_TOKEN_RETRY_CONFIG) { | ||
xsuaaServiceRetryConfig = JSON.parse(process.env.CREDENTIALS_TOKEN_RETRY_CONFIG); | ||
} | ||
} catch (e) { | ||
logger.error(`Failed to parse CREDENTIALS_TOKEN_RETRY_CONFIG, Using default config : ${e.message ? e.message : e}`); | ||
} | ||
requestsRetryConfig = { | ||
'strategy': xsuaaServiceRetryConfig.strategy || 'exponential', // The retry strategy (currently only 'exponential' is supported) | ||
'retries': xsuaaServiceRetryConfig.retries || 10, // Maximum number of retry attempts | ||
'initialDelay': xsuaaServiceRetryConfig.initialDelay || 100, // Initial delay in milliseconds before the first retry | ||
'factor': xsuaaServiceRetryConfig.factor || 2, // Multiplier for the delay after each retry | ||
'maxDelay': xsuaaServiceRetryConfig.maxDelay || 10000 // Maximum delay in milliseconds between retries | ||
}; | ||
return requestsRetryConfig; | ||
} |
@@ -6,15 +6,15 @@ { | ||
}, | ||
"version": "7.0.2", | ||
"version": "7.1.0", | ||
"description": "HTML5 application deployer", | ||
"main": "index.js", | ||
"dependencies": { | ||
"@sap/xsenv": "5.4.0", | ||
"@sap/xssec": "3.6.1", | ||
"@sap/xsenv": "5.6.1", | ||
"@sap/xssec": "4.8.0", | ||
"archiver": "5.3.2", | ||
"async": "3.2.3", | ||
"axios": "1.8.3", | ||
"async": "3.2.6", | ||
"axios": "1.10.0", | ||
"cf-nodejs-logging-support": "7.4.0", | ||
"file-type": "19.6.0", | ||
"read-chunk": "5.0.0", | ||
"form-data": "4.0.0", | ||
"form-data": "4.0.4", | ||
"uuid": "8.3.2" | ||
@@ -24,15 +24,16 @@ }, | ||
"chai": "3.5.0", | ||
"mocha": "8.3.0", | ||
"eslint": "3.2.2", | ||
"eslint": "9.31.0", | ||
"@eslint/js": "^9.28.0", | ||
"mocha": "8.4.0", | ||
"connect": "3.7.0", | ||
"multer": "1.3.0", | ||
"test-console": "1.1.0", | ||
"express": "4.20.0", | ||
"sinon": "4.2.2", | ||
"supertest": "3.0.0", | ||
"express": "4.21.2", | ||
"sinon": "4.5.0", | ||
"supertest": "3.4.2", | ||
"gulp": "4.0.2", | ||
"gulp-mocha": "8.0.0", | ||
"sonarqube-scanner": "2.8.2", | ||
"sonarqube-scanner": "2.9.1", | ||
"nyc": "15.1.0", | ||
"jszip": "3.8.0" | ||
"jszip": "3.10.1" | ||
}, | ||
@@ -43,3 +44,3 @@ "license": "SEE LICENSE IN LICENSE", | ||
"test": "node node_modules/nyc/bin/nyc.js --reporter=lcov node_modules/mocha/bin/_mocha test --recursive --check-leaks", | ||
"lint": "eslint -f stylish lib/ | tee ./defaultlint.xml", | ||
"lint": "eslint -f stylish lib/ test/ | tee ./defaultlint.xml", | ||
"prepareRelease": "npm prune --production", | ||
@@ -46,0 +47,0 @@ "sonar": "gulp sonarqube", |
Sorry, the diff of this file is not supported yet
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
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
77260
4.76%12
9.09%1006
8.17%15
7.14%26
8.33%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated
Updated
Updated