diff --git a/ui/src/app/metadata/filter/effect/collection.effect.ts b/ui/src/app/metadata/filter/effect/collection.effect.ts index cc41754c2..bbef8c89b 100644 --- a/ui/src/app/metadata/filter/effect/collection.effect.ts +++ b/ui/src/app/metadata/filter/effect/collection.effect.ts @@ -14,6 +14,7 @@ import { MetadataFilter } from '../../domain/model'; import { removeNulls } from '../../../shared/util'; import { EntityAttributesFilterEntity } from '../../domain/entity/filter/entity-attributes-filter'; import { MetadataFilterService } from '../../domain/service/filter.service'; +import { SelectProviderRequest } from '../../provider/action/collection.action'; /* istanbul ignore next */ @Injectable() @@ -96,6 +97,14 @@ export class FilterCollectionEffects { ); }) ); + @Effect() + updateFilterSuccessReloadProvider$ = this.actions$.pipe( + ofType(FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS), + map(action => action.payload), + withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), + map(([filter, providerId]) => new SelectProviderRequest(providerId)) + ); + @Effect({ dispatch: false }) updateFilterSuccessRedirect$ = this.actions$.pipe( ofType(FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS), diff --git a/ui/src/app/metadata/provider/effect/collection.effect.ts b/ui/src/app/metadata/provider/effect/collection.effect.ts index 55528b395..73ea1937b 100644 --- a/ui/src/app/metadata/provider/effect/collection.effect.ts +++ b/ui/src/app/metadata/provider/effect/collection.effect.ts @@ -6,7 +6,6 @@ import { Router } from '@angular/router'; import { of } from 'rxjs'; import { map, catchError, switchMap, tap, withLatestFrom, debounceTime } from 'rxjs/operators'; import { - ProviderCollectionActionsUnion, ProviderCollectionActionTypes, AddProviderRequest, AddProviderSuccess, @@ -32,14 +31,32 @@ import { import { MetadataProviderService } from '../../domain/service/provider.service'; import * as fromProvider from '../reducer'; import * as fromRoot from '../../../app.reducer'; -import { AddFilterSuccess, FilterCollectionActionTypes } from '../../filter/action/collection.action'; -import { debounce } from '../../../../../node_modules/rxjs-compat/operator/debounce'; +import { ClearProvider, ResetChanges } from '../action/entity.action'; +import { ShowContentionAction } from '../../../contention/action/contention.action'; +import { ContentionService } from '../../../contention/service/contention.service'; +import { MetadataProvider } from '../../domain/model'; /* istanbul ignore next */ @Injectable() export class CollectionEffects { + @Effect() + openContention$ = this.actions$.pipe( + ofType(ProviderCollectionActionTypes.UPDATE_PROVIDER_FAIL), + map(action => action.payload), + withLatestFrom(this.store.select(fromProvider.getSelectedProvider)), + switchMap(([changes, current]) => + this.providerService.find(current.resourceId).pipe( + map(data => new ShowContentionAction(this.contentionService.getContention(current, changes, data, { + resolve: (obj) => this.store.dispatch(new UpdateProviderRequest({ ...obj })), + reject: (obj) => this.store.dispatch(new ResetChanges()) + }))) + ) + ) + ); + + @Effect() loadProviders$ = this.actions$.pipe( ofType(ProviderCollectionActionTypes.LOAD_PROVIDER_REQUEST), @@ -100,7 +117,7 @@ export class CollectionEffects { .update(provider) .pipe( map(p => new UpdateProviderSuccess({id: p.id, changes: p})), - catchError((e) => of(new UpdateProviderFail(e))) + catchError((e) => of(new UpdateProviderFail(provider))) ) ) ); @@ -116,7 +133,10 @@ export class CollectionEffects { updateProviderSuccessRedirect$ = this.actions$.pipe( ofType(ProviderCollectionActionTypes.UPDATE_PROVIDER_SUCCESS), map(action => action.payload), - tap(provider => this.router.navigate(['metadata', 'manager', 'providers'])) + tap(provider => { + this.store.dispatch(new ClearProvider()); + this.router.navigate(['metadata', 'manager', 'providers']); + }) ); @Effect() @@ -210,6 +230,7 @@ export class CollectionEffects { private actions$: Actions, private router: Router, private store: Store, - private providerService: MetadataProviderService + private providerService: MetadataProviderService, + private contentionService: ContentionService ) { } } diff --git a/ui/src/app/metadata/provider/provider.module.ts b/ui/src/app/metadata/provider/provider.module.ts index 95d037256..80360c517 100644 --- a/ui/src/app/metadata/provider/provider.module.ts +++ b/ui/src/app/metadata/provider/provider.module.ts @@ -29,6 +29,7 @@ import { ProviderFilterListComponent } from './container/provider-filter-list.co import { ProviderEditorNavComponent } from './component/provider-editor-nav.component'; import { UnsavedProviderComponent } from './component/unsaved-provider.dialog'; +import { ContentionModule } from '../../contention/contention.module'; @NgModule({ declarations: [ @@ -55,6 +56,7 @@ import { UnsavedProviderComponent } from './component/unsaved-provider.dialog'; SharedModule, FormModule, RouterModule, + ContentionModule, NgbDropdownModule ], exports: [] diff --git a/ui/src/app/schema-form/widget/filter-target/filter-target.component.html b/ui/src/app/schema-form/widget/filter-target/filter-target.component.html index 482803777..5ddb9aeda 100644 --- a/ui/src/app/schema-form/widget/filter-target/filter-target.component.html +++ b/ui/src/app/schema-form/widget/filter-target/filter-target.component.html @@ -32,7 +32,9 @@ [matches]="ids$ | async" (keydown.enter)="onSelectValue(search.value)"> - You must add at least one entity id target. + + You must add at least one entity id target and they must each be unique. +

diff --git a/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts b/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts index ab810dc32..a93963678 100644 --- a/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts +++ b/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, AfterViewInit } from '@angular/core'; -import { FormControl, Validators } from '@angular/forms'; +import { FormControl, Validators, AbstractControl, ValidatorFn } from '@angular/forms'; import { ObjectWidget } from 'ngx-schema-form'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; @@ -25,7 +25,9 @@ export class FilterTargetComponent extends ObjectWidget implements OnDestroy, Af search: FormControl = new FormControl( '', [], - [EntityValidators.existsInCollection(this.store.select(fromFilters.getEntityCollection))] + [ + EntityValidators.existsInCollection(this.store.select(fromFilters.getEntityCollection)) + ] ); script: FormControl = new FormControl( @@ -59,6 +61,14 @@ export class FilterTargetComponent extends ObjectWidget implements OnDestroy, Af ngAfterViewInit(): void { super.ngAfterViewInit(); this.script.setValue(this.targets[0]); + + this.search.setValidators(this.unique()); + } + + unique(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } | null => { + return this.targets.indexOf(control.value) > -1 ? { unique: true } : null; + }; } searchEntityIds(term: string): void {