diff --git a/src/frontend/app/features/applications/application-delete/delete-app-instances/app-delete-instances-routes-list-config.service.ts b/src/frontend/app/features/applications/application-delete/delete-app-instances/app-delete-instances-routes-list-config.service.ts index 9143a2a0f5..bd6d37447d 100644 --- a/src/frontend/app/features/applications/application-delete/delete-app-instances/app-delete-instances-routes-list-config.service.ts +++ b/src/frontend/app/features/applications/application-delete/delete-app-instances/app-delete-instances-routes-list-config.service.ts @@ -1,23 +1,50 @@ import { DatePipe } from '@angular/common'; import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; +import { Observable, of as observableOf } from 'rxjs'; +import { first, map } from 'rxjs/operators'; +import { IServiceBinding } from '../../../../core/cf-api-svc.types'; +import { CurrentUserPermissionsService } from '../../../../core/current-user-permissions.service'; +import { RowState } from '../../../../shared/components/list/data-sources-controllers/list-data-source-types'; import { AppServiceBindingListConfigService, } from '../../../../shared/components/list/list-types/app-sevice-bindings/app-service-binding-list-config.service'; import { ListViewTypes } from '../../../../shared/components/list/list.component.types'; +import { PaginationMonitorFactory } from '../../../../shared/monitors/pagination-monitor.factory'; +import { FetchAllServiceBindings } from '../../../../store/actions/service-bindings.actions'; import { AppState } from '../../../../store/app-state'; +import { entityFactory, serviceSchemaKey } from '../../../../store/helpers/entity-factory'; +import { createEntityRelationPaginationKey } from '../../../../store/helpers/entity-relations/entity-relations.types'; +import { getPaginationObservables } from '../../../../store/reducers/pagination-reducer/pagination-reducer.helper'; +import { APIResource } from '../../../../store/types/api.types'; +import { QParam } from '../../../../store/types/pagination.types'; import { ApplicationService } from '../../application.service'; -import { CurrentUserPermissionsService } from '../../../../core/current-user-permissions.service'; @Injectable() export class AppDeleteServiceInstancesListConfigService extends AppServiceBindingListConfigService { hideRefresh: boolean; allowSelection: boolean; + obsCache: { [serviceGuid: string]: Observable } = {}; + + static createFetchServiceBinding = (cfGuid: string, serviceInstanceGuid: string): FetchAllServiceBindings => { + const action = new FetchAllServiceBindings( + cfGuid, + createEntityRelationPaginationKey(serviceSchemaKey, serviceInstanceGuid), + ); + action.initialParams['results-per-page'] = 1; + action.initialParams.q = [ + new QParam('service_instance_guid', serviceInstanceGuid), + ]; + return action; + } + constructor(store: Store, appService: ApplicationService, private _datePipe: DatePipe, - private currentUserPermissionService: CurrentUserPermissionsService) { + private currentUserPermissionService: CurrentUserPermissionsService, + private paginationMonitorFactory: PaginationMonitorFactory + ) { super(store, appService, _datePipe, currentUserPermissionService); this.getGlobalActions = () => null; @@ -28,5 +55,37 @@ export class AppDeleteServiceInstancesListConfigService extends AppServiceBindin this.defaultView = 'table'; this.viewType = ListViewTypes.TABLE_ONLY; this.allowSelection = true; + + // Show a warning if there is more than one service binding associated with a service instance + this.dataSource.getRowState = (serviceBinding: APIResource): Observable => { + if (!serviceBinding) { + return observableOf({}); + } + if (!this.obsCache[serviceBinding.entity.service_instance_guid]) { + const action = AppDeleteServiceInstancesListConfigService.createFetchServiceBinding( + appService.cfGuid, + serviceBinding.entity.service_instance_guid + ); + const pagObs = getPaginationObservables({ + store, + action, + paginationMonitor: this.paginationMonitorFactory.create( + action.paginationKey, + entityFactory(action.entityKey) + ) + }); + this.obsCache[serviceBinding.entity.service_instance_guid] = pagObs.pagination$.pipe( + map(pag => ({ + message: `There are other applications bound to this service instance (${pag.totalResults - 1}).`, + warning: pag.totalResults > 1, + })) + ); + // Ensure the request is made by sub'ing to the entities observable + pagObs.entities$.pipe( + first(), + ).subscribe(); + } + return this.obsCache[serviceBinding.entity.service_instance_guid]; + }; } } diff --git a/src/frontend/app/features/service-catalog/services.service.ts b/src/frontend/app/features/service-catalog/services.service.ts index 4c12ee5090..8e6a82ebe1 100644 --- a/src/frontend/app/features/service-catalog/services.service.ts +++ b/src/frontend/app/features/service-catalog/services.service.ts @@ -149,8 +149,6 @@ export class ServicesService { ); } - - getServiceName = () => { return observableCombineLatest(this.serviceExtraInfo$, this.service$) .pipe( diff --git a/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source-types.ts b/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source-types.ts index 80937a9201..91804d5f21 100644 --- a/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source-types.ts +++ b/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source-types.ts @@ -89,6 +89,7 @@ export interface RowState { blocked?: boolean; highlighted?: boolean; deleting?: boolean; + warning?: boolean; [customState: string]: any; } diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-radio/table-cell-radio.component.ts b/src/frontend/app/shared/components/list/list-table/table-cell-radio/table-cell-radio.component.ts index 293915ac95..4be09b65db 100644 --- a/src/frontend/app/shared/components/list/list-table/table-cell-radio/table-cell-radio.component.ts +++ b/src/frontend/app/shared/components/list/list-table/table-cell-radio/table-cell-radio.component.ts @@ -1,6 +1,5 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; -import { ApplicationService } from '../../../../../features/applications/application.service'; import { TableCellCustom } from '../../list.types'; @Component({ diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-select/table-cell-select.component.scss b/src/frontend/app/shared/components/list/list-table/table-cell-select/table-cell-select.component.scss index 7a51760c21..8b13789179 100644 --- a/src/frontend/app/shared/components/list/list-table/table-cell-select/table-cell-select.component.scss +++ b/src/frontend/app/shared/components/list/list-table/table-cell-select/table-cell-select.component.scss @@ -1,3 +1 @@ -mat-checkbox { - padding: 10px; -} + diff --git a/src/frontend/app/shared/components/list/list-table/table-header-select/table-header-select.component.scss b/src/frontend/app/shared/components/list/list-table/table-header-select/table-header-select.component.scss index 7a51760c21..8b13789179 100644 --- a/src/frontend/app/shared/components/list/list-table/table-header-select/table-header-select.component.scss +++ b/src/frontend/app/shared/components/list/list-table/table-header-select/table-header-select.component.scss @@ -1,3 +1 @@ -mat-checkbox { - padding: 10px; -} + diff --git a/src/frontend/app/shared/components/list/list-table/table-row/table-row.component.scss b/src/frontend/app/shared/components/list/list-table/table-row/table-row.component.scss index 3e0708c301..8eb203bd1c 100644 --- a/src/frontend/app/shared/components/list/list-table/table-row/table-row.component.scss +++ b/src/frontend/app/shared/components/list/list-table/table-row/table-row.component.scss @@ -15,7 +15,8 @@ display: block; } } - &__errored { + &__errored, + &__warning { .table-row__error { display: flex; } diff --git a/src/frontend/app/shared/components/list/list-table/table-row/table-row.component.theme.scss b/src/frontend/app/shared/components/list/list-table/table-row/table-row.component.theme.scss index 440ec7622f..91bf05f36e 100644 --- a/src/frontend/app/shared/components/list/list-table/table-row/table-row.component.theme.scss +++ b/src/frontend/app/shared/components/list/list-table/table-row/table-row.component.theme.scss @@ -8,26 +8,30 @@ $foreground: map-get($theme, foreground); .table-row { border-bottom-color: mat-color($foreground, divider); - &__error { - background-color: $error-color; - color: $text-color; - } - &__error-message { - a { - color: $text-color; - } - } } .table-row-wrapper { &__errored { .table-row { background-color: transparentize($error-color, .9); } + .table-row__error { + background-color: $error-color; + color: $text-color; + } + .table-row__error-message { + a { + color: $text-color; + } + } } &__warning { .table-row { background-color: transparentize($warn-color, .9); } + .table-row__error { + background-color: $warn-color; + color: $text-color; + } } &__highlighted { background-color: transparentize($primary-color, .95); diff --git a/src/frontend/app/shared/components/list/list-table/table.component.ts b/src/frontend/app/shared/components/list/list-table/table.component.ts index 2f41050ccb..6e58b7c7ad 100644 --- a/src/frontend/app/shared/components/list/list-table/table.component.ts +++ b/src/frontend/app/shared/components/list/list-table/table.component.ts @@ -17,7 +17,7 @@ const tableColumnSelect = { headerCellComponent: TableHeaderSelectComponent, cellComponent: TableCellSelectComponent, class: 'table-column-select', - cellFlex: '0 0 75px' + cellFlex: '0 0 60px' }; const tableColumnAction = { diff --git a/src/frontend/app/store/actions/service-bindings.actions.ts b/src/frontend/app/store/actions/service-bindings.actions.ts index 35fe3edae8..ab95482f65 100644 --- a/src/frontend/app/store/actions/service-bindings.actions.ts +++ b/src/frontend/app/store/actions/service-bindings.actions.ts @@ -1,8 +1,10 @@ +import { RequestOptions, URLSearchParams } from '@angular/http'; +import { entityFactory, serviceBindingSchemaKey } from '../helpers/entity-factory'; +import { PaginatedAction, PaginationParam } from '../types/pagination.types'; import { CFStartAction, ICFAction } from '../types/request.types'; -import { RequestOptions, URLSearchParams } from '@angular/http'; import { getActions } from './action.helper'; -import { entityFactory, serviceBindingSchemaKey } from '../helpers/entity-factory'; + export const DELETE_SERVICE_BINDING_ACTION = '[ Service Instances ] Delete Service Binding'; export const DELETE_SERVICE_BINDING_ACTION_SUCCESS = '[ Service Instances ] Delete Service Binding success'; @@ -11,6 +13,7 @@ export const DELETE_SERVICE_BINDING_ACTION_FAILURE = '[ Service Instances ] Dele export const CREATE_SERVICE_BINDING_ACTION = '[ Service Instances ] Create Service Binding'; export const CREATE_SERVICE_BINDING_ACTION_SUCCESS = '[ Service Instances ] Create Service Binding success'; export const CREATE_SERVICE_BINDING_ACTION_FAILURE = '[ Service Instances ] Create Service Binding failure'; + export class CreateServiceBinding extends CFStartAction implements ICFAction { constructor( public endpointGuid: string, @@ -33,7 +36,8 @@ export class CreateServiceBinding extends CFStartAction implements ICFAction { CREATE_SERVICE_BINDING_ACTION, CREATE_SERVICE_BINDING_ACTION_SUCCESS, CREATE_SERVICE_BINDING_ACTION_FAILURE - ]; entity = [entityFactory(serviceBindingSchemaKey)]; + ]; + entity = [entityFactory(serviceBindingSchemaKey)]; entityKey = serviceBindingSchemaKey; options: RequestOptions; } @@ -59,3 +63,21 @@ export class DeleteServiceBinding extends CFStartAction implements ICFAction { options: RequestOptions; removeEntityOnDelete = true; } + +export class FetchAllServiceBindings extends CFStartAction implements PaginatedAction { + constructor(public endpointGuid: string, public paginationKey: string, public includeRelations = [], public populateMissing = false) { + super(); + this.options = new RequestOptions(); + this.options.url = 'service_bindings'; + this.options.method = 'get'; + } + actions = getActions('Service Bindings', 'Get All'); + entity = [entityFactory(serviceBindingSchemaKey)]; + entityKey = serviceBindingSchemaKey; + options: RequestOptions; + initialParams: PaginationParam = { + 'order-direction': 'asc', + page: 1, + 'results-per-page': 100, + }; +}