diff --git a/components/app-framework/src/widgets/async-task-dialog/async-task-dialog.service.js b/components/app-framework/src/widgets/async-task-dialog/async-task-dialog.service.js index 03e59ccc7b..c236645535 100644 --- a/components/app-framework/src/widgets/async-task-dialog/async-task-dialog.service.js +++ b/components/app-framework/src/widgets/async-task-dialog/async-task-dialog.service.js @@ -61,6 +61,7 @@ } context.noCancel = !!config.noCancel; context.noSubmit = !!config.noSubmit; + context.allowEnter = config.allowEnter; if (angular.isFunction(invalidityCheck)) { context.invalidityCheck = invalidityCheck; @@ -143,7 +144,11 @@ // Ignore enter if event has been marked with preventDefault() vm.canUseEnter = function (ev) { - return !ev.isDefaultPrevented(); + if (vm.context.allowEnter) { + return vm.context.allowEnter(vm, ev); + } else { + return !ev.isDefaultPrevented(); + } }; /** diff --git a/components/cloud-foundry/frontend/i18n/en_US/app.json b/components/cloud-foundry/frontend/i18n/en_US/app.json index 0b22d29894..ae1d2485e0 100644 --- a/components/cloud-foundry/frontend/i18n/en_US/app.json +++ b/components/cloud-foundry/frontend/i18n/en_US/app.json @@ -259,6 +259,13 @@ }, "json-error-invalid": "Must be valid JSON" }, + "edit": { + "title": "Edit Service Instance", + "button": { + "yes": "Update" + }, + "success": "Service instance successfully updated" + }, "view-envs": { "title": "{{instanceName}}: Variables" }, diff --git a/components/cloud-foundry/frontend/i18n/en_US/space.json b/components/cloud-foundry/frontend/i18n/en_US/space.json index b7d3e4a97a..4897408f16 100644 --- a/components/cloud-foundry/frontend/i18n/en_US/space.json +++ b/components/cloud-foundry/frontend/i18n/en_US/space.json @@ -38,6 +38,7 @@ "unmap-route-action": "Unmap Route", "detach-service-action": "Detach Service", "delete-service-action": "Delete Service", + "edit-service-action": "Edit Service", "tabs": { "show-all-button": "Show All", "column.action": "action", diff --git a/components/cloud-foundry/frontend/src/model/service/service-instance.model.js b/components/cloud-foundry/frontend/src/model/service/service-instance.model.js index d5e2ad74ed..7fb3c428b4 100644 --- a/components/cloud-foundry/frontend/src/model/service/service-instance.model.js +++ b/components/cloud-foundry/frontend/src/model/service/service-instance.model.js @@ -33,6 +33,7 @@ all: all, createServiceInstance: createServiceInstance, deleteServiceInstance: deleteServiceInstance, + updateServiceInstance: updateServiceInstance, listAllServiceBindingsForServiceInstance: listAllServiceBindingsForServiceInstance, onAll: onAll }; @@ -101,6 +102,25 @@ }); } + /** + * @function updateServiceInstance + * @memberof cloud-foundry.model.service-instance.ServiceInstance + * @description Update a service instance. + * @param {string} cnsiGuid - the CNSI guid + * @param {object} serviceInstanceGuid - the service instance guid of the service instance to delete + * @param {object} bindingData - the service instance data + * @param {object} params - additional params to pass to request + * @returns {promise} A promise object + * @public + */ + function updateServiceInstance(cnsiGuid, serviceInstanceGuid, bindingData, params) { + return serviceInstanceApi.UpdateServiceInstance(serviceInstanceGuid, bindingData, params, + modelUtils.makeHttpConfig(cnsiGuid)) + .then(function (response) { + return response.data; + }); + } + /** * @function listAllServiceBindingsForServiceInstance * @memberof cloud-foundry.model.service-instance.ServiceInstance diff --git a/components/cloud-foundry/frontend/src/view/applications/services/service-instance/service-instance.service.js b/components/cloud-foundry/frontend/src/view/applications/services/service-instance/service-instance.service.js index 5ee7877291..2ce3c75d0e 100644 --- a/components/cloud-foundry/frontend/src/view/applications/services/service-instance/service-instance.service.js +++ b/components/cloud-foundry/frontend/src/view/applications/services/service-instance/service-instance.service.js @@ -16,10 +16,11 @@ * @param {app.view.appNotificationsService} appNotificationsService - the toast notification service * @param {app.framework.widgets.frameworkDetailView} frameworkDetailView - the detail view service * @param {app.framework.widgets.dialog.frameworkDialogConfirm} frameworkDialogConfirm - the confirm dialog + * @param {object} cfServiceCreateServiceInstanceWorkflow - the cfServiceCreateServiceInstanceWorkflow service * @returns {object} A service instance factory */ function serviceInstanceFactory($log, $translate, $q, modelManager, appNotificationsService, frameworkDetailView, - frameworkDialogConfirm) { + frameworkDialogConfirm, cfServiceCreateServiceInstanceWorkflow) { var appModel = modelManager.retrieve('cloud-foundry.model.application'); var bindingModel = modelManager.retrieve('cloud-foundry.model.service-binding'); var instanceModel = modelManager.retrieve('cloud-foundry.model.service-instance'); @@ -204,8 +205,33 @@ frameworkDetailView(config, context); } }); + }, + + /** + * @function editService + * @memberof cfServiceInstanceService + * @description Edit a service instance. + * @param {string} cnsiGuid - the CNSI guid + * @param {string} serviceInstance - the service instance + * @param {function=} callbackFunc - an optional callback function + * @returns {promise} The confirm dialog promise object + * @public + */ + editService: function (cnsiGuid, serviceInstance, callbackFunc) { + return cfServiceCreateServiceInstanceWorkflow.edit( + cnsiGuid, + serviceInstance + ).then(function () { + appNotificationsService.notify('success', $translate.instant('app.app-info.app-tabs.services.edit.success')); + if (angular.isDefined(callbackFunc)) { + callbackFunc(); + } + }).catch(function (error) { + $log.error('Failed to update service instance: ', error); + // Swallow error in rejected promise (most likely a failed http response) to ensure default msg is used + return $q.reject(); + }); } }; } - })(); diff --git a/components/cloud-foundry/frontend/src/view/applications/workflows/create-service-instance/create-service-instance.scss b/components/cloud-foundry/frontend/src/view/applications/workflows/create-service-instance/create-service-instance.scss index 1fbb39a0a1..81756b4e5d 100644 --- a/components/cloud-foundry/frontend/src/view/applications/workflows/create-service-instance/create-service-instance.scss +++ b/components/cloud-foundry/frontend/src/view/applications/workflows/create-service-instance/create-service-instance.scss @@ -89,4 +89,13 @@ form .form-group { } } +} + +.edit-service-instance .async-dialog { + min-height: 400px; + width: 600px; +} + +.modal.detail-view.detail-view-dialog.create-service-instance-dialog.edit-service-instance>.modal-dialog .async-dialog .detail-view-content>ng-include { + flex: 0; } \ No newline at end of file diff --git a/components/cloud-foundry/frontend/src/view/applications/workflows/create-service-instance/create-service-instance.service.js b/components/cloud-foundry/frontend/src/view/applications/workflows/create-service-instance/create-service-instance.service.js index 9ad7772094..835bfc922d 100644 --- a/components/cloud-foundry/frontend/src/view/applications/workflows/create-service-instance/create-service-instance.service.js +++ b/components/cloud-foundry/frontend/src/view/applications/workflows/create-service-instance/create-service-instance.service.js @@ -16,6 +16,7 @@ */ function cfServiceCreateServiceInstanceWorkflow($q, modelManager, frameworkAsyncTaskDialog) { var instanceModel = modelManager.retrieve('cloud-foundry.model.service-instance'); + var serviceModel = modelManager.retrieve('cloud-foundry.model.service'); /** * @function addService @@ -80,6 +81,83 @@ }, doCreate ).result; + }, + + // function asyncDialog(config, context, submitAction, invalidityCheck, initPromise) { + + edit: function (cnsiGuid, serviceInstance) { + var path = 'plugins/cloud-foundry/view/applications/workflows/create-service-instance/create-service-instance.html'; + var options = { + instanceNames: [], + servicePlans: [], + userInput: { + plan: null + } + }; + + var doEdit = function () { + var instanceData = { + name: options.userInput.name, + tags: _.map(options.userInput.tags, function (tag) { return tag.text; }) + }; + + if (serviceInstance.entity.service_plan_guid !== options.userInput.plan.metadata.guid) { + instanceData.service_plan_guid = options.userInput.plan.metadata.guid; + } + + if (options.userInput.params) { + instanceData.parameters = options.userInput.params; + } + return instanceModel.updateServiceInstance(cnsiGuid, serviceInstance.metadata.guid, instanceData); + }; + + var doInitEdit = function () { + options.userInput = { + name: serviceInstance.entity.name, + tags: _.map(serviceInstance.entity.tags, function (tag) { + return { + text: tag + }; + }) + }; + + return serviceModel.allServicePlans(cnsiGuid, serviceInstance.entity.service_guid).then(function (servicePlans) { + var plans = _.map(servicePlans, function (o) { return { label: o.entity.name, value: o }; }); + [].push.apply(options.servicePlans, plans); + + if (options.servicePlans.length) { + options.userInput.plan = options.servicePlans[0].value; + } + + // Get all of the instances so that we can check for name uniqueness + var queryOptions = { q: 'space_guid:' + serviceInstance.entity.space_guid }; + return instanceModel.all(cnsiGuid, queryOptions).then(function (spaceInstances) { + options.instanceNames = _.map(spaceInstances, function (instance) { return instance.entity.name; }); + }); + }); + }; + + return frameworkAsyncTaskDialog( + { + title: 'app.app-info.app-tabs.services.edit.title', + templateUrl: path, + submitCommit: true, + buttonTitles: { + submit: 'app.app-info.app-tabs.services.edit.button.yes' + }, + class: 'dialog-form-larger create-service-instance-dialog edit-service-instance', + dialog: true, + allowEnter: function () { + return false; + } + }, + { + options: options + }, + doEdit, + undefined, + doInitEdit + ).result; } }; } diff --git a/components/cloud-foundry/frontend/src/view/dashboard/cluster/organization/space/detail/services/space-services.module.js b/components/cloud-foundry/frontend/src/view/dashboard/cluster/organization/space/detail/services/space-services.module.js index b13738e2bc..df1726c731 100644 --- a/components/cloud-foundry/frontend/src/view/dashboard/cluster/organization/space/detail/services/space-services.module.js +++ b/components/cloud-foundry/frontend/src/view/dashboard/cluster/organization/space/detail/services/space-services.module.js @@ -86,6 +86,13 @@ function getInitialActions() { return [ + { + name: 'cf.space-info.edit-service-action', + disabled: false, + execute: function (serviceInstance) { + cfServiceInstanceService.editService(vm.clusterGuid, serviceInstance, _.bind(update, vm, serviceInstance)); + } + }, { name: 'cf.space-info.delete-service-action', disabled: false, @@ -127,8 +134,8 @@ _.forEach(serviceInstances, function (si) { if (vm.canDeleteOrUnbind) { vm.actionsPerSI[si.metadata.guid] = vm.actionsPerSI[si.metadata.guid] || getInitialActions(); - vm.actionsPerSI[si.metadata.guid][0].disabled = _.get(si.entity.service_bindings, 'length', 0) > 0 || !canDelete; - vm.actionsPerSI[si.metadata.guid][1].disabled = _.get(si.entity.service_bindings, 'length', 0) < 1 || !canUnbind; + vm.actionsPerSI[si.metadata.guid][1].disabled = _.get(si.entity.service_bindings, 'length', 0) > 0 || !canDelete; + vm.actionsPerSI[si.metadata.guid][2].disabled = _.get(si.entity.service_bindings, 'length', 0) < 1 || !canUnbind; } else { delete vm.actionsPerSI[si.metadata.guid]; }