提交 d7b0cec8 编写于 作者: P Piotr Bryk

Frontend for generic delete menu on list views (#735)

Please sync and test this :)
上级 5b450ece
......@@ -29,6 +29,9 @@ type EventList struct {
// Event is a single event representation.
type Event struct {
ObjectMeta ObjectMeta `json:"objectMeta"`
TypeMeta TypeMeta `json:"typeMeta"`
// A human-readable description of the status of related object.
Message string `json:"message"`
......
......@@ -92,6 +92,7 @@ const (
ResourceKindService = "service"
ResourceKindDeployment = "deployment"
ResourceKindPod = "pod"
ResourceKindEvent = "event"
ResourceKindReplicationController = "replicationcontroller"
)
......@@ -101,6 +102,7 @@ const (
var kindToAPIPathMapping = map[string]string{
ResourceKindService: "services",
ResourceKindPod: "pods",
ResourceKindEvent: "events",
ResourceKindReplicationController: "replicationcontrollers",
ResourceKindDeployment: "deployments",
ResourceKindReplicaSet: "replicasets",
......
......@@ -79,6 +79,8 @@ func GetPodsEvents(client client.Interface, namespace string, resourceSelector m
func AppendEvents(source []api.Event, target common.EventList) common.EventList {
for _, event := range source {
target.Events = append(target.Events, common.Event{
ObjectMeta: common.NewObjectMeta(event.ObjectMeta),
TypeMeta: common.NewTypeMeta(common.ResourceKindEvent),
Message: event.Message,
SourceComponent: event.Source.Component,
SourceHost: event.Source.Host,
......
......@@ -86,6 +86,8 @@ backendApi.EventList;
/**
* @typedef {{
* objectMeta: !backendApi.ObjectMeta,
* typeMeta: !backendApi.TypeMeta,
* message: string,
* sourceComponent: string,
* sourceHost: string,
......
......@@ -27,6 +27,12 @@ export class ResourceCardController {
*/
this.objectMeta;
/**
* Initialized from a binding.
* @export {!backendApi.TypeMeta}
*/
this.typeMeta;
/**
* Initialized from require just before $onInit is called.
* @export {!./resourcecardlist_component.ResourceCardListController}
......@@ -34,6 +40,17 @@ export class ResourceCardController {
this.resourceCardListCtrl;
}
/** @export */
$onInit() {
if (!this.objectMeta) {
throw new Error('object-meta binding is required for resource card component');
}
if (!this.typeMeta) {
throw new Error('type-meta binding is required for resource card component');
}
}
/**
* @return {boolean}
* @export
......@@ -54,8 +71,10 @@ export class ResourceCardController {
*/
export const resourceCardComponent = {
bindings: {
/** type {!backendApi.ObjectMeta} Metadata of the resource displayed in the card. */
/** type {!backendApi.ObjectMeta} Object metadata of the resource displayed in the card. */
'objectMeta': '<',
/** type {!backendApi.TypeMeta} Type metadata of the resource displayed in the card. */
'typeMeta': '<',
},
controller: ResourceCardController,
templateUrl: 'common/components/resourcecard/resourcecard.html',
......
......@@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import resourceModule from 'common/resource/resource_module';
import {resourceCardComponent} from './resourcecard_component';
import {resourceCardListComponent} from './resourcecardlist_component';
import {resourceCardMenuComponent} from './resourcecardmenu_component';
import {resourceCardDeleteMenuItemComponent} from './resourcecarddeletemenuitem_component';
import {resourceCardColumnComponent} from './resourcecardcolumn_component';
import {resourceCardColumnsComponent} from './resourcecardcolumns_component';
import {resourceCardHeaderColumnComponent} from './resourcecardheadercolumn_component';
......@@ -29,9 +32,13 @@ export default angular
'kubernetesDashboard.common.components.resourcecard',
[
'ngMaterial',
'ui.router',
resourceModule.name,
])
.component('kdResourceCard', resourceCardComponent)
.component('kdResourceCardList', resourceCardListComponent)
.component('kdResourceCardMenu', resourceCardMenuComponent)
.component('kdResourceCardDeleteMenuItem', resourceCardDeleteMenuItemComponent)
.component('kdResourceCardColumn', resourceCardColumnComponent)
.component('kdResourceCardColumns', resourceCardColumnsComponent)
.component('kdResourceCardHeaderColumn', resourceCardHeaderColumnComponent)
......
<!--
Copyright 2015 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-menu-item>
<md-button ng-click="$ctrl.remove()">
Delete
</md-button>
</md-menu-item>
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* Controller for the resource card delete menu item component. It deletes card's resource.
* @final
*/
export class ResourceCardDeleteMenuItemController {
/**
* @param {!./../../resource/verber_service.VerberService} kdResourceVerberService
* @param {!ui.router.$state} $state
* @param {!md.$dialog} $mdDialog
* @ngInject
*/
constructor(kdResourceVerberService, $state, $mdDialog) {
/**
* Initialized from require just before $onInit is called.
* @export {!./resourcecard_component.ResourceCardController}
*/
this.resourceCardCtrl;
/** @export {string} Initialized from a binding.*/
this.resourceKindName;
/** @private {!./../../resource/verber_service.VerberService} */
this.kdResourceVerberService_ = kdResourceVerberService;
/** @private {!ui.router.$state}} */
this.state_ = $state;
/** @private {!md.$dialog} */
this.mdDialog_ = $mdDialog;
}
/**
* @export
*/
remove() {
this.kdResourceVerberService_
.showDeleteDialog(
this.resourceKindName, this.resourceCardCtrl.typeMeta, this.resourceCardCtrl.objectMeta)
.then(
() => {
// For now just reload the state. Later we can remove the item in place.
this.state_.reload();
},
(/** angular.$http.Response|null */ err) => {
if (err) {
// Show dialog if there was an error, not user canceling dialog.
this.mdDialog_.show(this.mdDialog_.alert()
.ok('Ok')
.title(err.statusText || 'Internal server error')
.textContent(err.data || 'Could not delete the resource'));
}
});
}
}
/**
* @type {!angular.Component}
*/
export const resourceCardDeleteMenuItemComponent = {
templateUrl: 'common/components/resourcecard/resourcecarddeletemenuitem.html',
bindings: {
'resourceKindName': '@',
},
bindToController: true,
require: {
'resourceCardCtrl': '^kdResourceCard',
},
controller: ResourceCardDeleteMenuItemController,
};
<!--
Copyright 2015 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<md-menu>
<md-button ng-click="$mdOpenMenu($event)" class="md-icon-button">
<md-icon md-font-library="material-icons">more_vert</md-icon>
<md-tooltip>Actions</md-tooltip>
</md-button>
<md-menu-content ng-transclude>
</md-menu-content>
</md-menu>
......@@ -12,17 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
@import '../variables';
md-checkbox {
&.kd-deletedialog-services-checkbox {
margin: $baseline-grid 0 0 $baseline-grid;
}
}
.kd-deletedialog-info-icon {
font-size: $subhead-font-size-base;
height: $subhead-font-size-base;
line-height: $caption-font-size-base;
margin-left: $baseline-grid / 2;
}
/**
* Resource card header columns component. See resource card list for documentation.
* @type {!angular.Component}
*/
export const resourceCardMenuComponent = {
templateUrl: 'common/components/resourcecard/resourcecardmenu.html',
transclude: true,
};
......@@ -16,22 +16,15 @@ limitations under the License.
<md-dialog aria-label="Delete Replication Controller" layout="column">
<md-dialog-content layout-padding>
<h4 class="md-title">Delete Replication Controller</h4>
<h4 class="md-title">Delete a {{$ctrl.resourceKindName}}</h4>
<div>
Delete replication controller {{::ctrl.replicationController}} in namespace {{::ctrl.namespace}}.<br>
Pods managed by the replication controller will be also deleted.
Are you sure you want to delete {{$ctrl.resourceKindName}}
<kd-middle-ellipsis display-string="{{$ctrl.objectMeta.name}}"></kd-middle-ellipsis>
in namespace {{$ctrl.objectMeta.namespace}}?
</div>
<md-checkbox ng-model="ctrl.deleteServices" class="kd-deletedialog-services-checkbox">
Delete related services
<md-icon class="material-icons kd-deletedialog-info-icon">
info
<md-tooltip>Services with label selector matching only this replication controller
will be deleted</md-tooltip>
</md-icon>
</md-checkbox>
<md-dialog-actions>
<md-button class="md-primary kd-cancel-btn" ng-click="ctrl.cancel()">Cancel</md-button>
<md-button class="md-primary kd-delete-btn" ng-click="ctrl.remove()">Delete</md-button>
<md-button class="md-primary kd-cancel-btn" ng-click="$ctrl.cancel()">Cancel</md-button>
<md-button class="md-primary kd-delete-btn" ng-click="$ctrl.remove()">Delete</md-button>
</md-dialog-actions>
</md-dialog-content>
</md-dialog>
......@@ -12,31 +12,28 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import {StateParams} from './replicationcontrollerdetail_state';
import {getReplicationControllerDetailsResource} from './replicationcontrollerdetail_stateconfig';
/**
* Controller for the delete replication controller dialog.
* Controller for the delete resource dialog.
*
* @final
*/
export default class DeleteReplicationControllerDialogController {
export class DeleteResourceController {
/**
* @param {!md.$dialog} $mdDialog
* @param {!angular.$resource} $resource
* @param {string} namespace
* @param {string} replicationController
* @param {!backendApi.TypeMeta} typeMeta
* @param {!backendApi.ObjectMeta} objectMeta
* @ngInject
*/
constructor($mdDialog, $resource, namespace, replicationController) {
constructor($mdDialog, $resource, resourceKindName, typeMeta, objectMeta) {
/** @export {string} */
this.replicationController = replicationController;
this.resourceKindName = resourceKindName;
/** @export {string} */
this.namespace = namespace;
/** @private {!backendApi.TypeMeta} */
this.typeMeta_ = typeMeta;
/** @export {boolean} */
this.deleteServices = true;
/** @export {!backendApi.ObjectMeta} */
this.objectMeta = objectMeta;
/** @private {!md.$dialog} */
this.mdDialog_ = $mdDialog;
......@@ -46,22 +43,12 @@ export default class DeleteReplicationControllerDialogController {
}
/**
* Deletes the replication controller and closes the dialog.
*
* @export
*/
remove() {
let resource = getReplicationControllerDetailsResource(
new StateParams(this.namespace, this.replicationController), this.resource_);
/** @type {!backendApi.DeleteReplicationControllerSpec} */
let deleteReplicationControllerSpec = {
deleteServices: this.deleteServices,
};
resource.remove(
deleteReplicationControllerSpec, () => { this.mdDialog_.hide(); },
() => { this.mdDialog_.cancel(); });
let resource = this.resource_(
`api/v1/${this.typeMeta_.kind}/namespace/${this.objectMeta.namespace}/name/${this.objectMeta.name}`);
resource.remove(this.mdDialog_.hide, this.mdDialog_.cancel);
}
/**
......
......@@ -12,24 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import DeleteReplicationControllerDialogController from './deletereplicationcontroller_controller';
import {DeleteResourceController} from './deleteresource_controller';
/**
* @param {!md.$dialog} mdDialog
* @param {string} namespace
* @param {string} replicationController
* @param {string} resourceKindName
* @param {!backendApi.TypeMeta} typeMeta
* @param {!backendApi.ObjectMeta} objectMeta
* @return {!angular.$q.Promise}
*/
export default function showDeleteReplicationControllerDialog(
mdDialog, namespace, replicationController) {
export default function showDeleteDialog(mdDialog, resourceKindName, typeMeta, objectMeta) {
return mdDialog.show({
controller: DeleteReplicationControllerDialogController,
controllerAs: 'ctrl',
controller: DeleteResourceController,
controllerAs: '$ctrl',
clickOutsideToClose: true,
templateUrl: 'replicationcontrollerdetail/deletereplicationcontroller.html',
templateUrl: 'common/resource/deleteresource.html',
locals: {
'namespace': namespace,
'replicationController': replicationController,
'typeMeta': typeMeta,
'objectMeta': objectMeta,
'resourceKindName': resourceKindName,
},
});
}
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {VerberService} from './verber_service';
/**
* Angular module containing navigation chrome for the application.
*/
export default angular
.module(
'kubernetesDashboard.common.resource',
[
'ngMaterial',
'ngResource',
])
.service('kdResourceVerberService', VerberService);
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import showDeleteDialog from './deleteresource_dialog';
/**
* Verber service for performing common verb operations on resources, e.g., deleting or editing
* them.
* @final
*/
export class VerberService {
/**
* @param {!md.$dialog} $mdDialog
* @ngInject
*/
constructor($mdDialog) {
/** @private {!md.$dialog} */
this.mdDialog_ = $mdDialog;
}
/**
* Opens a resource delete dialog. Returns a promise that is resolved/rejected when
* user wants to delete the resource. Nothing happens when user clicks cancel on the dialog.
* @param {string} resourceKindName
* @param {!backendApi.TypeMeta} typeMeta
* @param {!backendApi.ObjectMeta} objectMeta
* @return {!angular.$q.Promise}
*/
showDeleteDialog(resourceKindName, typeMeta, objectMeta) {
return showDeleteDialog(this.mdDialog_, resourceKindName, typeMeta, objectMeta);
}
}
......@@ -60,6 +60,12 @@ limitations under the License.
<kd-middle-ellipsis display-string="{{::image}}"></kd-middle-ellipsis>
</div>
</kd-resource-card-column>
<kd-resource-card-column class="kd-row-layout-column kd-icon-column">
<kd-resource-card-menu>
<kd-resource-card-delete-menu-item resource-kind-name="Deployment">
</kd-resource-card-delete-menu-item>
</kd-resource-card-menu>
</kd-resource-card-column>
</kd-resource-card-columns>
<kd-resource-card-footer ng-if="::$ctrl.hasWarnings()">
<div ng-repeat="warning in ::$ctrl.deployment.pods.warnings">
......
......@@ -31,6 +31,8 @@ limitations under the License.
<kd-resource-card-header-column>
Images
</kd-resource-card-header-column>
<kd-resource-card-header-column size="small" grow="nogrow">
</kd-resource-card-header-column>
</kd-resource-card-header-columns>
<kd-deployment-card ng-repeat="deployment in $ctrl.deployments" deployment="deployment">
</kd-deployment-card>
......
......@@ -41,7 +41,8 @@ limitations under the License.
<kd-resource-card-header-column>Last seen</kd-resource-card-header-column>
</kd-resource-card-header-columns>
<kd-resource-card ng-repeat="event in $ctrl.filteredEvents">
<kd-resource-card ng-repeat="event in $ctrl.filteredEvents"
object-meta="event.objectMeta" type-meta="event.typeMeta">
<kd-resource-card-status>
<i ng-if="$ctrl.isEventWarning(event)"
class="material-icons kd-replicationcontrollerevents-warning-icon">warning</i>
......
......@@ -25,9 +25,12 @@ limitations under the License.
<kd-resource-card-header-column>Age</kd-resource-card-header-column>
<kd-resource-card-header-column>Cluster IP</kd-resource-card-header-column>
<kd-resource-card-header-column>Logs</kd-resource-card-header-column>
<kd-resource-card-header-column size="small" grow="nogrow">
</kd-resource-card-header-column>
</kd-resource-card-header-columns>
<kd-resource-card ng-repeat="pod in $ctrl.podList.pods">
<kd-resource-card ng-repeat="pod in $ctrl.podList.pods"
type-meta="pod.typeMeta" object-meta="pod.objectMeta">
<kd-resource-card-columns>
<kd-resource-card-column>
<div>
......@@ -59,6 +62,12 @@ limitations under the License.
</div>
<div ng-hide="::$ctrl.getPodLogsHref(pod)">-</div>
</kd-resource-card-column>
<kd-resource-card-column class="kd-row-layout-column kd-icon-column">
<kd-resource-card-menu>
<kd-resource-card-delete-menu-item resource-kind-name="Pod">
</kd-resource-card-delete-menu-item>
</kd-resource-card-menu>
</kd-resource-card-column>
</kd-resource-card-columns>
</kd-resource-card>
</kd-resource-card-list>
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<kd-resource-card>
<kd-resource-card object-meta="$ctrl.replicaSet.objectMeta" type-meta="$ctrl.replicaSet.typeMeta">
<kd-resource-card-status layout="row">
<md-icon class="material-icons md-warn"
ng-if="::$ctrl.hasWarnings()">
......@@ -61,6 +61,12 @@ limitations under the License.
<kd-middle-ellipsis display-string="{{::image}}"></kd-middle-ellipsis>
</div>
</kd-resource-card-column>
<kd-resource-card-column class="kd-row-layout-column kd-icon-column">
<kd-resource-card-menu>
<kd-resource-card-delete-menu-item resource-kind-name="Replica Set">
</kd-resource-card-delete-menu-item>
</kd-resource-card-menu>
</kd-resource-card-column>
</kd-resource-card-columns>
<kd-resource-card-footer ng-if="::$ctrl.hasWarnings()">
<div ng-repeat="warning in ::$ctrl.replicaSet.pods.warnings">
......
......@@ -31,6 +31,8 @@ limitations under the License.
<kd-resource-card-header-column>
Images
</kd-resource-card-header-column>
<kd-resource-card-header-column size="small" grow="nogrow">
</kd-resource-card-header-column>
</kd-resource-card-header-columns>
<kd-replica-set-card ng-repeat="rc in $ctrl.replicaSets"
replica-set="rc">
......
......@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import showDeleteReplicationControllerDialog from './deletereplicationcontroller_dialog';
import showUpdateReplicasDialog from './updatereplicas_dialog';
/**
......@@ -23,24 +22,25 @@ import showUpdateReplicasDialog from './updatereplicas_dialog';
export class ReplicationControllerService {
/**
* @param {!md.$dialog} $mdDialog
* @param {!./../common/resource/verber_service.VerberService} kdResourceVerberService
* @ngInject
*/
constructor($mdDialog) {
constructor($mdDialog, kdResourceVerberService) {
/** @private {!md.$dialog} */
this.mdDialog_ = $mdDialog;
/** @private {!./../common/resource/verber_service.VerberService}*/
this.kdResourceVerberService_ = kdResourceVerberService;
}
/**
* Opens a replication controller delete dialog. Returns a promise that is resolved/rejected when
* user wants
* to delete the replica. Nothing happens when user clicks cancel on the dialog.
*
* @param {string} namespace
* @param {string} replicationController
* @param {!backendApi.TypeMeta} typeMeta
* @param {!backendApi.ObjectMeta} objectMeta
* @return {!angular.$q.Promise}
*/
showDeleteDialog(namespace, replicationController) {
return showDeleteReplicationControllerDialog(this.mdDialog_, namespace, replicationController);
showDeleteDialog(typeMeta, objectMeta) {
return this.kdResourceVerberService_.showDeleteDialog(
'Replication Controller', typeMeta, objectMeta);
}
/**
......
......@@ -14,6 +14,7 @@
import componentsModule from 'common/components/components_module';
import eventsModule from 'events/events_module';
import resourceModule from 'common/resource/resource_module';
import filtersModule from 'common/filters/filters_module';
import logsModule from 'logs/logs_module';
import podListModule from 'podlist/podlist_module';
......@@ -40,6 +41,7 @@ export default angular
podListModule.name,
serviceListModule.name,
eventsModule.name,
resourceModule.name,
])
.config(stateConfig)
.component('kdReplicationControllerInfo', replicationControllerInfoComponent)
......
......@@ -82,7 +82,7 @@ export default class ReplicationControllerDetailActionBarController {
*/
handleDeleteReplicationControllerDialog() {
this.kdReplicationControllerService_
.showDeleteDialog(this.stateParams_.namespace, this.stateParams_.replicationController)
.showDeleteDialog(this.details_.typeMeta, this.details_.objectMeta)
.then(this.onReplicationControllerDeleteSuccess_.bind(this));
}
......
......@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<kd-resource-card>
<kd-resource-card object-meta="$ctrl.replicationController.objectMeta"
type-meta="$ctrl.replicationController.typeMeta">
<kd-resource-card-status layout="row">
<md-icon class="material-icons kd-error" ng-if="::$ctrl.hasWarnings()">
error
......
......@@ -14,26 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<md-menu>
<md-button ng-click="$ctrl.openMenu($mdOpenMenu, $event)" class="md-icon-button">
<md-icon md-font-library="material-icons">more_vert</md-icon>
<md-tooltip>Actions</md-tooltip>
</md-button>
<md-menu-content width="3">
<md-menu-item>
<md-button ng-click="$ctrl.viewDetails()">
View details
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="$ctrl.showUpdateReplicasDialog()">
Edit pod count
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="$ctrl.showDeleteDialog()">
Delete
</md-button>
</md-menu-item>
</md-menu-content>
<kd-resource-card-menu>
<md-menu-item>
<md-button ng-click="$ctrl.viewDetails()">
View details
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="$ctrl.showUpdateReplicasDialog()">
Edit pod count
</md-button>
</md-menu-item>
<kd-resource-card-delete-menu-item resource-kind-name="Replication Controller">
</kd-resource-card-delete-menu-item>
</md-menu>
......@@ -61,17 +61,6 @@ export default class ReplicationControllerCardMenuController {
this.replicationController.objectMeta.name));
}
/**
* @export
*/
showDeleteDialog() {
this.kdReplicationControllerService_
.showDeleteDialog(
this.replicationController.objectMeta.namespace,
this.replicationController.objectMeta.name)
.then(() => this.state_.reload());
}
/**
* @export
*/
......
......@@ -62,13 +62,12 @@ export default class ReplicationControllerCardMenuController {
}
/**
* @export
*/
* @export
*/
showDeleteDialog() {
this.kdReplicationControllerService_
.showDeleteDialog(
this.replicationController.objectMeta.namespace,
this.replicationController.objectMeta.name)
this.replicationController.typeMeta, this.replicationController.objectMeta)
.then(() => this.state_.reload());
}
......
......@@ -21,9 +21,12 @@ limitations under the License.
<kd-resource-card-header-column>Cluster IP</kd-resource-card-header-column>
<kd-resource-card-header-column>Internal endpoints</kd-resource-card-header-column>
<kd-resource-card-header-column>External endpoints</kd-resource-card-header-column>
<kd-resource-card-header-column size="small" grow="nogrow">
</kd-resource-card-header-column>
</kd-resource-card-header-columns>
<kd-resource-card ng-repeat="service in ::$ctrl.services">
<kd-resource-card ng-repeat="service in ::$ctrl.services"
object-meta="service.objectMeta" type-meta="service.typeMeta">
<kd-resource-card-columns>
<kd-resource-card-column>
<a ng-href="{{::$ctrl.getServiceDetailHref(service)}}">
......@@ -47,6 +50,12 @@ limitations under the License.
</div>
<div ng-hide="service.externalEndpoints">-</div>
</kd-resource-card-column>
<kd-resource-card-column class="kd-row-layout-column kd-icon-column">
<kd-resource-card-menu>
<kd-resource-card-delete-menu-item resource-kind-name="Service">
</kd-resource-card-delete-menu-item>
</kd-resource-card-menu>
</kd-resource-card-column>
</kd-resource-card-columns>
</kd-resource-card>
</kd-resource-card-list>
......@@ -188,6 +188,8 @@ func TestAppendEvents(t *testing.T) {
Namespace: "test-namespace",
Events: []common.Event{
{
ObjectMeta: common.ObjectMeta{Name: "my-event", Namespace: "test-namespace"},
TypeMeta: common.TypeMeta{common.ResourceKindEvent},
Message: "my-event-msg",
SourceComponent: "my-event-src-component",
SourceHost: "my-event-src-host",
......@@ -211,7 +213,7 @@ func TestAppendEvents(t *testing.T) {
for _, c := range cases {
actual := AppendEvents(c.source, c.target)
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("AppendEvents(%#v, %#v) == %#v, expected %#v",
t.Errorf("AppendEvents(%#v, %#v) == \n%#v, expected \n%#v",
c.source, c.target, actual, c.expected)
}
}
......
......@@ -49,8 +49,9 @@ func TestGetReplicaSetEvents(t *testing.T) {
&common.EventList{
Namespace: "test-namespace",
Events: []common.Event{{
Message: "test-message",
Type: api.EventTypeNormal,
TypeMeta: common.TypeMeta{common.ResourceKindEvent},
Message: "test-message",
Type: api.EventTypeNormal,
}}},
},
}
......
......@@ -12,88 +12,48 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import DeleteReplicationControllerDialogController from 'replicationcontrollerdetail/deletereplicationcontroller_controller';
import replicationControllerDetailModule from 'replicationcontrollerdetail/replicationcontrollerdetail_module';
import resourceModule from 'common/resource/resource_module';
import {DeleteResourceController} from 'common/resource/deleteresource_controller';
describe('Delete replication controller dialog controller', () => {
/** @type {!DeleteReplicationControllerDialogController} */
describe('Delete resource controller', () => {
/** @type !{!common/resource/deleteresource_controller.DeleteResourceController} */
let ctrl;
/** @type {!md.$dialog} */
let mdDialog;
/** @type {!angular.$httpBackend} */
let httpBackend;
let namespaceMock = 'foo-namespace';
let replicationControllerMock = 'foo-name';
beforeEach(() => angular.mock.module(resourceModule.name));
beforeEach(() => {
angular.mock.module(replicationControllerDetailModule.name);
angular.mock.inject(($log, $mdDialog, $controller, $httpBackend) => {
mdDialog = $mdDialog;
httpBackend = $httpBackend;
ctrl = $controller(DeleteReplicationControllerDialogController, {
namespace: namespaceMock,
replicationController: replicationControllerMock,
});
beforeEach(angular.mock.inject(($controller, $mdDialog, $httpBackend) => {
ctrl = $controller(DeleteResourceController, {
resourceKindName: 'My Resource',
objectMeta: {name: 'Foo', namespace: 'Bar'},
typeMeta: {kind: 'qux'},
});
});
mdDialog = $mdDialog;
httpBackend = $httpBackend;
}));
it('should cancel', () => {
// given
spyOn(mdDialog, 'cancel');
// when
ctrl.cancel();
// then
expect(mdDialog.cancel).toHaveBeenCalled();
});
it('should delete without services', () => {
// given
it('should delete resource', () => {
spyOn(mdDialog, 'hide');
ctrl.deleteServices = false;
// when
httpBackend
.whenDELETE('api/v1/replicationcontrollers/foo-namespace/foo-name?deleteServices=false')
.respond(200, {});
ctrl.remove();
httpBackend.expectDELETE('api/v1/qux/namespace/Bar/name/Foo').respond(200, {ok: 'ok'});
httpBackend.flush();
// then
expect(mdDialog.hide).toHaveBeenCalled();
});
it('should delete with services', () => {
// given
spyOn(mdDialog, 'hide');
// when
httpBackend
.whenDELETE('api/v1/replicationcontrollers/foo-namespace/foo-name?deleteServices=true')
.respond(200, {});
it('should propagate errors on delete', () => {
spyOn(mdDialog, 'cancel');
ctrl.remove();
httpBackend.expectDELETE('api/v1/qux/namespace/Bar/name/Foo').respond(500, {err: 'err'});
httpBackend.flush();
// then
expect(mdDialog.hide).toHaveBeenCalled();
expect(mdDialog.cancel).toHaveBeenCalled();
});
it('should cancel on delete failure', () => {
// given
it('should cancel', () => {
spyOn(mdDialog, 'cancel');
// when
httpBackend
.whenDELETE('api/v1/replicationcontrollers/foo-namespace/foo-name?deleteServices=true')
.respond(503, {});
ctrl.remove();
httpBackend.flush();
// then
ctrl.cancel();
expect(mdDialog.cancel).toHaveBeenCalled();
});
});
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import resourceModule from 'common/resource/resource_module';
describe('Verber service', () => {
/** @type !{!common/resource/verber_service.VerberService} */
let verber;
/** @type {!md.$dialog} */
let mdDialog;
beforeEach(() => angular.mock.module(resourceModule.name));
beforeEach(angular.mock.inject((kdResourceVerberService, $mdDialog) => {
verber = kdResourceVerberService;
mdDialog = $mdDialog;
}));
it('should show delete dialog resource', () => {
let promise = {};
spyOn(mdDialog, 'show').and.returnValue(promise);
let actual = verber.showDeleteDialog('Foo resource', {foo: 'bar'}, {baz: 'qux'});
expect(mdDialog.show).toHaveBeenCalledWith(jasmine.objectContaining({
locals: {
'resourceKindName': 'Foo resource',
'typeMeta': {foo: 'bar'},
'objectMeta': {baz: 'qux'},
},
}));
expect(actual).toBe(promise);
});
});
// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import resourceCardModule from 'common/components/resourcecard/resourcecard_module';
describe('Delete resource menu item', () => {
/** @type
* {!common/components/resourcecard/resourcecarddeletemenuitem_component.ResourceCardDeleteMenuItemController}
*/
let ctrl;
/** @type {!angular.$q} */
let q;
/** @type {!angular.Scope} */
let scope;
/** @type {!ui.router.$state} */
let state;
/** @type {!common/resource/verber_service.VerberService} */
let kdResourceVerberService;
/** @type {!md.$dialog}*/
let mdDialog;
beforeEach(() => {
angular.mock.module(resourceCardModule.name);
angular.mock.inject(
($rootScope, $componentController, _kdResourceVerberService_, $q, $state, $mdDialog) => {
ctrl = $componentController('kdResourceCardDeleteMenuItem');
ctrl.resourceCardCtrl = {
objectMeta: {name: 'foo-name', namespace: 'foo-namespace'},
typeMeta: {kind: 'foo'},
};
state = $state;
kdResourceVerberService = _kdResourceVerberService_;
scope = $rootScope;
q = $q;
mdDialog = $mdDialog;
});
});
it('should delete the resource', () => {
let deferred = q.defer();
spyOn(kdResourceVerberService, 'showDeleteDialog').and.returnValue(deferred.promise);
spyOn(state, 'reload');
ctrl.remove();
expect(state.reload).not.toHaveBeenCalled();
deferred.resolve();
scope.$digest();
expect(state.reload).toHaveBeenCalled();
});
it('should ignore cancels', () => {
let deferred = q.defer();
spyOn(kdResourceVerberService, 'showDeleteDialog').and.returnValue(deferred.promise);
spyOn(state, 'reload');
spyOn(mdDialog, 'alert').and.callThrough();
ctrl.remove();
deferred.reject();
scope.$digest();
expect(state.reload).not.toHaveBeenCalled();
expect(mdDialog.alert).not.toHaveBeenCalled();
});
it('should show alert window on error', () => {
let deferred = q.defer();
spyOn(kdResourceVerberService, 'showDeleteDialog').and.returnValue(deferred.promise);
spyOn(state, 'reload');
spyOn(mdDialog, 'alert').and.callThrough();
ctrl.remove();
deferred.reject({data: 'foo-data', statusText: 'foo-text'});
scope.$digest();
expect(state.reload).not.toHaveBeenCalled();
expect(mdDialog.alert).toHaveBeenCalled();
});
});
......@@ -43,7 +43,7 @@ describe('Resource card list', () => {
LABELS_COLUMN
</kd-resource-card-header-column>
</kd-resource-card-header-columns>
<kd-resource-card>
<kd-resource-card object-meta="{}" type-meta="{}">
<kd-resource-card-status>STATUS</kd-resource-card-status>
<kd-resource-card-columns>
<kd-resource-card-column>
......@@ -58,7 +58,7 @@ describe('Resource card list', () => {
</kd-resource-card-columns>
<kd-resource-card-footer>FOOTER</kd-resource-card-footer>
</kd-resource-card>
<kd-resource-card>
<kd-resource-card object-meta="{}" type-meta="{}">
<kd-resource-card-status>STATUS</kd-resource-card-status>
<kd-resource-card-columns>
<kd-resource-card-column>
......@@ -101,4 +101,50 @@ describe('Resource card list', () => {
scope.$digest();
expect(elem.html()).toContain('md-checkbox');
});
it('should throw an error when no object meta', () => {
let compileFn = compile(`
<kd-resource-card-list selectable="selectable" with-statuses="withStatuses">
<kd-resource-card-header-columns>
<kd-resource-card-header-column size="small" grow="nogrow">
NAME_COLUMN
</kd-resource-card-header-column>
</kd-resource-card-header-columns>
<kd-resource-card type-meta="{}">
<kd-resource-card-columns>
<kd-resource-card-column>
FIRST_COLUMN1
</kd-resource-card-column>
</kd-resource-card-columns>
</kd-resource-card>
</kd-resource-card-list>
`);
compileFn(scope);
expect(scope.$digest)
.toThrow(new Error('object-meta binding is required for resource card component'));
});
it('should throw an error when no type meta', () => {
let compileFn = compile(`
<kd-resource-card-list selectable="selectable" with-statuses="withStatuses">
<kd-resource-card-header-columns>
<kd-resource-card-header-column size="small" grow="nogrow">
NAME_COLUMN
</kd-resource-card-header-column>
</kd-resource-card-header-columns>
<kd-resource-card object-meta="{}">
<kd-resource-card-columns>
<kd-resource-card-column>
FIRST_COLUMN1
</kd-resource-card-column>
</kd-resource-card-columns>
</kd-resource-card>
</kd-resource-card-list>
`);
compileFn(scope);
expect(scope.$digest)
.toThrow(new Error('type-meta binding is required for resource card component'));
});
});
......@@ -22,8 +22,6 @@ describe('Replication controller card menu controller', () => {
let ctrl;
/** @type {!ui.router.$state} */
let state;
/** @type {!angular.Scope} */
let scope;
/** @type
* {!replicationcontrollerdetail/replicationcontroller_service.ReplicationControllerService} */
let kdReplicationControllerService;
......@@ -31,15 +29,13 @@ describe('Replication controller card menu controller', () => {
beforeEach(() => {
angular.mock.module(replicationControllerListModule.name);
angular.mock.inject(
($componentController, $state, _kdReplicationControllerService_, $rootScope) => {
state = $state;
kdReplicationControllerService = _kdReplicationControllerService_;
scope = $rootScope;
ctrl = $componentController('kdReplicationControllerCardMenu', null, {
replicationController: {objectMeta: {name: 'foo-name', namespace: 'foo-namespace'}},
});
});
angular.mock.inject(($componentController, $state, _kdReplicationControllerService_) => {
state = $state;
kdReplicationControllerService = _kdReplicationControllerService_;
ctrl = $componentController('kdReplicationControllerCardMenu', null, {
replicationController: {objectMeta: {name: 'foo-name', namespace: 'foo-namespace'}},
});
});
});
it('should view details', () => {
......@@ -66,22 +62,6 @@ describe('Replication controller card menu controller', () => {
expect(openMenuFn).toHaveBeenCalledWith(event);
});
it('should reload on successful delete', angular.mock.inject(($q) => {
// given
let deferred = $q.defer();
spyOn(state, 'reload');
spyOn(kdReplicationControllerService, 'showDeleteDialog').and.returnValue(deferred.promise);
// when
ctrl.showDeleteDialog();
deferred.resolve();
// then
expect(state.reload).not.toHaveBeenCalled();
scope.$apply();
expect(state.reload).toHaveBeenCalled();
}));
it('should show update replicas dialog', () => {
// given
ctrl.replicationController = {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册