{"action":"created","comment":{"url":"https://api.github.com/repos/wso2/product-app-manager/pulls/comments/51753411","id":51753411,"diff_hunk":"@@ -21,553 +21,805 @@\n  Created Date: 6/8/2013\n  */\n \n-require('/modules/publisher.js').exec(function(ctx) {\n-\n-  var config = require('/config/publisher.json');\n-  var caramel = require('caramel');\n-  var router = require('/modules/router-g.js').router();\n-  var routeManager = new router.Router();\n-  var log = new Log();\n-  var permissions=require('/modules/permissions.js').permissions;\n-\n-  routeManager.register('POST', 'publisher', '/publisher/api/lifecycle/{type}/{id}', function(context) {\n-    var shortName = context.params.type;\n-    var id = context.params.id;\n-    var lifecycle = context.post.lifecycle;\n-    //var autoPromote=context.post.promote||false;    //If promote is not specified do not auto promote\n-    var invokeAction = '';\n-    //Check the config for a lifeCycleBehaviour block\n-    utility.isPresent(config, 'lifeCycleBehaviour', function(lifeCycleBehaviour) {\n-      utility.isPresent(lifeCycleBehaviour, lifeCycle, function(lifeCycleData) {\n-        utility.isPresent(lifeCycleData, 'onAttach', function(onAttach) {\n-          invokeAction = onAttach.action || '';\n-        });\n-      });\n-\n-    });\n-\n-    if (!lifecycle) {\n-        response.sendError(400, 'Missing required parameter: lifecycle');\n-        return;\n-    }\n-\n-    var artifactManager = rxtManager.getArtifactManager(shortName);\n-    try {\n-      var artifact = artifactManager.get(id);\n-      artifactManager.attachLifecycle(lifecycle, artifact);\n-      //Perform the action if the user has specified an action\n-      if (invokeAction != '') {\n-        artifactManager.promoteLifecycleState(invokeAction, artifact);\n-      }\n-\n-      var msg = {};\n-      msg['status'] = 'ok';\n-\n-      print(msg);\n-    } catch (e) {\n-      log.debug('The asset with id: ' + id + ' could not be found.The following exception was thrown: ' + e);\n-      response.sendError(404, 'The asset with id: ' + id + ' could not be found.Please refere to the server log.');\n-    }\n-  });\n-\n-  /*\n-        Ticks the check list item at the provided index\n-*/\n-  routeManager.register('POST', 'publisher', '/publisher/api/lifecycle/checklistitem/{index}/{type}/{id}', function(context) {\n-\n-    var checkListItemIndex = context.params.index;\n-    var shortName = context.params.type;\n-    var id = context.params.id;\n-    var artifactManager = rxtManager.getArtifactManager(shortName);\n-\n-    try {\n-      var artifact = artifactManager.get(id);\n-      artifactManager.checkItem(checkListItemIndex, artifact);\n-      var msg = {};\n-      msg['status'] = 'ok';\n-    } catch (e) {\n-      log.debug('The asset with id: ' + id + ' could not be found.The following exception was thrown: ' + e);\n-      response.sendError(404, 'The asset with id: ' + id + ' could not be found.Please refer to the server log.');\n-    }\n-  });\n-\n-  routeManager.register('DELETE', 'publisher', '/publisher/api/lifecycle/{type}/{id}', function(context) {\n-\n-    var shortName = context.params.type;\n-    var id = context.params.id;\n-    var artifactManager = rxtManager.getArtifactManager(shortName);\n-    try {\n-      var artifact = artifactManager.get(id);\n-      artifactManager.detachLifecycle(artifact);\n-\n-      var msg = {};\n-      msg['status'] = 'ok';\n-\n-      print(msg);\n-    } catch (e) {\n-      log.debug('The asset with id: ' + id + ' could not be found.The following exception was thrown: ' + e);\n-      response.sendError(404, 'The asset with id: ' + id + ' could not be found.Please refer to the server log.');\n-    }\n-\n-  });\n+require('/modules/publisher.js').exec(function (ctx) {\n+\n+\tvar config = require('/config/publisher.json');\n+\tvar caramel = require('caramel');\n+\tvar router = require('/modules/router-g.js').router();\n+\tvar routeManager = new router.Router();\n+\tvar log = new Log();\n+\tvar permissions = require('/modules/permissions.js').permissions;\n+\n+\trouteManager.register('POST', 'publisher', '/publisher/api/lifecycle/{type}/{id}', function (context) {\n+\t\tvar shortName = context.params.type;\n+\t\tvar id = context.params.id;\n+\t\tvar lifecycle = context.post.lifecycle;\n+\t\t//var autoPromote=context.post.promote||false;    //If promote is not specified do not auto promote\n+\t\tvar invokeAction = '';\n+\t\t//Check the config for a lifeCycleBehaviour block\n+\t\tutility.isPresent(config, 'lifeCycleBehaviour', function (lifeCycleBehaviour) {\n+\t\t\tutility.isPresent(lifeCycleBehaviour, lifeCycle, function (lifeCycleData) {\n+\t\t\t\tutility.isPresent(lifeCycleData, 'onAttach', function (onAttach) {\n+\t\t\t\t\tinvokeAction = onAttach.action || '';\n+\t\t\t\t});\n+\t\t\t});\n+\n+\t\t});\n+\n+\t\tif (!lifecycle) {\n+\t\t\tresponse.sendError(400, 'Missing required parameter: lifecycle');\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tvar artifactManager = rxtManager.getArtifactManager(shortName);\n+\t\ttry {\n+\t\t\tvar artifact = artifactManager.get(id);\n+\t\t\tartifactManager.attachLifecycle(lifecycle, artifact);\n+\t\t\t//Perform the action if the user has specified an action\n+\t\t\tif (invokeAction != '') {\n+\t\t\t\tartifactManager.promoteLifecycleState(invokeAction, artifact);\n+\t\t\t}\n+\n+\t\t\tvar msg = {};\n+\t\t\tmsg['status'] = 'ok';\n+\n+\t\t\tprint(msg);\n+\t\t} catch (e) {\n+\t\t\tlog.debug('The asset with id: ' + id + ' could not be found.The following exception was thrown: ' + e);\n+\t\t\tresponse.sendError(404, 'The asset with id: ' + id + ' could not be found.Please refere to the server log.');\n+\t\t}\n+\t});\n+\n+\t/*\n+\t Ticks the check list item at the provided index\n+\t */\n+\trouteManager.register('POST', 'publisher', '/publisher/api/lifecycle/checklistitem/{index}/{type}/{id}', function (context) {\n+\n+\t\tvar checkListItemIndex = context.params.index;\n+\t\tvar shortName = context.params.type;\n+\t\tvar id = context.params.id;\n+\t\tvar artifactManager = rxtManager.getArtifactManager(shortName);\n+\n+\t\ttry {\n+\t\t\tvar artifact = artifactManager.get(id);\n+\t\t\tartifactManager.checkItem(checkListItemIndex, artifact);\n+\t\t\tvar msg = {};\n+\t\t\tmsg['status'] = 'ok';\n+\t\t} catch (e) {\n+\t\t\tlog.debug('The asset with id: ' + id + ' could not be found.The following exception was thrown: ' + e);\n+\t\t\tresponse.sendError(404, 'The asset with id: ' + id + ' could not be found.Please refer to the server log.');\n+\t\t}\n+\t});\n+\n+\trouteManager.register('DELETE', 'publisher', '/publisher/api/lifecycle/{type}/{id}', function (context) {\n+\n+\t\tvar shortName = context.params.type;\n+\t\tvar id = context.params.id;\n+\t\tvar artifactManager = rxtManager.getArtifactManager(shortName);\n+\t\ttry {\n+\t\t\tvar artifact = artifactManager.get(id);\n+\t\t\tartifactManager.detachLifecycle(artifact);\n+\n+\t\t\tvar msg = {};\n+\t\t\tmsg['status'] = 'ok';\n+\n+\t\t\tprint(msg);\n+\t\t} catch (e) {\n+\t\t\tlog.debug('The asset with id: ' + id + ' could not be found.The following exception was thrown: ' + e);\n+\t\t\tresponse.sendError(404, 'The asset with id: ' + id + ' could not be found.Please refer to the server log.');\n+\t\t}\n+\n+\t});\n+\n+\t/*\n+\t Unticks the check list item at the given index\n+\t */\n+\trouteManager.register('DELETE', 'publisher', '/publisher/api/lifecycle/checklistitem/{index}/{type}/{id}', function (context) {\n+\t\tvar checkListItemIndex = context.params.index;\n+\t\tvar shortName = context.params.type;\n+\t\tvar id = context.params.id;\n+\t\tvar artifactManager = rxtManager.getArtifactManager(shortName);\n+\n+\t\ttry {\n+\t\t\tvar artifact = artifactManager.get(id);\n+\t\t\tartifactManager.uncheckItem(checkListItemIndex, artifact);\n+\t\t\tvar msg = {};\n+\t\t\tmsg['status'] = 'ok';\n+\t\t} catch (e) {\n+\t\t\tlog.debug('The lifecyle check list item at index ' + checkListItemIndex + ' of asset id: ' + id + ' could not be unticked' + 'The following exception was thrown: ' + e);\n+\t\t\tresponse.sendError(404, 'The lifecycle check list item at index ' + checkListItemIndex + ' of asset id: ' + id + ' could not be unchecked' + '.Please refer to the server log');\n+\t\t}\n+\t});\n+\n+\n+\t/**\n+\t * API for handling web/mobile apps lifecycle transitions\n+\t */\n+\trouteManager.register('PUT', 'publisher', '/publisher/api/lifecycle/{action}/{type}/{id}', function (context) {\n+\n+\t\tvar appType = context.params.type; // webapp, mobileapp\n+\t\tif (appType === \"mobileapp\") {\n+\t\t\tmobileAppsLifeCycleChange(context, rxtManager, utility);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tvar action = context.params.action; // see '/config/publisher.json' --> publisherActions\n+\t\tvar id = context.params.id; // UUID\n+\t\tvar roles = context.content.roles || [];\n+\n+\t\tvar content = {};\n+\t\tcontent.message = [];\n+\n+\t\t// validating action\n+\t\tif (config.publisherActions.indexOf(action) == -1) {\n+\t\t\tlog.error(\"Invalid action '\" + action + \"' invoked on a \" + appType + \" with UUID \" +\n+\t\t\t\tid);\n+\t\t\tresponse.status = 422; // Unprocessable Entity (WebDAV; RFC 4918)\n+\t\t\tresponse.contentType = \"application/json\";\n+\t\t\tcontent.status = \"Invalid Action\";\n+\t\t\tcontent.message.push(\"Action '\" + action + \"' cannot be performed on \" + appType + \"s\");\n+\t\t\tresponse.content = stringify(content);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tvar carbon = require('carbon');\n+\t\tvar userManager = server.userManager(server.current(session).tenantId);\n+\t\tvar user = server.current(session);\n+\n+\t\t// check whether current user has publisher permissions\n+\t\tvar authorizedPublisher = permissions.isAuthorized(user.username,\n+\t\t\tconfig.permissions.webapp_publish, userManager);\n+\t\tif (!authorizedPublisher) {\n+\t\t\tlog.error(\"Insufficient permissions to perform '\" + action + \"' on \" + appType);\n+\t\t\tresponse.status = 401;\n+\t\t\tresponse.contentType = \"application/json\";\n+\t\t\tcontent.status = \"Access Denied\";\n+\t\t\tcontent.message.push(\"You do not have publisher permissions to perform '\" +\n+\t\t\t\taction + \"' on \" + appType + \"s\");\n+\t\t\tresponse.content = stringify(content);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\t// get artifact manager for appType\n+\t\tvar artifactManager = rxtManager.getSystemArtifactManager(appType, session);\n+\n+\t\ttry {\n+\t\t\tvar currentArtifact = artifactManager.get(id);\n+\t\t} catch (e) {\n+\t\t\t// this exception is thrown by\n+\t\t\t// GenericArtifactManager.getGenericArtifact(id) throws GovernanceException\n+\t\t\tlog.error(\"The asset with UUID \" + id + \" could not be retrieved.\");\n+\t\t\tlog.error(e);\n+\t\t\tresponse.status = 404;\n+\t\t\tresponse.contentType = \"application/json\";\n+\t\t\tcontent.status = \"Not Found\";\n+\t\t\tcontent.message.push(\"The \" + appType + \" with UUID \" + id + \" cannot be retrieved\");\n+\t\t\tresponse.content = stringify(content);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\t// grant permissions\n+\t\tif (roles.length > 0) {\n+\t\t\tvar currentRole = null;\n+\t\t\ttry {\n+\t\t\t\troles.forEach(function (role) {\n+\t\t\t\t\tcurrentRole = role;\n+\t\t\t\t\tuserManager.authorizeRole(currentRole, currentArtifact.path,\n+\t\t\t\t\t\tcarbon.registry.actions.GET);\n+\t\t\t\t});\n+\t\t\t} catch (e) {\n+\t\t\t\tlog.error(\"Cannot grant permissions to role '\" + currentRole +\n+\t\t\t\t\t\"' to perform action '\" + carbon.registry.actions.GET + \"'\");\n+\t\t\t\tlog.error(e);\n+\t\t\t\t// continue work flow\n+\t\t\t}\n+\t\t}\n+\n+\t\t// check permissions to perform life cycle action\n+\t\ttry {\n+\t\t\tif (!permissions.isLCActionsPermitted(user.username, currentArtifact.path,\n+\t\t\t\t\tuserManager)) {\n+\t\t\t\tlog.error(\"Insufficient permissions to perform '\" + action + \"' on \" + appType +\n+\t\t\t\t\t\" for user \" + user.username);\n+\t\t\t\tresponse.status = 401;\n+\t\t\t\tresponse.contentType = \"application/json\";\n+\t\t\t\tcontent.status = \"Insufficient Permissions\";\n+\t\t\t\tcontent.message.push(\"You do not have permissions to perform action '\" + action +\n+\t\t\t\t\t\"' on version \" + currentArtifact.attributes.overview_version + \" of '\" +\n+\t\t\t\t\tcurrentArtifact.attributes.overview_name + \"' \" + appType);\n+\t\t\t\tresponse.content = stringify(content);\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t} catch (e) {\n+\t\t\t// this exception is thrown by\n+\t\t\t// org.wso2.carbon.user.core.AuthorizationManager.isRoleAuthorized(roleName,\n+\t\t\t// \t\t\t\t\t\t\t\t\t\tresourceId, action) throws UserStoreException\n+\t\t\tlog.error(\"Cannot retrieve permissions for action '\" + action + \"' on artifact '\" +\n+\t\t\t\tcurrentArtifact.path + \"' for user \" + user.username);\n+\t\t\tlog.error(e);\n+\t\t\tresponse.status = 500;\n+\t\t\tresponse.contentType = \"application/json\";\n+\t\t\tcontent.status = \"Error\";\n+\t\t\tcontent.message = \"Cannot retrieve permissions for action '\" + action + \"'\";\n+\t\t\tresponse.content = stringify(content);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\t// retrieve previously published web app versions (same provider & name)\n+\n+\t\t// If everything went correctly in previous executions, previousArtifacts array should\n+\t\t// contain only 1 item. However due to some failure previousArtifacts can contain\n+\t\t// multiple items, that means there are more than 1 published version of this app.\n+\t\t// In order to handle these situation previousArtifacts array is processed as a\n+\t\t// multi-item array in the following code.\n+\t\tvar previousArtifacts = [];\n+\n+\t\ttry {\n+\t\t\tpreviousArtifacts = artifactManager.find(function (artifact) {\n+\t\t\t\tvar provider = (artifact.attributes.overview_provider ==\n+\t\t\t\tcurrentArtifact.attributes.overview_provider);\n+\t\t\t\tvar name = (artifact.attributes.overview_name ==\n+\t\t\t\tcurrentArtifact.attributes.overview_name);\n+\t\t\t\treturn (provider && name && (artifact.lifecycleState == 'Published'));\n+\t\t\t}, null);\n+\t\t} catch (e) {\n+\t\t\tlog.error(\"Cannot retrieve previously published \" + appType + \" \" +\n+\t\t\t\tcurrentArtifact.attributes.overview_provider + \"-\" +\n+\t\t\t\tcurrentArtifact.attributes.overview_provider);\n+\t\t\tlog.error(e);\n+\t\t\t// continue work flow\n+\t\t}\n+\n+\t\tvar operationResult; // {isSuccess: true/false, message: \"\"}\n+\n+\t\t// for all previously published apps when publishing or re-publishing current app\n+\t\t// unpublish previously published version of this app\n+\t\tif (((action == \"Publish\") || (action == \"Re-Publish\")) && (previousArtifacts.length > 0)) {\n+\t\t\tvar i;\n+\t\t\tfor (i = 0; i < previousArtifacts.length; i++) {\n+\t\t\t\toperationResult = promoteArtifactLifeCycle(artifactManager, previousArtifacts[i],\n+\t\t\t\t\tappType, \"Unpublish\");\n+\t\t\t\tif (operationResult.isSuccess) {\n+\t\t\t\t\tcontent.message.push(operationResult.message);\n+\t\t\t\t} else {\n+\t\t\t\t\t// some error occurred in promoteArtifactLifeCycle function,\n+\t\t\t\t\t// abort further unpublishing previousArtifacts\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (!operationResult.isSuccess) {\n+\t\t\t\t// something went wrong in the above loop, revert changes\n+\t\t\t\tfor (var j = 0; j < i; j++) {\n+\t\t\t\t\tpromoteArtifactLifeCycle(artifactManager, previousArtifacts[j], appType,\n+\t\t\t\t\t\t\"Re-Publish\");\n+\t\t\t\t\t// ignore errors when reverting changes\n+\t\t\t\t}\n+\t\t\t\tresponse.status = 500;\n+\t\t\t\tresponse.contentType = \"application/json\";\n+\t\t\t\tcontent.status = \"Error\";\n+\t\t\t\tcontent.message.push(operationResult.message); // error message from last operation\n+\t\t\t\tresponse.content = stringify(content);\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t}\n+\n+\t\t// promote lifecycle of this app (current artifact)\n+\t\toperationResult = promoteArtifactLifeCycle(artifactManager, currentArtifact, appType, action);\n+\t\tif (operationResult.isSuccess) {\n+\t\t\tcontent.message.push(operationResult.message);\n+\t\t} else {\n+\t\t\t// some error occurred in promoteArtifactLifeCycle function,\n+\t\t\t// revert previously unpublished artifacts to Publish state\n+\t\t\tfor (i = 0; i < previousArtifacts.length; i++) {\n+\t\t\t\tpromoteArtifactLifeCycle(artifactManager, previousArtifacts[i], appType,\n+\t\t\t\t\t\"Re-Publish\");\n+\t\t\t\t// ignore errors when reverting changes\n+\t\t\t}\n+\t\t\tresponse.status = 500;\n+\t\t\tresponse.contentType = \"application/json\";\n+\t\t\tcontent.status = \"Error\";\n+\t\t\tcontent.message.push(operationResult.message); // error message from last operation\n+\t\t\tresponse.content = stringify(content);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\t// add comments for life cycle promotion\n+\t\ttry {\n+\t\t\tvar comment = null;\n+\t\t\tif (context.content && (comment = parse(context.content).comment)) {\n+\t\t\t\tvar lcHistory = artifactManager.getLifecycleHistory(currentArtifact);\n+\t\t\t\tvar lcHistoryXML = new XML(lcHistory.content);\n+\t\t\t\tlcHistoryJSON = utility.xml.convertE4XtoJSON(lcHistoryXML);\n+\t\t\t\tvar newChangeOrder = lcHistoryJSON.item[0].order;\n+\t\t\t\tvar commentHandler = require('/modules/comment.js');\n+\t\t\t\tcommentHandler.addLifecycleComment(artifactManager, currentArtifact.path, comment,\n+\t\t\t\t\tnewChangeOrder);\n+\t\t\t}\n+\t\t} catch (e) {\n+\t\t\tlog.error(\"Cannot add comment '\" + comment + \" for action '\" + action + \"' on \" +\n+\t\t\t\t\"artifact '\" + currentArtifact.path + \"' for user \" + user.username);\n+\t\t\tlog.error(e);\n+\t\t\t// continue work flow\n+\t\t}\n+\n+\t\t// only when publishing\n+\t\tif (action == \"Publish\") {\n+\n+\t\t\t//Enforcing restrict visibility logic\n+\t\t\tvar registry = new carbon.registry.Registry(server, {\n+\t\t\t\tusername: server.current(session).username,\n+\t\t\t\ttenantId: server.current(session).tenantId\n+\t\t\t});\n+\t\t\tvar roleRestrictionPath = currentArtifact.path + \"role_restrictions\";\n+\t\t\ttry {\n+\t\t\t\tvar permissionSet = registry.get(roleRestrictionPath);\n+\t\t\t\tvar permissionContent = '' + permissionSet.content;\n+\t\t\t\tif (permissionContent) {\n+\t\t\t\t\tuserManager.denyRole('Internal/everyone', currentArtifact.path,\n+\t\t\t\t\t\tcarbon.registry.actions.GET);\n+\t\t\t\t\tuserManager.denyRole('system/wso2.anonymous.role', currentArtifact.path,\n+\t\t\t\t\t\tcarbon.registry.actions.GET);\n+\t\t\t\t}\n+\t\t\t} catch (e) {\n+\t\t\t\tlog.debug('No role restrictions found for APP ' + id);\n+\t\t\t\t// continue work flow\n+\t\t\t}\n+\t\t}\n+\n+\t\t// for all previously published apps when publishing or re-publishing current app\n+\t\t// move subscriptions of that app version to this app version\n+\t\tif (((action == \"Publish\") || (action == \"Re-Publish\")) && (previousArtifacts.length > 0)) {\n+\n+\t\t\tfor (i = 0; i < previousArtifacts.length; i++) {\n+\t\t\t\toperationResult = moveSubscriptions(previousArtifacts[i], currentArtifact, appType);\n+\t\t\t\tif (operationResult.isSuccess) {\n+\t\t\t\t\tcontent.message.push(operationResult.message);\n+\t\t\t\t} else {\n+\t\t\t\t\t// some error occurred in moveSubscriptions function\n+\t\t\t\t\t// abort further subscription moving\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (!operationResult.isSuccess) {\n+\t\t\t\t// something went wrong in the above loop, revert subscription moving\n+\t\t\t\tif (previousArtifacts.length == 1) {\n+\t\t\t\t\t// if there are more than 1 previously published app, then we cannot revert\n+\t\t\t\t\t// moved subscriptions :(\n+\t\t\t\t\tmoveSubscriptions(currentArtifact, previousArtifacts[0], appType);\n+\t\t\t\t\t// ignore errors when reverting changes\n+\t\t\t\t}\n+\t\t\t\t// revert this app (current artifact) to unpublish state\n+\t\t\t\tpromoteArtifactLifeCycle(artifactManager, currentArtifact, appType, \"Unpublish\");\n+\t\t\t\t// revert previously unpublished artifacts to Publish state\n+\t\t\t\tfor (i = 0; i < previousArtifacts.length; i++) {\n+\t\t\t\t\tpromoteArtifactLifeCycle(artifactManager, previousArtifacts[i], appType,\n+\t\t\t\t\t\t\"Re-Publish\");\n+\t\t\t\t\t// ignore errors when reverting changes\n+\t\t\t\t}\n+\t\t\t\tresponse.status = 500;\n+\t\t\t\tresponse.contentType = \"application/json\";\n+\t\t\t\tcontent.status = \"Error\";\n+\t\t\t\tcontent.message.push(operationResult.message); // error message from last operation\n+\t\t\t\tresponse.content = stringify(content);\n+\t\t\t\treturn;\n+\t\t\t}\n+\n+\t\t}\n+\n+\t\t// everything went great\n+\t\tresponse.status = 200;\n+\t\tresponse.contentType = \"application/json\";\n+\t\tcontent.status = \"Success\";\n+\t\tresponse.content = stringify(content);\n+\n+\t});\n+\n+\tfunction promoteArtifactLifeCycle(artifactManager, artifact, artifactType, action) {\n+\t\tvar result = {};\n+\t\ttry {\n+\t\t\tartifactManager.promoteLifecycleState(action, artifact);\n+\t\t\tresult.isSuccess = true;\n+\t\t\tresult.message = getMessageForLifeCyclePromotion(artifact, artifactType, action, true);\n+\t\t} catch (e) {\n+\t\t\t// GovernanceArtifactImpl.invokeAction(action) throws GovernanceException\n+\t\t\tresult.isSuccess = false;\n+\t\t\tresult.message = getMessageForLifeCyclePromotion(artifact, artifactType, action, false);\n+\t\t\tlog.error(result.message);\n+\t\t\tlog.error(e);\n+\t\t}\n+\t\treturn result;\n+\t}\n \n-/*\n-  Unticks the check list item at the given index\n-*/\n-  routeManager.register('DELETE', 'publisher', '/publisher/api/lifecycle/checklistitem/{index}/{type}/{id}', function(context) {\n-    var checkListItemIndex = context.params.index;\n-    var shortName = context.params.type;\n-    var id = context.params.id;\n-    var artifactManager = rxtManager.getArtifactManager(shortName);\n-\n-    try {\n-      var artifact = artifactManager.get(id);\n-      artifactManager.uncheckItem(checkListItemIndex, artifact);\n-      var msg = {};\n-      msg['status'] = 'ok';\n-    } catch (e) {\n-      log.debug('The lifecyle check list item at index ' + checkListItemIndex + ' of asset id: ' + id + ' could not be unticked' + 'The following exception was thrown: ' + e);\n-      response.sendError(404, 'The lifecycle check list item at index ' + checkListItemIndex + ' of asset id: ' + id + ' could not be unchecked' + '.Please refer to the server log');\n-    }\n-  });\n-\n-\n-/**\n- * API for handling API Lifecycle Transitions\n- */\n-routeManager.register('PUT', 'publisher', '/publisher/api/lifecycle/{action}/{type}/{id}', function(context) {\n-        var shortName = context.params.type;\n-\n-        if(shortName === \"mobileapp\"){\n-\n-            mobileAppsLifeCycleChange(context, rxtManager, utility);\n-            return;\n-        }\n-\n-        var action = context.params.action;\n-        var id = context.params.id;\n-\n-        var roles = context.content.roles || [];\n-        //var artifactManager = rxtManager.getArtifactManager(shortName);\n-\n-        var artifactManager = rxtManager.getSystemArtifactManager(shortName,session);\n-        var artifact = artifactManager.get(id);\n-        var currentState = artifactManager.getLifecycleState(artifact).toString();\n-        var carbon = require('carbon');\n-        var um = server.userManager(server.current(session).tenantId);\n-        var validRoles = um.allRoles();\n-        var commentNeeded = false;\n-        var publishAuthorized = true;\n-        var msg={};\n-        var publisherActions = config.publisherActions;\n-        var user = server.current(session);\n-\n-        if(publisherActions.indexOf(action) > -1) {\n-            var um = server.userManager(server.current(session).tenantId);\n-            var publishAuthorized = permissions.isAuthorized(user.username, config.permissions.webapp_publish, um);\n-            log.debug(\"## Publish Authorized Check : \" + publishAuthorized);\n-        }\n-\n-        if (publishAuthorized) {\n-            if (commentNeeded) {\n-                response.sendError(400, 'This change requires that you insert a comment');\n-            } else {\n-                try {\n-                    var artifact = artifactManager.get(id);\n-                    var attributes = artifact.attributes;\n-\n-                    var previourStatus = artifact['lifecycleState'];\n-                    log.debug('Calling promoteLifecycleState for action : ' + action);\n-\n-                    if (roles.length > 0) {\n-                        roles.forEach(function (role) {\n-                            um.authorizeRole(role, artifact.path, carbon.registry.actions.GET);\n-                        });\n-                    }\n-\n-                    if (permissions.isLCActionsPermitted(user.username, artifact.path, um)) {\n-                        artifactManager.promoteLifecycleState(action, artifact);\n-                        msg['status'] = 'ok';\n-                        log.debug('Has permission to perform the action');\n-                    } else {\n-                        log.debug('Does not have permission to perform the action');\n-                        msg['status'] = 'failed';\n-                        msg['message'] = 'You do not have rights to perform the life-cycle operation';\n-                        response.status = 401;\n-                    }\n-\n-                    var comment;\n-                    if (context.content && (comment = parse(context.content).comment)) {\n-                        var lcHistory = artifactManager.getLifecycleHistory(artifact);\n-                        var lcHistoryXML = new XML(lcHistory.content);\n-                        lcHistoryJSON = utility.xml.convertE4XtoJSON(lcHistoryXML);\n-                        var newChangeOrder = lcHistoryJSON.item[0].order;\n-                        require('/modules/comment.js').addLifecycleComment(artifactManager, artifact.path, comment, newChangeOrder);\n-                    }\n-\n-                    //Enforcing restrict visibility logic\n-                    if(action==\"Publish\") {\n-                        var registry = new carbon.registry.Registry(server, {\n-                            username: server.current(session).username,\n-                            tenantId: server.current(session).tenantId\n-                        });\n-\n-                        var roleRestrictionPath = artifact.path + \"role_restrictions\";\n-                        try{\n-                            var permissionSet = registry.get(roleRestrictionPath);\n-                            var content = '' + permissionSet.content;\n-\n-                            if (content != \"{}\") {\n-                                um.denyRole('Internal/everyone', artifact.path, carbon.registry.actions.GET);\n-                                um.denyRole('system/wso2.anonymous.role', artifact.path, carbon.registry.actions.GET);\n-                            }\n-                        } catch (e) {\n-                            log.debug('No role restrictions found for APP ' + id);\n-                        }\n-                    }\n-                } catch (e) {\n-                    log.debug('The asset with id: ' + id + ' could not be found.The following exception was thrown: ' + e);\n-                    response.sendError(404, 'The asset with id: ' + id + ' could not be found.Please refer to the server log.');\n-                }\n-            }\n-\n-        } else {\n-            msg['status'] = 'error';\n-            msg['message'] = 'Action not permitted. Insufficient privileges.';\n-        }\n-        print(msg);\n-\n-});\n+\tfunction getMessageForLifeCyclePromotion(artifact, artifactType, action, isSuccess) {\n+\t\t// actions (from '/config/publisher.json' --> publisherActions):\n+\t\t// Submit for Review, Approve , Reject, Publish, Unpublish, Re-Publish, Deprecate, Retire, Recycle\n+\t\tif (isSuccess) {\n+\t\t\tvar msgAction = null;\n+\t\t\tif (action == \"Submit for Review\") {\n+\t\t\t\tmsgAction = \"submitted for review\"\n+\t\t\t} else if ((action == \"Approve\") || (action == \"Deprecate\") ||\n+\t\t\t\t(action == \"Retire\") || (action == \"Recycle\")) {\n+\t\t\t\tmsgAction = action.toLowerCase() + \"d\";\n+\t\t\t} else {\n+\t\t\t\t// Reject, Publish, Unpublish, Re-Publish\n+\t\t\t\tmsgAction = action.toLowerCase() + \"ed\";\n+\t\t\t}\n+\t\t\treturn \"Version \" + artifact.attributes.overview_version + \" of '\" +\n+\t\t\t\tartifact.attributes.overview_name + \"' \" + artifactType + \" \" +\n+\t\t\t\tmsgAction + \" successfully.\";\n+\t\t} else {\n+\t\t\treturn \"Cannot perform action '\" + action + \"' on version \" +\n+\t\t\t\tartifact.attributes.overview_version + \" of '\" +\n+\t\t\t\tartifact.attributes.overview_name + \"' \" + artifactType +\n+\t\t\t\t\" which is in '\" + artifact.lifecycleState + \"' state\";\n+\t\t}\n+\t}\n \n-/*\n- The function returns the lifecycle structure of a given asset\n- */\n-  function obtainLifeCycle(type) {\n-    CommonUtil = Packages.org.wso2.carbon.governance.lcm.util.CommonUtil;\n-    var rootReg = rxtManager.registry.registry;\n-    var configReg = rootReg.getChrootedRegistry('/_system/config');\n-\n-    //Obtain a model of the type\n-    var model = modelManager.getModel(type);\n-\n-    var lifecycle = model.get('*.lifeCycle').value;\n-\n-    var data = CommonUtil.getLifecycleConfiguration(lifecycle, configReg);\n-\n-    //Convert to an xml\n-    data = '<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>' + data;\n-    var xml = new XML(data);\n-\n-    //Convert to a json\n-    data = data.replace('<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>', '');\n-    var lcJSON = utility.xml.convertE4XtoJSON(xml);\n-\n-    return lcJSON;\n-  }\n-\n-\t\trouteManager.register('GET', 'publisher', '/publisher/api/lifecycle/{type}/{id}', function(context) {\n-    try {\n-\n-        appPublishWFExecutor = org.wso2.carbon.appmgt.impl.workflow.WorkflowExecutorFactory.getInstance().getWorkflowExecutor(\"AM_APPLICATION_PUBLISH\");\n-        var isAsynchronousFlow = appPublishWFExecutor.isAsynchronus();\n-        var shortName=context.params.type;\n-        var id=context.params.id;\n-        var artifactManager=rxtManager.getArtifactManager(shortName);\n-        var artifact=artifactManager.get(id);\n-        var state=artifactManager.getLifecycleState(artifact);\n-        var actions=artifactManager.availableActions(artifact);\n-        var lcJSON=obtainLifeCycle(shortName);\n-\n-        var msg={};\n-        msg['status']='ok';\n-        msg['state']=state;\n-        msg['actions']=actions;\n-        msg['lifecycle']=lcJSON;\n-        msg['asynch']=isAsynchronousFlow;\n-\n-        log.debug(msg);\n-        print(msg);\n-    } catch(e){\n-        log.debug('The asset with id: '+id+' could not be found.The following exception was thrown: '+e);\n-        response.sendError(404,'The asset with id: '+id+' could not be found.Please refere to the server log.');\n-    }\n-});\n-  \n-routeManager.register('GET', 'publisher', '/publisher/api/lifecycle/subscribe/{type}/{id}', function(context) {\n-    try {\n-\tvar shortName=context.params.type;\n-  \tvar id=context.params.id;\n-  \tvar artifactManager=rxtManager.getArtifactManager(shortName);\n-  \tvar artifact=artifactManager.get(id);\n-  \tvar state=artifactManager.getLifecycleState(artifact);\n-\tvar webappProvider = artifact.attributes['overview_provider'];\n-  \tvar webappName = artifact.attributes['overview_name'];\n-  \tvar webappVersion = artifact.attributes['overview_version'];\n-  \t\n-\tvar apiIdentifier = Packages.org.wso2.carbon.appmgt.api.model.APIIdentifier;\n-  \tvar apiIdentifierObj = new apiIdentifier(webappProvider, webappName, webappVersion);\n-  \t       \n-  \tvar appMDAO = Packages.org.wso2.carbon.appmgt.impl.dao.AppMDAO;\n-  \tvar appMDAOObj = new appMDAO();\n-  \tvar count = appMDAOObj.getAPISubscriptionCountByAPI(apiIdentifierObj);\n-  \tvar subscribed = false;\n-  \tif(count > 0){\n-\t\tsubscribed = true;\n-  \t}\n-  \t        \n-  \tvar msg={};\n-  \tmsg['subscribed'] = subscribed;\n-  \tmsg['state'] = state;\n-  \t \t\n-  \tlog.debug(msg);\n-  \tprint(msg);\n-    } catch(e){\t\n-\tlog.debug('The asset with id: '+id+' could not be found.The following exception was thrown: '+e);\n-  \tresponse.sendError(404,'The asset with id: '+id+' could not be found.Please refere to the server log.');\n-    }\n-});\n- \n-routeManager.register('GET', 'publisher', '/publisher/api/lifecycle/information/meta/{type}/roles', function(context) {\n-    var shortName = context.params.type;\n-    var carbon = require('carbon');\n-    var realmService = carbon.server.osgiService('org.wso2.carbon.user.core.service.RealmService');\n-    var realm = realmService.getTenantUserRealm(server.current(session).tenantId);\n-\n-    var allRoles = realm.getUserStoreManager().getRoleNames();\n-\n-    var lifecycleJSON = obtainLifeCycle(shortName);\n-    var relatedRoles = [];\n-\n-    // TODO: needs to go in a config\n-    var criticalRoles = ['admin'];\n-\n-    lifecycleJSON.configuration[0].lifecycle[0].scxml[0].state.forEach(function(state) {\n-      if (state.id != \"Retired\") {\n-\t      state.datamodel.forEach(function(dataModel) {\n-\t\tdataModel.data.forEach(function(data) {\n-\t\t  data.execution.forEach(function(execution) {\n-\t\t    execution.parameter.forEach(function(parameter) {\n-\t\t      if (parameter.name.indexOf('STATE_RULE') > -1) {\n-\t\t        var role = parameter.value.split(':')[0];\n-\t\t        if (role.indexOf('{') === -1) {\n-\t\t          relatedRoles.push(parameter.value.split(':')[0]);\n-\t\t        }\n-\t\t      }\n-\t\t    })\n-\t\t  })\n-\t\t})\n-\t      })\n+\tfunction moveSubscriptions(fromArtifact, toArtifact, artifactType) {\n+\t\tvar result = {};\n+\t\ttry {\n+\t\t\tvar apiIdentifier = Packages.org.wso2.carbon.appmgt.api.model.APIIdentifier;\n+\t\t\tvar apiProviderImpl = Packages.org.wso2.carbon.appmgt.impl.APIProviderImpl;\n+\t\t\tvar provider = new apiProviderImpl(jagg.getUser());\n+\n+\t\t\tvar fromIdentifier = new apiIdentifier(fromArtifact.attributes.overview_provider,\n+\t\t\t\tfromArtifact.attributes.overview_name,\n+\t\t\t\tfromArtifact.attributes.overview_version);\n+\t\t\tvar toIdentifier = new apiIdentifier(toArtifact.attributes.overview_provider,\n+\t\t\t\ttoArtifact.attributes.overview_name,\n+\t\t\t\ttoArtifact.attributes.overview_version);\n+\n+\t\t\tvar count = provider.moveSubscriptions(fromIdentifier, toIdentifier);\n+\t\t\tresult.isSuccess = true;\n+\t\t\tresult.message = count + \" subscription(s) moved from version \" +\n+\t\t\t\tfromArtifact.attributes.overview_version + \" to \" +\n+\t\t\t\ttoArtifact.attributes.overview_version + \" of '\" +\n+\t\t\t\tfromArtifact.attributes.overview_name + \"' \" + artifactType + \".\";\n+\t\t} catch (e) {\n+\t\t\t// this exception is thrown by\n+\t\t\t// APIProviderImpl.moveSubscriptions(fromIdentifier, toIdentifier) throws\n+\t\t\t//\t\t\t\t\t\t\t\t\t\t\t\t\t\tAppManagementException\n+\t\t\tresult.isSuccess = false;\n+\t\t\tresult.message = \"Cannot move subscriptions from version \" +\n+\t\t\t\tfromArtifact.attributes.overview_version + \" of '\" +\n+\t\t\t\tfromArtifact.attributes.overview_name + \"' \" + artifactType + \" to \" +\n+\t\t\t\ttoArtifact.attributes.overview_version + \" of '\" +\n+\t\t\t\ttoArtifact.attributes.overview_name + \"' \" + artifactType;\n+\t\t\tlog.error(result.message);\n+\t\t\tlog.error(e);\n+\t\t}\n+\t\treturn result;\n \t}\n-    });\n-\n-    for (var i = 0; i < criticalRoles.length; i++) {\n-      if (allRoles.indexOf(criticalRoles[i]) > -1) {\n-        allRoles.splice(i, 1);\n-      }\n-    };\n-\n-    // TODO: needs to go in a config\n-    if (relatedRoles.indexOf('Internal/everyone') === -1 && relatedRoles.indexOf('system/wso2.anonymous.role') === -1) {\n-      // if there are no permissions for anoanymous users and registered users\n-\n-      for (var i = 0; i < allRoles.length; i++) {\n-        if (relatedRoles.indexOf(allRoles[i]) === -1) {\n-          // if the role is not included in the lifecycle, remove the role from the list of possible roles\n-          allRoles.splice(i, 1);\n-        }\n-      }\n-    }\n-\n-    var matchedRoles = [];\n-\n-    var query = request.getQueryString() && request.getQueryString().split('=')[1];\n-\n-    if (!query) {\n-      query = '';\n-    }\n-\n-    for (var i = 0; i < allRoles.length; i++) {\n-      if (allRoles[i].indexOf(query) > -1) {\n-        matchedRoles.push({\n-          id: allRoles[i],\n-          name: allRoles[i]\n-        });\n-      }\n-    };\n-\n-    matchedRoles = parse(stringify(matchedRoles));\n-\n-    var hiddenRoles = config.hiddenRoles;\n-\n-    var filteredRoles = [];\n-\n-    if(matchedRoles.length > 0) {\n-        for (var i = 0; i < matchedRoles.length; i++) {\n-            if ((matchedRoles[i].id.indexOf(\"Internal/private_\") == -1) && (hiddenRoles.indexOf(matchedRoles[i].id) == -1)) {\n-                filteredRoles.push(matchedRoles[i]);\n-            }\n-        }\n-    }\n-\n-\n-    response.contentType = 'application/json';\n-    response.status = 200;\n-    response.content = filteredRoles;\n-});\n-\n-routeManager.register('GET','publisher','/publisher/api/lifecycle/checklist/{type}/{id}',function(context){\n-   //log.debug('short name: '+context.params.type);\n-\n-    try {\n-      log.debug('Entered check list/type/id ');\n-      var shortName = context.params.type;\n-      var id = context.params.id;\n-      var artifactManager = rxtManager.getArtifactManager(shortName);\n-      var artifact = artifactManager.get(id);\n-      var checkListItems = artifactManager.getCheckListItemNames(artifact);\n-      var msg = {};\n-      msg['status'] = 'ok';\n-      msg['checkListItems'] = checkListItems;\n-      print(msg);\n-    } catch (e) {\n-      log.debug('The check list names were not retrieved for artifact: ' + id + ' .The following exception was thrown: ' + e);\n-      response.sendError(404, 'The check list item names could not be retrieved for artifact: ' + id + ' .Please refer to the server log.');\n-    }\n-  });\n \n-/*\n-  Gets the checked or unchecked state of a check list item\n-*/\n-  routeManager.register('GET', 'publisher', '/publisher/api/lifecycle/checklistitem/{index}/{type}/{id}', function(context) {\n-    try {\n-      log.debug('Entered checklistitem/index/type/id ');\n-      var checkListItemIndex = context.params.index;\n-      var shortName = context.params.type;\n-      var id = context.params.id;\n-\n-      var artifactManager = rxtManager.getArtifactManager(shortName);\n-\n-      var artifact = artifactManager.get(id);\n-      var isChecked = artifactManager.isItemChecked(checkListItemIndex, artifact);\n-      var msg = {};\n-      msg['status'] = 'ok';\n-      msg['index'] = checkListItemIndex;\n-      msg['isChecked'] = isChecked;\n-      print(msg);\n-    } catch (e) {\n-      log.debug('The check list item index state could not be  retrieved for aritfact: ' + id + ' .The following exception was thrown: ' + e);\n-      response.sendError(404, 'The check list item state could not be retrieved for artifact: ' + id + ' .Please refer to the server log.');\n-    }\n-  });\n-\n-routeManager.register('GET', 'publisher', '/publisher/api/lifecycle/information/history/{type}/{id}/{version}', function(context) {\n-    try {\n-      var shortName = context.params.type;\n-      var id = context.params.id;\n-      var artifactManager = rxtManager.getArtifactManager(shortName);\n-      var artifact = artifactManager.get(id);\n-      var historyRes = artifactManager.getLifecycleHistory(artifact);\n-      var historyComments = require('/modules/comment.js').getLifecycleComments(artifactManager, artifact);\n-      var json = {};\n-\n-      if (historyRes != undefined) {\n-        var xml = new XML(historyRes.content);\n-        json = utility.xml.convertE4XtoJSON(xml);\n-      }\n-      print(json);\n-   }catch(e){\n-       log.debug('The lifecycle history for artifact: '+id+' could not be found.The following exception was thrown: '+e);\n-       response.sendError(404,'The lifecycle history details could not be retrieved for artifact: '+id+'.Pleas refer to the server log.');\n-   }\n-});\n-\n-var securityModule=require('/modules/security/security.manager.js').securityManagementModule();\n-\n-var sm=securityModule.cached();\n+\t/*\n+\t The function returns the lifecycle structure of a given asset\n+\t */\n+\tfunction obtainLifeCycle(type) {\n+\t\tCommonUtil = Packages.org.wso2.carbon.governance.lcm.util.CommonUtil;\n+\t\tvar rootReg = rxtManager.registry.registry;\n+\t\tvar configReg = rootReg.getChrootedRegistry('/_system/config');\n \n-//This will short circuit the handling of the requests\n-var passed=sm.check(session,function(){\n-    response.sendError(401);\n-});\n+\t\t//Obtain a model of the type\n+\t\tvar model = modelManager.getModel(type);\n \n-  //Stop servicing the request if the check failed\n-  if (passed) {\n-    var store = require('store');\n-    var user = store.user;\n-    var utility = require('/modules/utility.js').rxt_utility();\n-    var server = store.server;\n+\t\tvar lifecycle = model.get('*.lifeCycle').value;\n \n-    var publisher = require('/modules/publisher.js').publisher(request, session);\n-    var rxtManager = publisher.rxtManager;\n-    var modelManager = publisher.modelManager;\n+\t\tvar data = CommonUtil.getLifecycleConfiguration(lifecycle, configReg);\n \n-    routeManager.handle(request, response);\n-  }\n+\t\t//Convert to an xml\n+\t\tdata = '<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>' + data;\n+\t\tvar xml = new XML(data);\n \n-}, request, response, session);\n+\t\t//Convert to a json\n+\t\tdata = data.replace('<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>', '');\n+\t\tvar lcJSON = utility.xml.convertE4XtoJSON(xml);\n \n+\t\treturn lcJSON;\n+\t}\n \n+\trouteManager.register('GET', 'publisher', '/publisher/api/lifecycle/{type}/{id}', function (context) {\n+\t\ttry {\n+\n+\t\t\tappPublishWFExecutor = org.wso2.carbon.appmgt.impl.workflow.WorkflowExecutorFactory.getInstance().getWorkflowExecutor(\"AM_APPLICATION_PUBLISH\");","path":"modules/jaggery-apps/app-publisher-web/apis/v1/lifecycle_api_router.jag","position":978,"original_position":978,"commit_id":"de7b0c94d7b2b6cddfa1e668ba035e6e7a84ef68","original_commit_id":"de7b0c94d7b2b6cddfa1e668ba035e6e7a84ef68","user":{"login":"sajithab","id":11046571,"avatar_url":"https://avatars.githubusercontent.com/u/11046571?v=3","gravatar_id":"","url":"https://api.github.com/users/sajithab","html_url":"https://github.com/sajithab","followers_url":"https://api.github.com/users/sajithab/followers","following_url":"https://api.github.com/users/sajithab/following{/other_user}","gists_url":"https://api.github.com/users/sajithab/gists{/gist_id}","starred_url":"https://api.github.com/users/sajithab/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sajithab/subscriptions","organizations_url":"https://api.github.com/users/sajithab/orgs","repos_url":"https://api.github.com/users/sajithab/repos","events_url":"https://api.github.com/users/sajithab/events{/privacy}","received_events_url":"https://api.github.com/users/sajithab/received_events","type":"User","site_admin":false},"body":"Is there any reason to put this object in global context? ","created_at":"2016-02-03T17:12:07Z","updated_at":"2016-02-03T17:12:07Z","html_url":"https://github.com/wso2/product-app-manager/pull/234#discussion_r51753411","pull_request_url":"https://api.github.com/repos/wso2/product-app-manager/pulls/234","_links":{"self":{"href":"https://api.github.com/repos/wso2/product-app-manager/pulls/comments/51753411"},"html":{"href":"https://github.com/wso2/product-app-manager/pull/234#discussion_r51753411"},"pull_request":{"href":"https://api.github.com/repos/wso2/product-app-manager/pulls/234"}}},"pull_request":{"url":"https://api.github.com/repos/wso2/product-app-manager/pulls/234","id":36930131,"html_url":"https://github.com/wso2/product-app-manager/pull/234","diff_url":"https://github.com/wso2/product-app-manager/pull/234.diff","patch_url":"https://github.com/wso2/product-app-manager/pull/234.patch","issue_url":"https://api.github.com/repos/wso2/product-app-manager/issues/234","number":234,"state":"closed","locked":false,"title":"App versioning","user":{"login":"sajithar","id":11549922,"avatar_url":"https://avatars.githubusercontent.com/u/11549922?v=3","gravatar_id":"","url":"https://api.github.com/users/sajithar","html_url":"https://github.com/sajithar","followers_url":"https://api.github.com/users/sajithar/followers","following_url":"https://api.github.com/users/sajithar/following{/other_user}","gists_url":"https://api.github.com/users/sajithar/gists{/gist_id}","starred_url":"https://api.github.com/users/sajithar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sajithar/subscriptions","organizations_url":"https://api.github.com/users/sajithar/orgs","repos_url":"https://api.github.com/users/sajithar/repos","events_url":"https://api.github.com/users/sajithar/events{/privacy}","received_events_url":"https://api.github.com/users/sajithar/received_events","type":"User","site_admin":false},"body":"Implementing feature: Improving app versioning support in App Manager","created_at":"2015-06-04T12:42:49Z","updated_at":"2016-02-03T17:12:07Z","closed_at":"2015-06-05T07:15:57Z","merged_at":"2015-06-05T07:15:57Z","merge_commit_sha":"c8692655de052db77aa9ab60d7c5f873caedfa03","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/wso2/product-app-manager/pulls/234/commits","review_comments_url":"https://api.github.com/repos/wso2/product-app-manager/pulls/234/comments","review_comment_url":"https://api.github.com/repos/wso2/product-app-manager/pulls/comments{/number}","comments_url":"https://api.github.com/repos/wso2/product-app-manager/issues/234/comments","statuses_url":"https://api.github.com/repos/wso2/product-app-manager/statuses/de7b0c94d7b2b6cddfa1e668ba035e6e7a84ef68","head":{"label":"sajithar:app-versioning","ref":"app-versioning","sha":"de7b0c94d7b2b6cddfa1e668ba035e6e7a84ef68","user":{"login":"sajithar","id":11549922,"avatar_url":"https://avatars.githubusercontent.com/u/11549922?v=3","gravatar_id":"","url":"https://api.github.com/users/sajithar","html_url":"https://github.com/sajithar","followers_url":"https://api.github.com/users/sajithar/followers","following_url":"https://api.github.com/users/sajithar/following{/other_user}","gists_url":"https://api.github.com/users/sajithar/gists{/gist_id}","starred_url":"https://api.github.com/users/sajithar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sajithar/subscriptions","organizations_url":"https://api.github.com/users/sajithar/orgs","repos_url":"https://api.github.com/users/sajithar/repos","events_url":"https://api.github.com/users/sajithar/events{/privacy}","received_events_url":"https://api.github.com/users/sajithar/received_events","type":"User","site_admin":false},"repo":{"id":32860728,"name":"product-app-manager","full_name":"sajithar/product-app-manager","owner":{"login":"sajithar","id":11549922,"avatar_url":"https://avatars.githubusercontent.com/u/11549922?v=3","gravatar_id":"","url":"https://api.github.com/users/sajithar","html_url":"https://github.com/sajithar","followers_url":"https://api.github.com/users/sajithar/followers","following_url":"https://api.github.com/users/sajithar/following{/other_user}","gists_url":"https://api.github.com/users/sajithar/gists{/gist_id}","starred_url":"https://api.github.com/users/sajithar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sajithar/subscriptions","organizations_url":"https://api.github.com/users/sajithar/orgs","repos_url":"https://api.github.com/users/sajithar/repos","events_url":"https://api.github.com/users/sajithar/events{/privacy}","received_events_url":"https://api.github.com/users/sajithar/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/sajithar/product-app-manager","description":"","fork":true,"url":"https://api.github.com/repos/sajithar/product-app-manager","forks_url":"https://api.github.com/repos/sajithar/product-app-manager/forks","keys_url":"https://api.github.com/repos/sajithar/product-app-manager/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sajithar/product-app-manager/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sajithar/product-app-manager/teams","hooks_url":"https://api.github.com/repos/sajithar/product-app-manager/hooks","issue_events_url":"https://api.github.com/repos/sajithar/product-app-manager/issues/events{/number}","events_url":"https://api.github.com/repos/sajithar/product-app-manager/events","assignees_url":"https://api.github.com/repos/sajithar/product-app-manager/assignees{/user}","branches_url":"https://api.github.com/repos/sajithar/product-app-manager/branches{/branch}","tags_url":"https://api.github.com/repos/sajithar/product-app-manager/tags","blobs_url":"https://api.github.com/repos/sajithar/product-app-manager/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sajithar/product-app-manager/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sajithar/product-app-manager/git/refs{/sha}","trees_url":"https://api.github.com/repos/sajithar/product-app-manager/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sajithar/product-app-manager/statuses/{sha}","languages_url":"https://api.github.com/repos/sajithar/product-app-manager/languages","stargazers_url":"https://api.github.com/repos/sajithar/product-app-manager/stargazers","contributors_url":"https://api.github.com/repos/sajithar/product-app-manager/contributors","subscribers_url":"https://api.github.com/repos/sajithar/product-app-manager/subscribers","subscription_url":"https://api.github.com/repos/sajithar/product-app-manager/subscription","commits_url":"https://api.github.com/repos/sajithar/product-app-manager/commits{/sha}","git_commits_url":"https://api.github.com/repos/sajithar/product-app-manager/git/commits{/sha}","comments_url":"https://api.github.com/repos/sajithar/product-app-manager/comments{/number}","issue_comment_url":"https://api.github.com/repos/sajithar/product-app-manager/issues/comments{/number}","contents_url":"https://api.github.com/repos/sajithar/product-app-manager/contents/{+path}","compare_url":"https://api.github.com/repos/sajithar/product-app-manager/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sajithar/product-app-manager/merges","archive_url":"https://api.github.com/repos/sajithar/product-app-manager/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sajithar/product-app-manager/downloads","issues_url":"https://api.github.com/repos/sajithar/product-app-manager/issues{/number}","pulls_url":"https://api.github.com/repos/sajithar/product-app-manager/pulls{/number}","milestones_url":"https://api.github.com/repos/sajithar/product-app-manager/milestones{/number}","notifications_url":"https://api.github.com/repos/sajithar/product-app-manager/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sajithar/product-app-manager/labels{/name}","releases_url":"https://api.github.com/repos/sajithar/product-app-manager/releases{/id}","deployments_url":"https://api.github.com/repos/sajithar/product-app-manager/deployments","created_at":"2015-03-25T11:36:24Z","updated_at":"2016-01-18T08:54:17Z","pushed_at":"2016-02-01T06:10:38Z","git_url":"git://github.com/sajithar/product-app-manager.git","ssh_url":"git@github.com:sajithar/product-app-manager.git","clone_url":"https://github.com/sajithar/product-app-manager.git","svn_url":"https://github.com/sajithar/product-app-manager","homepage":"","size":181347,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"wso2:master","ref":"master","sha":"3e3a620397ab1045a0909f197b94db1876e0b7d8","user":{"login":"wso2","id":533043,"avatar_url":"https://avatars.githubusercontent.com/u/533043?v=3","gravatar_id":"","url":"https://api.github.com/users/wso2","html_url":"https://github.com/wso2","followers_url":"https://api.github.com/users/wso2/followers","following_url":"https://api.github.com/users/wso2/following{/other_user}","gists_url":"https://api.github.com/users/wso2/gists{/gist_id}","starred_url":"https://api.github.com/users/wso2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wso2/subscriptions","organizations_url":"https://api.github.com/users/wso2/orgs","repos_url":"https://api.github.com/users/wso2/repos","events_url":"https://api.github.com/users/wso2/events{/privacy}","received_events_url":"https://api.github.com/users/wso2/received_events","type":"Organization","site_admin":false},"repo":{"id":16755765,"name":"product-app-manager","full_name":"wso2/product-app-manager","owner":{"login":"wso2","id":533043,"avatar_url":"https://avatars.githubusercontent.com/u/533043?v=3","gravatar_id":"","url":"https://api.github.com/users/wso2","html_url":"https://github.com/wso2","followers_url":"https://api.github.com/users/wso2/followers","following_url":"https://api.github.com/users/wso2/following{/other_user}","gists_url":"https://api.github.com/users/wso2/gists{/gist_id}","starred_url":"https://api.github.com/users/wso2/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wso2/subscriptions","organizations_url":"https://api.github.com/users/wso2/orgs","repos_url":"https://api.github.com/users/wso2/repos","events_url":"https://api.github.com/users/wso2/events{/privacy}","received_events_url":"https://api.github.com/users/wso2/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/wso2/product-app-manager","description":"App Manager store and publisher","fork":false,"url":"https://api.github.com/repos/wso2/product-app-manager","forks_url":"https://api.github.com/repos/wso2/product-app-manager/forks","keys_url":"https://api.github.com/repos/wso2/product-app-manager/keys{/key_id}","collaborators_url":"https://api.github.com/repos/wso2/product-app-manager/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/wso2/product-app-manager/teams","hooks_url":"https://api.github.com/repos/wso2/product-app-manager/hooks","issue_events_url":"https://api.github.com/repos/wso2/product-app-manager/issues/events{/number}","events_url":"https://api.github.com/repos/wso2/product-app-manager/events","assignees_url":"https://api.github.com/repos/wso2/product-app-manager/assignees{/user}","branches_url":"https://api.github.com/repos/wso2/product-app-manager/branches{/branch}","tags_url":"https://api.github.com/repos/wso2/product-app-manager/tags","blobs_url":"https://api.github.com/repos/wso2/product-app-manager/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/wso2/product-app-manager/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/wso2/product-app-manager/git/refs{/sha}","trees_url":"https://api.github.com/repos/wso2/product-app-manager/git/trees{/sha}","statuses_url":"https://api.github.com/repos/wso2/product-app-manager/statuses/{sha}","languages_url":"https://api.github.com/repos/wso2/product-app-manager/languages","stargazers_url":"https://api.github.com/repos/wso2/product-app-manager/stargazers","contributors_url":"https://api.github.com/repos/wso2/product-app-manager/contributors","subscribers_url":"https://api.github.com/repos/wso2/product-app-manager/subscribers","subscription_url":"https://api.github.com/repos/wso2/product-app-manager/subscription","commits_url":"https://api.github.com/repos/wso2/product-app-manager/commits{/sha}","git_commits_url":"https://api.github.com/repos/wso2/product-app-manager/git/commits{/sha}","comments_url":"https://api.github.com/repos/wso2/product-app-manager/comments{/number}","issue_comment_url":"https://api.github.com/repos/wso2/product-app-manager/issues/comments{/number}","contents_url":"https://api.github.com/repos/wso2/product-app-manager/contents/{+path}","compare_url":"https://api.github.com/repos/wso2/product-app-manager/compare/{base}...{head}","merges_url":"https://api.github.com/repos/wso2/product-app-manager/merges","archive_url":"https://api.github.com/repos/wso2/product-app-manager/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/wso2/product-app-manager/downloads","issues_url":"https://api.github.com/repos/wso2/product-app-manager/issues{/number}","pulls_url":"https://api.github.com/repos/wso2/product-app-manager/pulls{/number}","milestones_url":"https://api.github.com/repos/wso2/product-app-manager/milestones{/number}","notifications_url":"https://api.github.com/repos/wso2/product-app-manager/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/wso2/product-app-manager/labels{/name}","releases_url":"https://api.github.com/repos/wso2/product-app-manager/releases{/id}","deployments_url":"https://api.github.com/repos/wso2/product-app-manager/deployments","created_at":"2014-02-12T04:19:27Z","updated_at":"2016-02-02T13:06:07Z","pushed_at":"2016-02-02T18:20:27Z","git_url":"git://github.com/wso2/product-app-manager.git","ssh_url":"git@github.com:wso2/product-app-manager.git","clone_url":"https://github.com/wso2/product-app-manager.git","svn_url":"https://github.com/wso2/product-app-manager","homepage":null,"size":182802,"stargazers_count":8,"watchers_count":8,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":43,"mirror_url":null,"open_issues_count":4,"forks":43,"open_issues":4,"watchers":8,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/wso2/product-app-manager/pulls/234"},"html":{"href":"https://github.com/wso2/product-app-manager/pull/234"},"issue":{"href":"https://api.github.com/repos/wso2/product-app-manager/issues/234"},"comments":{"href":"https://api.github.com/repos/wso2/product-app-manager/issues/234/comments"},"review_comments":{"href":"https://api.github.com/repos/wso2/product-app-manager/pulls/234/comments"},"review_comment":{"href":"https://api.github.com/repos/wso2/product-app-manager/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/wso2/product-app-manager/pulls/234/commits"},"statuses":{"href":"https://api.github.com/repos/wso2/product-app-manager/statuses/de7b0c94d7b2b6cddfa1e668ba035e6e7a84ef68"}}}}