From 2ff49c480ea64d71f8ca9205be46398985b92121 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 3 Feb 2020 13:42:11 +0000 Subject: [PATCH] Multiple User Provided Service Instance Create/Update Fixes - On transistion from list to edit stepper user can now supply just tags to enable form - Fixed error handling on create and update flows - Ensure we update service instance list after creating user provided service instance --- ...specify-user-provided-details.component.ts | 20 ++++++-- ...-foundry-user-provided-services.service.ts | 47 +++++++++++++------ 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/add-service-instance/specify-user-provided-details/specify-user-provided-details.component.ts b/src/frontend/packages/cloud-foundry/src/shared/components/add-service-instance/specify-user-provided-details/specify-user-provided-details.component.ts index ee8e5309e8..433f914488 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/add-service-instance/specify-user-provided-details/specify-user-provided-details.component.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/components/add-service-instance/specify-user-provided-details/specify-user-provided-details.component.ts @@ -23,7 +23,6 @@ import { selectCreateServiceInstance, } from '../../../../../../cloud-foundry/src/store/selectors/create-service-instance.selectors'; import { IUserProvidedServiceInstance } from '../../../../../../core/src/core/cf-api-svc.types'; -import { entityCatalog } from '../../../../../../store/src/entity-catalog/entity-catalog.service'; import { safeUnsubscribe, urlValidationExpression } from '../../../../../../core/src/core/utils.service'; import { environment } from '../../../../../../core/src/environments/environment'; import { @@ -34,6 +33,7 @@ import { isValidJsonValidator } from '../../../../../../core/src/shared/form-val import { CloudFoundryUserProvidedServicesService, } from '../../../../../../core/src/shared/services/cloud-foundry-user-provided-services.service'; +import { entityCatalog } from '../../../../../../store/src/entity-catalog/entity-catalog.service'; import { APIResource } from '../../../../../../store/src/types/api.types'; import { CF_ENDPOINT_TYPE } from '../../../../cf-types'; import { CreateServiceFormMode, CsiModeService } from './../csi-mode.service'; @@ -124,6 +124,17 @@ export class SpecifyUserProvidedDetailsComponent implements OnDestroy { ), map(valid => this.validAndChanged(valid)), ); + this.subscriptions.push(this.tagsChanged.subscribe(() => { + if (this.formMode === CreateServiceFormMode.CreateServiceInstance) { + this.createEditServiceInstance.markAsTouched(); + this.createEditServiceInstance.markAsDirty(); + this.createEditServiceInstance.updateValueAndValidity(); + } else { + this.bindExistingInstance.markAsTouched(); + this.bindExistingInstance.markAsDirty(); + this.bindExistingInstance.updateValueAndValidity(); + } + })); this.subscriptions.push(obs.subscribe(valid => this.valid.next(valid))); } @@ -252,10 +263,10 @@ export class SpecifyUserProvidedDetailsComponent implements OnDestroy { ).pipe( combineLatest(this.store.select(selectCreateServiceInstance)), switchMap(([request, state]) => { - const newGuid = request.response.result[0]; const success = !request.error; const redirect = !request.error; if (!!state.bindAppGuid && success) { + const newGuid = request.response.result[0]; return this.createApplicationServiceBinding(newGuid, state); } return observableOf({ @@ -293,7 +304,7 @@ export class SpecifyUserProvidedDetailsComponent implements OnDestroy { ); } - private onNextUpdate() { + private onNextUpdate(): Observable { const updateData = this.getServiceData(); return this.upsService.updateUserProvidedService( this.cfGuid, @@ -302,7 +313,8 @@ export class SpecifyUserProvidedDetailsComponent implements OnDestroy { ).pipe( map(er => ({ success: !er.updating[UpdateUserProvidedServiceInstance.updateServiceInstance].error, - redirect: !er.updating[UpdateUserProvidedServiceInstance.updateServiceInstance].error + redirect: !er.updating[UpdateUserProvidedServiceInstance.updateServiceInstance].error, + message: `Failed to update service instance: ${er.updating[UpdateUserProvidedServiceInstance.updateServiceInstance].message}` })) ); } diff --git a/src/frontend/packages/core/src/shared/services/cloud-foundry-user-provided-services.service.ts b/src/frontend/packages/core/src/shared/services/cloud-foundry-user-provided-services.service.ts index 57233539c2..f27848125f 100644 --- a/src/frontend/packages/core/src/shared/services/cloud-foundry-user-provided-services.service.ts +++ b/src/frontend/packages/core/src/shared/services/cloud-foundry-user-provided-services.service.ts @@ -1,9 +1,8 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; import { combineLatest, Observable } from 'rxjs'; -import { debounceTime, filter, map } from 'rxjs/operators'; +import { filter, first, map, pairwise, tap } from 'rxjs/operators'; -import { CF_ENDPOINT_TYPE } from '../../../../cloud-foundry/src/cf-types'; import { CreateUserProvidedServiceInstance, getUserProvidedServiceInstanceRelations, @@ -17,22 +16,24 @@ import { spaceEntityType, userProvidedServiceInstanceEntityType, } from '../../../../cloud-foundry/src/cf-entity-types'; +import { CF_ENDPOINT_TYPE } from '../../../../cloud-foundry/src/cf-types'; import { UserProvidedServiceActionBuilder, } from '../../../../cloud-foundry/src/entity-action-builders/user-provided-service.action-builders'; import { createEntityRelationPaginationKey } from '../../../../cloud-foundry/src/entity-relations/entity-relations.types'; import { fetchTotalResults } from '../../../../cloud-foundry/src/features/cloud-foundry/cf.helpers'; -import { selectCfRequestInfo } from '../../../../cloud-foundry/src/store/selectors/api.selectors'; import { QParam, QParamJoiners } from '../../../../cloud-foundry/src/shared/q-param'; +import { selectCfRequestInfo } from '../../../../cloud-foundry/src/store/selectors/api.selectors'; +import { ClearPaginationOfType } from '../../../../store/src/actions/pagination.actions'; +import { entityCatalog } from '../../../../store/src/entity-catalog/entity-catalog.service'; +import { EntityCatalogEntityConfig, IEntityMetadata } from '../../../../store/src/entity-catalog/entity-catalog.types'; +import { EntityServiceFactory } from '../../../../store/src/entity-service-factory.service'; +import { PaginationMonitorFactory } from '../../../../store/src/monitors/pagination-monitor.factory'; import { RequestInfoState } from '../../../../store/src/reducers/api-request-reducer/types'; import { getPaginationObservables } from '../../../../store/src/reducers/pagination-reducer/pagination-reducer.helper'; import { APIResource } from '../../../../store/src/types/api.types'; import { PaginatedAction } from '../../../../store/src/types/pagination.types'; import { IUserProvidedServiceInstance } from '../../core/cf-api-svc.types'; -import { entityCatalog } from '../../../../store/src/entity-catalog/entity-catalog.service'; -import { EntityCatalogEntityConfig, IEntityMetadata } from '../../../../store/src/entity-catalog/entity-catalog.types'; -import { EntityServiceFactory } from '../../../../store/src/entity-service-factory.service'; -import { PaginationMonitorFactory } from '../../../../store/src/monitors/pagination-monitor.factory'; @Injectable() @@ -43,6 +44,11 @@ export class CloudFoundryUserProvidedServicesService { entityType: serviceInstancesEntityType }; + private userProvidedServiceInstancesEntityConfig: EntityCatalogEntityConfig = { + endpointType: CF_ENDPOINT_TYPE, + entityType: userProvidedServiceInstanceEntityType + }; + private userProvidedServiceEntity = entityCatalog.getEntity( CF_ENDPOINT_TYPE, userProvidedServiceInstanceEntityType @@ -114,12 +120,21 @@ export class CloudFoundryUserProvidedServicesService { guid: string, data: IUserProvidedServiceInstanceData ): Observable { - const action = new CreateUserProvidedServiceInstance(cfGuid, guid, data, this.serviceInstancesEntityConfig); + const action = new CreateUserProvidedServiceInstance(cfGuid, guid, data, this.userProvidedServiceInstancesEntityConfig); const create$ = this.store.select(selectCfRequestInfo(userProvidedServiceInstanceEntityType, guid)); this.store.dispatch(action); return create$.pipe( - debounceTime(250), - filter(a => !a.creating), + pairwise(), + filter(([oldV, newV]) => oldV.creating && !newV.creating), + map(([, newV]) => newV), + first(), + tap(v => { + if (!v.error) { + // Problem - Lists with multiple actions aren't updated following the creation of an entity based on secondary action + // Here the service instance list (1st action SI, 2nd action UPSI) isn't updated so manually do so + this.store.dispatch(new ClearPaginationOfType(this.serviceInstancesEntityConfig)); + } + }) ); } @@ -132,16 +147,18 @@ export class CloudFoundryUserProvidedServicesService { guid, cfGuid, data, - this.serviceInstancesEntityConfig + this.userProvidedServiceInstancesEntityConfig ); return this.userProvidedServiceEntity.getEntityMonitor( this.store, guid ).entityRequest$.pipe( - filter( - er => er.updating[UpdateUserProvidedServiceInstance.updateServiceInstance] && - er.updating[UpdateUserProvidedServiceInstance.updateServiceInstance].busy - ) + filter(v => !!v.updating[UpdateUserProvidedServiceInstance.updateServiceInstance]), + pairwise(), + filter(([oldV, newV]) => + oldV.updating[UpdateUserProvidedServiceInstance.updateServiceInstance].busy && + !newV.updating[UpdateUserProvidedServiceInstance.updateServiceInstance].busy), + map(([, newV]) => newV) ); }