Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lifecycleStatus control in launch and retired/obsolete #314

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 144 additions & 60 deletions controllers/tmf-apis/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,28 @@ const catalog = (function() {
})
};

const getDependencySpecs = function (endpoint, path, refs, fields, callback){

const specPath = `/${path}?id=${tmfUtils.refsToQuery(refs)}&fields=${fields}`
const uri = utils.getAPIURL(
endpoint.appSsl,
endpoint.host,
endpoint.port,
specPath
);
axios.get(uri).then((response) => {
callback(null, {
status: response.status,
body: response.data
});

}).catch((err) => {
callback({
status: err.status
});
})
}

// Retrieves the product belonging to a given offering
const retrieveProduct = function(productId, callback) {

Expand Down Expand Up @@ -533,6 +555,62 @@ const catalog = (function() {
}
};

const checkDependencySpecs = function(prevBody, newBody, callback){
if (!!prevBody.lifecycleStatus && prevBody.lifecycleStatus.toLowerCase() !== 'launched' &&
!!newBody.lifecycleStatus && newBody.lifecycleStatus.toLowerCase() === 'launched'
){
async.series([
function(callback){
getDependencySpecs(config.endpoints.service , 'serviceSpecification', prevBody.serviceSpecification, 'lifecycleStatus',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The service specification must exist and be a non-empty list

function (err, response){
if (err){
return callback({
status: 400,
message: 'Error getting service specification through the API'
})
}
else {
const serviceSpecification = response.body
if(!tmfUtils.haveSameStatus('launched', serviceSpecification)){
return callback({
status: 409,
message: 'It is not allowed to launch a product spec without launching service spec previously'
})
}
callback(null)
}
}
)
},
function(callback){
getDependencySpecs(config.endpoints.resource, 'resourceSpecification', prevBody.resourceSpecification, 'lifecycleStatus',
function (err, response){
if (err){
return callback({
status: 400,
message: 'Error getting resource specification through the API'
})
}
else {
const resourceSpecification = response.body
if(!tmfUtils.haveSameStatus('launched', resourceSpecification)){
return callback({
status: 409,
message: 'It is not allowed to launch a product spec without launching resource spec previously'
})
}
callback(null)
}
}
)
}
], callback)
}
else{
callback(null)
}
}

const validateProductUpdate = function(req, prevBody, newBody, callback) {
if (
(!!newBody.isBundle || !!newBody.bundledProductSpecification) &&
Expand All @@ -544,70 +622,76 @@ const catalog = (function() {
'It is not allowed to update bundle related attributes (isBundle, bundledProductSpecification) in launched products'
});
}
async.series([
function(callback){
checkDependencySpecs(prevBody, newBody, callback)
},
function(callback){
// Check upgrade problems if the product is a digital one
if (tmfUtils.isDigitalProduct(prevBody.productSpecCharacteristic)) {
if (
!!newBody.version &&
!tmfUtils.isDigitalProduct(newBody.productSpecCharacteristic) &&
newBody.version != prevBody.version
) {
// Trying to upgrade the product without providing new asset info
return callback({
status: 422,
message: 'To upgrade product specifications it is required to provide new asset info'
});
}

// Check upgrade problems if the product is a digital one
if (tmfUtils.isDigitalProduct(prevBody.productSpecCharacteristic)) {
if (
!!newBody.version &&
!tmfUtils.isDigitalProduct(newBody.productSpecCharacteristic) &&
newBody.version != prevBody.version
) {
// Trying to upgrade the product without providing new asset info
return callback({
status: 422,
message: 'To upgrade product specifications it is required to provide new asset info'
});
}

if (
(!!newBody.version && newBody.version == prevBody.version) ||
(typeof newBody.version === 'undefined' &&
!!newBody.productSpecCharacteristic &&
!equal(newBody.productSpecCharacteristic, prevBody.productSpecCharacteristic))
) {
return callback({
status: 422,
message: 'Product specification characteristics only can be updated for upgrading digital products'
});
}
if (
(!!newBody.version && newBody.version == prevBody.version) ||
(typeof newBody.version === 'undefined' &&
!!newBody.productSpecCharacteristic &&
!equal(newBody.productSpecCharacteristic, prevBody.productSpecCharacteristic))
) {
return callback({
status: 422,
message: 'Product specification characteristics only can be updated for upgrading digital products'
});
}

if (
!!newBody.version &&
newBody.version != prevBody.version &&
tmfUtils.isDigitalProduct(newBody.productSpecCharacteristic) &&
!tmfUtils.equalCustomCharacteristics(
newBody.productSpecCharacteristic,
prevBody.productSpecCharacteristic
)
) {
return callback({
status: 422,
message: 'It is not allowed to update custom characteristics during a product upgrade'
});
}
if (
!!newBody.version &&
newBody.version != prevBody.version &&
tmfUtils.isDigitalProduct(newBody.productSpecCharacteristic) &&
!tmfUtils.equalCustomCharacteristics(
newBody.productSpecCharacteristic,
prevBody.productSpecCharacteristic
)
) {
return callback({
status: 422,
message: 'It is not allowed to update custom characteristics during a product upgrade'
});
}

if (!!newBody.version && newBody.version != prevBody.version && !!newBody.productSpecCharacteristic) {
return storeClient.upgradeProduct(
{
id: prevBody.id,
version: newBody.version,
productSpecCharacteristic: newBody.productSpecCharacteristic
},
req.user,
callback
);
}
} /*else if (
!!newBody.productSpecCharacteristic &&
!equal(newBody.productSpecCharacteristic, prevBody.productSpecCharacteristic)
) {
return callback({
status: 422,
message: 'Product spec characteristics cannot be updated'
});
}*/
if (!!newBody.version && newBody.version != prevBody.version && !!newBody.productSpecCharacteristic) {
return storeClient.upgradeProduct(
{
id: prevBody.id,
version: newBody.version,
productSpecCharacteristic: newBody.productSpecCharacteristic
},
req.user,
callback
);
}
} /*else if (
!!newBody.productSpecCharacteristic &&
!equal(newBody.productSpecCharacteristic, prevBody.productSpecCharacteristic)
) {
return callback({
status: 422,
message: 'Product spec characteristics cannot be updated'
});
}*/

return callback(null);
return callback(null);
},
], callback)
};

const validateProduct = function(req, productSpec, callback) {
Expand Down
52 changes: 51 additions & 1 deletion controllers/tmf-apis/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,28 @@ const resource = (function (){
}
};

const getProductSpecs = function (ref, fields, callback){
const endpoint = config.endpoints.catalog
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an extra tab

const specPath = `/productSpecification?resourceSpecification.id=${ref}&fields=${fields}`
const uri = utils.getAPIURL(
endpoint.appSsl,
endpoint.host,
endpoint.port,
specPath
);
axios.get(uri).then((response) => {
callback(null, {
status: response.status,
body: response.data
});

}).catch((err) => {
callback({
status: err.status
});
})
}

const validateUpdate = function(req, callback) {
// Check the lifecycle updates
const body = req.parsedBody
Expand All @@ -132,8 +154,36 @@ const resource = (function (){
message: `Cannot transition from lifecycle status ${prevBody.lifecycleStatus} to ${body.lifecycleStatus}`
})
}
if (!!prevBody.lifecycleStatus && prevBody.lifecycleStatus.toLowerCase() !== 'retired' &&
!!body.lifecycleStatus && body.lifecycleStatus.toLowerCase() === 'retired' ){
getProductSpecs(prevBody.id, 'lifecycleStatus', function (err, response){
if(err) {
callback(err)
} else {
const data = response.body
let allRetObs = true
for (let prodSpec of data){
if(prodSpec.lifecycleStatus.toLowerCase() !== 'retired' && prodSpec.lifecycleStatus.toLowerCase() !== 'obsolete'){
allRetObs = false
break;
}
}
if(allRetObs){
callback(null)
}
else {
callback({
status: 409,
message: `Cannot retire a resource spec without retiring all product specs linked with it`
})
}
}
})
}
else{
callback(null)
}

callback(null)
}

const validateOwnerSellerPost = function(req, callback) {
Expand Down
52 changes: 51 additions & 1 deletion controllers/tmf-apis/serviceCatalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,28 @@ const serviceCatalog = (function() {
}
};

const getProductSpecs = function (ref, fields, callback){
const endpoint = config.endpoints.catalog
const specPath = `/productSpecification?serviceSpecification.id=${ref}&fields=${fields}`
const uri = utils.getAPIURL(
endpoint.appSsl,
endpoint.host,
endpoint.port,
specPath
);
axios.get(uri).then((response) => {
callback(null, {
status: response.status,
body: response.data
});

}).catch((err) => {
callback({
status: err.status
});
})
}

const validateUpdate = function(req, callback) {
// Check the lifecycle updates
const body = req.parsedBody
Expand All @@ -150,8 +172,36 @@ const serviceCatalog = (function() {
message: `Cannot transition from lifecycle status ${prevBody.lifecycleStatus} to ${body.lifecycleStatus}`
})
}
if (!!prevBody.lifecycleStatus && prevBody.lifecycleStatus.toLowerCase() !== 'retired' &&
!!body.lifecycleStatus && body.lifecycleStatus.toLowerCase() === 'retired' ){
getProductSpecs(prevBody.id, 'lifecycleStatus', function (err, response){

callback(null)
if(err) {
callback(err)
} else {
const data = response.body
let allRetObs = true
for (let prodSpec of data){
if(prodSpec.lifecycleStatus.toLowerCase() !== 'retired' && prodSpec.lifecycleStatus.toLowerCase() !== 'obsolete'){
allRetObs = false
break;
}
}
if(allRetObs){
callback(null)
}
else {
callback({
status: 409,
message: `Cannot retire a service spec without retiring all product specs linked with it`
})
}
}
})
}
else{
callback(null)
}
}

const validators = {
Expand Down
26 changes: 26 additions & 0 deletions lib/tmfUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,4 +294,30 @@ exports.isValidStatusTransition = function(firstSt, nextSt) {
const nextInd = lifecycle.indexOf(nextSt.toLowerCase())

return firstInd > -1 && nextInd > -1 && nextInd == firstInd + 1
}

/**
* Checks whether the specified status are the same in each element of the array
* @param status Value of lifecycleStatus in lower case that should be in each element of the array
* @param array An array of service/resource specification references
*/
exports.haveSameStatus = function(status, array){
size= array.reduce((acc, spec) => {
if(!!spec.lifecycleStatus && spec.lifecycleStatus.toLowerCase() === status){
acc++
}
return acc
}, 0)
return (size === array.length)
}

/**
* Extracts id of each reference and returns a String with all ids in query param mode
* @param refs An array of dependency references
*/
exports.refsToQuery = function(refs){
if (!refs || refs.length === 0) {
return ''
}
return refs.map(ref => ref.id).join(',');
}
Loading
Loading