From 40d15f8efe57bb509a406188582f8db19ad3b8c0 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Wed, 22 Aug 2018 17:06:51 -0400 Subject: [PATCH 1/4] SHIBUI-782: fix filters delete bug --- .../controller/MetadataFiltersController.java | 29 +++++++---- ...taFiltersControllerIntegrationTests.groovy | 48 +++++++++++++++++++ .../MetadataFiltersControllerTests.groovy | 1 + 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java index 2ec5f01ab..7e20287be 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersController.java @@ -27,10 +27,12 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.net.URI; +import java.util.ArrayList; +import java.util.List; import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.util.stream.Collectors.toList; import static org.springframework.http.HttpStatus.NOT_FOUND; @RestController @@ -67,7 +69,7 @@ public ResponseEntity getAll(@PathVariable String metadataResolverId) { @Transactional(readOnly = true) public ResponseEntity getOne(@PathVariable String metadataResolverId, @PathVariable String resourceId) { MetadataResolver resolver = findResolverOrThrowHttp404(metadataResolverId); - return ResponseEntity.ok(findFilterOrThrowHttp404(resolver, resourceId)); + return ResponseEntity.ok(findFilterOrThrowHttp404(resourceId)); } @PostMapping("/Filters") @@ -130,12 +132,20 @@ public ResponseEntity delete(@PathVariable String metadataResolverId, @PathVariable String resourceId) { MetadataResolver resolver = findResolverOrThrowHttp404(metadataResolverId); + MetadataFilter filterToDelete = findFilterOrThrowHttp404(resourceId); + //TODO: consider implementing delete of filter directly from RDBMS via FilterRepository - boolean removed = resolver.getMetadataFilters().removeIf(f -> f.getResourceId().equals(resourceId)); + //This is currently the only way to correctly delete and manage resolver-filter relationship + //Until we implement a bi-directional relationship between them which turns out to be a much larger + //change that we need to make in the entire code base + List updatedFilters = new ArrayList<>(resolver.getMetadataFilters()); + boolean removed = updatedFilters.removeIf(f -> f.getResourceId().equals(resourceId)); if(!removed) { throw HTTP_404_CLIENT_ERROR_EXCEPTION.get(); } + resolver.setMetadataFilters(updatedFilters); repository.save(resolver); + filterRepository.delete(filterToDelete); //TODO: do we need to reload filters here?!? //metadataResolverService.reloadFilters(persistedMr.getName()); @@ -151,17 +161,18 @@ private MetadataResolver findResolverOrThrowHttp404(String resolverResourceId) { return resolver; } - private MetadataFilter findFilterOrThrowHttp404(MetadataResolver resolver, String filterResourceId) { - return resolver.getMetadataFilters().stream() - .filter(f -> f.getResourceId().equals(filterResourceId)) - .findFirst() - .orElseThrow(HTTP_404_CLIENT_ERROR_EXCEPTION); + private MetadataFilter findFilterOrThrowHttp404(String filterResourceId) { + MetadataFilter filter = filterRepository.findByResourceId(filterResourceId); + if(filter == null) { + throw HTTP_404_CLIENT_ERROR_EXCEPTION.get(); + } + return filter; } private MetadataFilter newlyPersistedFilter(Stream filters, final String filterResourceId) { MetadataFilter persistedFilter = filters .filter(f -> f.getResourceId().equals(filterResourceId)) - .collect(Collectors.toList()).get(0); + .collect(toList()).get(0); return persistedFilter; } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerIntegrationTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerIntegrationTests.groovy index e65a7e963..06f9d0ca0 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerIntegrationTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerIntegrationTests.groovy @@ -126,6 +126,54 @@ class MetadataFiltersControllerIntegrationTests extends Specification { GETResultAfterDelete.statusCode.value() == 404 } + def "DELETE Filter with resolver having more than TWO filters attached"() { + given: 'MetadataResolver with 3 attached filters is available in data store' + def resolver = generator.buildRandomMetadataResolverOfType('FileBacked') + resolver.metadataFilters << generator.entityAttributesFilter() + resolver.metadataFilters << generator.entityAttributesFilter() + resolver.metadataFilters << generator.entityAttributesFilter() + resolver.metadataFilters << generator.entityAttributesFilter() + resolver.metadataFilters << generator.entityAttributesFilter() + resolver.metadataFilters << generator.entityAttributesFilter() + resolver.metadataFilters << generator.entityAttributesFilter() + def filter_THREE_ResourceId = resolver.metadataFilters[2].resourceId + def filter_SIX_ResourceId = resolver.metadataFilters[5].resourceId + def resolverResourceId = resolver.resourceId + metadataResolverRepository.save(resolver) + + when: 'GET resolver to count the original number of filters' + def originalResolverResult = this.restTemplate.getForEntity("$BASE_URI/$resolverResourceId", Map) + + then: + originalResolverResult.body.metadataFilters.size == 7 + + when: 'DELETE call is made for one of the filters and then GET call is made for the just deleted filter' + restTemplate.delete("$BASE_URI/$resolverResourceId/Filters/$filter_SIX_ResourceId") + def GETResultAfterDelete = this.restTemplate.getForEntity("$BASE_URI/$resolverResourceId/Filters/$filter_SIX_ResourceId", String) + + then: 'The deleted resource is gone' + GETResultAfterDelete.statusCodeValue == 404 + + and: 'GET resolver to count modified number of filters' + def resolverResult_2 = this.restTemplate.getForEntity("$BASE_URI/$resolverResourceId", Map) + + then: + resolverResult_2.body.metadataFilters.size == 6 + + and: 'DELETE call is made for one of the filters and then GET call is made for the just deleted filter' + restTemplate.delete("$BASE_URI/$resolverResourceId/Filters/$filter_THREE_ResourceId") + def GETResultAfterDelete_2 = this.restTemplate.getForEntity("$BASE_URI/$resolverResourceId/Filters/$filter_THREE_ResourceId", String) + + then: 'The deleted resource is gone' + GETResultAfterDelete_2.statusCodeValue == 404 + + and: 'GET resolver to count modified number of filters' + def resolverResult_3 = this.restTemplate.getForEntity("$BASE_URI/$resolverResourceId", Map) + + then: + resolverResult_3.body.metadataFilters.size == 5 + } + private HttpEntity createRequestHttpEntityFor(Closure jsonBodySupplier) { new HttpEntity(jsonBodySupplier(), ['Content-Type': 'application/json'] as HttpHeaders) } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy index 40c6cefef..bf622dfb5 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataFiltersControllerTests.groovy @@ -112,6 +112,7 @@ class MetadataFiltersControllerTests extends Specification { def expectedFilter = testObjectGenerator.entityAttributesFilter() metadataResolver.metadataFilters = [expectedFilter] 1 * metadataResolverRepository.findByResourceId(_) >> metadataResolver + 1 * metadataFilterRepository.findByResourceId(_) >> expectedFilter def expectedResourceId = expectedFilter.resourceId def expectedHttpResponseStatus = status().isOk() From 66850722daa79a70de3aec0625e5cdea5ce86ceb Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Mon, 27 Aug 2018 19:52:02 +0000 Subject: [PATCH 2/4] Merged in feature/SHIBUI-744 (pull request #180) SHIBUI-744 Implemented preview buttons Approved-by: Shibui Jenkins Approved-by: Ryan Mathis --- .../metadata/domain/action/entity.action.ts | 5 +++- .../domain/effect/entity.effect.spec.ts | 4 +-- .../metadata/domain/effect/entity.effect.ts | 9 +++--- .../container/edit-filter.component.html | 1 + .../filter/container/edit-filter.component.ts | 21 ++++++++++---- .../dashboard-resolvers-list.component.ts | 2 +- .../provider-select.component.spec.ts | 12 ++++++++ .../container/provider-select.component.ts | 17 +++++------ ui/src/app/schema-form/registry.ts | 3 ++ ui/src/app/schema-form/schema-form.module.ts | 4 ++- .../widget/button/icon-button.component.html | 4 +++ .../widget/button/icon-button.component.ts | 28 +++++++++++++++++++ .../filter-target.component.html | 9 ++++-- .../filter-target/filter-target.component.ts | 11 +++++++- .../filter/entity-attributes.schema.json | 7 +++++ 15 files changed, 111 insertions(+), 26 deletions(-) create mode 100644 ui/src/app/schema-form/widget/button/icon-button.component.html create mode 100644 ui/src/app/schema-form/widget/button/icon-button.component.ts diff --git a/ui/src/app/metadata/domain/action/entity.action.ts b/ui/src/app/metadata/domain/action/entity.action.ts index 4bfe1380c..c3f47e1ab 100644 --- a/ui/src/app/metadata/domain/action/entity.action.ts +++ b/ui/src/app/metadata/domain/action/entity.action.ts @@ -6,7 +6,10 @@ export const PREVIEW_ENTITY = '[Domain] Preview Entity'; export class PreviewEntity implements Action { readonly type = PREVIEW_ENTITY; - constructor(public payload: MetadataEntity) { } + constructor(public payload: { + id: string, + entity: MetadataEntity + }) { } } export type Actions = diff --git a/ui/src/app/metadata/domain/effect/entity.effect.spec.ts b/ui/src/app/metadata/domain/effect/entity.effect.spec.ts index 9039a4cd9..07f7143e5 100644 --- a/ui/src/app/metadata/domain/effect/entity.effect.spec.ts +++ b/ui/src/app/metadata/domain/effect/entity.effect.spec.ts @@ -48,7 +48,7 @@ describe('Entity Effects', () => { it('should open a modal window for a filter', fakeAsync(() => { spyOn(modal, 'open').and.returnValue({componentInstance: {}}); spyOn(idService, 'preview').and.returnValue(of('')); - effects.openModal(new EntityAttributesFilterEntity()); + effects.openModal({ id: 'foo', entity: new EntityAttributesFilterEntity()}); expect(idService.preview).toHaveBeenCalled(); tick(10); expect(modal.open).toHaveBeenCalled(); @@ -57,7 +57,7 @@ describe('Entity Effects', () => { it('should open a modal window for a provider', fakeAsync(() => { spyOn(modal, 'open').and.returnValue({ componentInstance: {} }); spyOn(providerService, 'preview').and.returnValue(of('')); - effects.openModal(new FileBackedHttpMetadataResolver()); + effects.openModal({id: 'foo', entity: new FileBackedHttpMetadataResolver()}); expect(providerService.preview).toHaveBeenCalled(); tick(10); expect(modal.open).toHaveBeenCalled(); diff --git a/ui/src/app/metadata/domain/effect/entity.effect.ts b/ui/src/app/metadata/domain/effect/entity.effect.ts index 4901b3828..2bf1825aa 100644 --- a/ui/src/app/metadata/domain/effect/entity.effect.ts +++ b/ui/src/app/metadata/domain/effect/entity.effect.ts @@ -20,7 +20,7 @@ export class EntityEffects { previewEntityXml$ = this.actions$.pipe( ofType(entityActions.PREVIEW_ENTITY), map(action => action.payload), - tap(entity => this.openModal(entity)) + tap(prev => this.openModal(prev)) ); constructor( @@ -30,9 +30,10 @@ export class EntityEffects { private entityService: EntityIdService ) { } - openModal(entity: MetadataEntity): void { - let request: Observable = entity.kind === MetadataTypes.FILTER ? - this.entityService.preview(entity.getId()) : this.providerService.preview(entity.getId()); + openModal(prev: { id: string, entity: MetadataEntity }): void { + let { id, entity } = prev, + request: Observable = entity.kind === MetadataTypes.FILTER ? + this.entityService.preview(id) : this.providerService.preview(id); request.subscribe(xml => { let modal = this.modalService.open(PreviewDialogComponent, { size: 'lg', diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.html b/ui/src/app/metadata/filter/container/edit-filter.component.html index cbdf2df18..5c9ecb5c8 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.html +++ b/ui/src/app/metadata/filter/container/edit-filter.component.html @@ -29,6 +29,7 @@ [schema]="schema$ | async" [model]="model$ | async" [validators]="definition.getValidators()" + [actions]="actions" (onChange)="valueChangeSubject.next($event)" (onErrorChange)="statusChangeSubject.next($event)"> diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.ts b/ui/src/app/metadata/filter/container/edit-filter.component.ts index 50184abf8..6e058a464 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.ts +++ b/ui/src/app/metadata/filter/container/edit-filter.component.ts @@ -1,11 +1,11 @@ import { Component } from '@angular/core'; import { Store } from '@ngrx/store'; -import { Subject, Observable, of } from 'rxjs'; +import { Subject, Observable } from 'rxjs'; import * as fromFilter from '../reducer'; -import { MetadataFilterTypes, EntityAttributesFilter } from '../model'; +import { MetadataFilterTypes } from '../model'; import { FormDefinition } from '../../../wizard/model'; -import { MetadataFilter, MetadataEntity } from '../../domain/model'; +import { MetadataFilter } from '../../domain/model'; import { SchemaService } from '../../../schema-form/service/schema.service'; import { UpdateFilterRequest } from '../action/collection.action'; import { CancelCreateFilter, UpdateFilterChanges } from '../action/filter.action'; @@ -32,6 +32,8 @@ export class EditFilterComponent { filter: MetadataFilter; isValid: boolean; + actions: any; + constructor( private store: Store, private schemaService: SchemaService @@ -50,6 +52,12 @@ export class EditFilterComponent { this.store .select(fromFilter.getFilter) .subscribe(filter => this.filter = filter); + + this.actions = { + preview: (property: any, parameters: any) => { + this.preview(parameters.id); + } + }; } save(): void { @@ -60,8 +68,11 @@ export class EditFilterComponent { this.store.dispatch(new CancelCreateFilter()); } - preview(entity: MetadataFilter): void { - this.store.dispatch(new PreviewEntity(new EntityAttributesFilterEntity(entity))); + preview(id: string): void { + this.store.dispatch(new PreviewEntity({ + id, + entity: new EntityAttributesFilterEntity(this.filter) + })); } } diff --git a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts index 962efd52e..b4b5af807 100644 --- a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts +++ b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts @@ -79,7 +79,7 @@ export class DashboardResolversListComponent implements OnInit { } openPreviewDialog(entity: MetadataEntity): void { - this.store.dispatch(new PreviewEntity(entity)); + this.store.dispatch(new PreviewEntity({ id: entity.getId(), entity })); } deleteResolver(entity: MetadataResolver): void { diff --git a/ui/src/app/metadata/provider/container/provider-select.component.spec.ts b/ui/src/app/metadata/provider/container/provider-select.component.spec.ts index b8e07460b..c52a494b8 100644 --- a/ui/src/app/metadata/provider/container/provider-select.component.spec.ts +++ b/ui/src/app/metadata/provider/container/provider-select.component.spec.ts @@ -6,6 +6,7 @@ import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; import { ProviderSelectComponent } from './provider-select.component'; import * as fromRoot from '../reducer'; import * as fromWizard from '../../../wizard/reducer'; +import { MetadataProvider } from '../../domain/model'; @Component({ template: ` @@ -53,4 +54,15 @@ describe('Provider Select Component', () => { it('should instantiate the component', async(() => { expect(app).toBeTruthy(); })); + + describe('setDefinition method', () => { + it('should not dispatch an action if no provider is defined', () => { + app.setDefinition(null); + expect(store.dispatch).not.toHaveBeenCalled(); + }); + it('should dispatch an action if a provider is defined', () => { + app.setDefinition({} as MetadataProvider); + expect(store.dispatch).toHaveBeenCalled(); + }); + }); }); diff --git a/ui/src/app/metadata/provider/container/provider-select.component.ts b/ui/src/app/metadata/provider/container/provider-select.component.ts index 1e64aaae6..baba7f626 100644 --- a/ui/src/app/metadata/provider/container/provider-select.component.ts +++ b/ui/src/app/metadata/provider/container/provider-select.component.ts @@ -33,14 +33,15 @@ export class ProviderSelectComponent implements OnDestroy { this.provider$ = this.store.select(fromProviders.getSelectedProvider).pipe(skipWhile(p => !p)); - this.provider$ - .subscribe(provider => { - if (provider) { - this.store.dispatch(new SetDefinition({ - ...MetadataProviderEditorTypes.find(def => def.type === provider['@type']) - })); - } - }); + this.provider$.subscribe(provider => this.setDefinition(provider)); + } + + setDefinition(provider: MetadataProvider): void { + if (provider) { + this.store.dispatch(new SetDefinition({ + ...MetadataProviderEditorTypes.find(def => def.type === provider['@type']) + })); + } } ngOnDestroy() { diff --git a/ui/src/app/schema-form/registry.ts b/ui/src/app/schema-form/registry.ts index 050edbf42..81ca2ac63 100644 --- a/ui/src/app/schema-form/registry.ts +++ b/ui/src/app/schema-form/registry.ts @@ -16,6 +16,7 @@ import { CustomArrayComponent } from './widget/array/array.component'; import { CustomIntegerComponent } from './widget/number/number.component'; import { FilterTargetComponent } from './widget/filter-target/filter-target.component'; import { ChecklistComponent } from './widget/check/checklist.component'; +import { IconButtonComponent } from './widget/button/icon-button.component'; export class CustomWidgetRegistry extends WidgetRegistry { @@ -55,6 +56,8 @@ export class CustomWidgetRegistry extends WidgetRegistry { this.register('filter-target', FilterTargetComponent); + this.register('icon-button', IconButtonComponent); + /* NGX-Form */ this.register('range', RangeWidget); diff --git a/ui/src/app/schema-form/schema-form.module.ts b/ui/src/app/schema-form/schema-form.module.ts index a637d629c..d4a157e47 100644 --- a/ui/src/app/schema-form/schema-form.module.ts +++ b/ui/src/app/schema-form/schema-form.module.ts @@ -17,6 +17,7 @@ import { CustomArrayComponent } from './widget/array/array.component'; import { CustomIntegerComponent } from './widget/number/number.component'; import { FilterTargetComponent } from './widget/filter-target/filter-target.component'; import { ChecklistComponent } from './widget/check/checklist.component'; +import { IconButtonComponent } from './widget/button/icon-button.component'; export const COMPONENTS = [ BooleanRadioComponent, @@ -29,7 +30,8 @@ export const COMPONENTS = [ CustomArrayComponent, CustomIntegerComponent, FilterTargetComponent, - ChecklistComponent + ChecklistComponent, + IconButtonComponent ]; @NgModule({ diff --git a/ui/src/app/schema-form/widget/button/icon-button.component.html b/ui/src/app/schema-form/widget/button/icon-button.component.html new file mode 100644 index 000000000..107a8eb7c --- /dev/null +++ b/ui/src/app/schema-form/widget/button/icon-button.component.html @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/ui/src/app/schema-form/widget/button/icon-button.component.ts b/ui/src/app/schema-form/widget/button/icon-button.component.ts new file mode 100644 index 000000000..2dcca890f --- /dev/null +++ b/ui/src/app/schema-form/widget/button/icon-button.component.ts @@ -0,0 +1,28 @@ +import { + Component, AfterViewInit, +} from '@angular/core'; +import { ButtonWidget } from 'ngx-schema-form'; +import { ɵb as ActionRegistry } from 'ngx-schema-form'; + +@Component({ + selector: 'icon-button', + templateUrl: `./icon-button.component.html` +}) +export class IconButtonComponent extends ButtonWidget implements AfterViewInit { + + action = ($event) => {}; + + constructor(private actionRegistry: ActionRegistry) { + super(); + } + + ngAfterViewInit(): void { + this.action = (e) => { + let action = this.actionRegistry.get(this.button.id); + if (this.button.id && action) { + action(this.formProperty, this.button.parameters); + } + e.preventDefault(); + }; + } +} 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 5124e7fac..73c392817 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 @@ -97,9 +97,12 @@
  • {{ id }} - + + + +
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 a93963678..2b08949d2 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 @@ -61,7 +61,6 @@ export class FilterTargetComponent extends ObjectWidget implements OnDestroy, Af ngAfterViewInit(): void { super.ngAfterViewInit(); this.script.setValue(this.targets[0]); - this.search.setValidators(this.unique()); } @@ -80,6 +79,16 @@ export class FilterTargetComponent extends ObjectWidget implements OnDestroy, Af } } + getButtonConfig(id: string): any { + let buttons = this.formProperty.getProperty('value').schema.buttons; + return (buttons || []).map(btn => ({ + ...btn, + parameters: { + id + } + })); + } + get targets(): string[] { return this.formProperty.getProperty('value').value; } diff --git a/ui/src/assets/schema/filter/entity-attributes.schema.json b/ui/src/assets/schema/filter/entity-attributes.schema.json index e57792b00..3baede17f 100644 --- a/ui/src/assets/schema/filter/entity-attributes.schema.json +++ b/ui/src/assets/schema/filter/entity-attributes.schema.json @@ -74,6 +74,13 @@ }, "value": { "type": "array", + "buttons": [ + { + "id": "preview", + "label": "Preview", + "widget": "icon-button" + } + ], "minItems": 1, "uniqueItems": true, "items": { From 4a7bd22a6d7dddd91087022357380fb590b5db9e Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Mon, 27 Aug 2018 22:50:57 +0000 Subject: [PATCH 3/4] Merged in feature/SHIBUI-797 (pull request #181) SHIBUI-797 Fixed backup file init configuration Approved-by: Shibui Jenkins Approved-by: Ryan Mathis --- .../provider/action/collection.action.ts | 8 ++++ .../provider/effect/collection.effect.ts | 5 ++- .../filebacked-http-common.editor.schema.json | 25 +++-------- .../filebacked-http-common.schema.json | 41 +------------------ 4 files changed, 18 insertions(+), 61 deletions(-) diff --git a/ui/src/app/metadata/provider/action/collection.action.ts b/ui/src/app/metadata/provider/action/collection.action.ts index a58164407..375b851a2 100644 --- a/ui/src/app/metadata/provider/action/collection.action.ts +++ b/ui/src/app/metadata/provider/action/collection.action.ts @@ -6,6 +6,7 @@ export enum ProviderCollectionActionTypes { UPDATE_PROVIDER_REQUEST = '[Metadata Provider] Update Request', UPDATE_PROVIDER_SUCCESS = '[Metadata Provider] Update Success', UPDATE_PROVIDER_FAIL = '[Metadata Provider] Update Fail', + UPDATE_PROVIDER_CONFLICT = '[Metadata Provider] Update Conflict', LOAD_PROVIDER_REQUEST = '[Metadata Provider Collection] Provider Load REQUEST', LOAD_PROVIDER_SUCCESS = '[Metadata Provider Collection] Provider Load SUCCESS', @@ -91,6 +92,12 @@ export class UpdateProviderFail implements Action { constructor(public payload: MetadataProvider) { } } +export class UpdateProviderConflict implements Action { + readonly type = ProviderCollectionActionTypes.UPDATE_PROVIDER_FAIL; + + constructor(public payload: MetadataProvider) { } +} + export class AddProviderRequest implements Action { readonly type = ProviderCollectionActionTypes.ADD_PROVIDER_REQUEST; @@ -195,6 +202,7 @@ export type ProviderCollectionActionsUnion = | UpdateProviderRequest | UpdateProviderSuccess | UpdateProviderFail + | UpdateProviderConflict | SetOrderProviderRequest | SetOrderProviderSuccess | SetOrderProviderFail diff --git a/ui/src/app/metadata/provider/effect/collection.effect.ts b/ui/src/app/metadata/provider/effect/collection.effect.ts index eb6b0af4b..73ce52ea5 100644 --- a/ui/src/app/metadata/provider/effect/collection.effect.ts +++ b/ui/src/app/metadata/provider/effect/collection.effect.ts @@ -19,6 +19,7 @@ import { UpdateProviderRequest, UpdateProviderSuccess, UpdateProviderFail, + UpdateProviderConflict, GetOrderProviderRequest, GetOrderProviderSuccess, GetOrderProviderFail, @@ -44,7 +45,7 @@ export class CollectionEffects { @Effect() openContention$ = this.actions$.pipe( - ofType(ProviderCollectionActionTypes.UPDATE_PROVIDER_FAIL), + ofType(ProviderCollectionActionTypes.UPDATE_PROVIDER_CONFLICT), map(action => action.payload), withLatestFrom(this.store.select(fromProvider.getSelectedProvider)), switchMap(([changes, current]) => @@ -118,7 +119,7 @@ export class CollectionEffects { .update(provider) .pipe( map(p => new UpdateProviderSuccess({id: p.id, changes: p})), - catchError((e) => of(new UpdateProviderFail(provider))) + catchError((e) => e.status === 409 ? of(new UpdateProviderConflict(provider)) : of(new UpdateProviderFail(provider))) ) ) ); diff --git a/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json b/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json index ca8ea2646..684a1c13c 100644 --- a/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json +++ b/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json @@ -16,11 +16,10 @@ "required": [ "name", "xmlId", - "metadataURL" + "metadataURL", + "backingFile", + "backupFileInitNextRefreshDelay" ], - "dependencies": { - "initializeFromBackupFile": {"required": ["backingFile", "backupFileInitNextRefreshDelay"]} - }, "anyOf": [ { "properties": { @@ -29,11 +28,7 @@ true ] } - }, - "required": [ - "backingFile", - "backupFileInitNextRefreshDelay" - ] + } }, { "properties": { @@ -144,22 +139,12 @@ "title": "Backing File", "description": "Specifies where the backing file is located. If the remote server is unavailable at startup, the backing file is loaded instead.", "type": "string", - "default": "", - "visibleIf": { - "initializeFromBackupFile": [ - true - ] - } + "default": "" }, "backupFileInitNextRefreshDelay": { "title": "Backup File Init Next Refresh Delay", "description": "Delay duration after which to schedule next HTTP refresh when initialized from the backing file.", "type": "string", - "visibleIf": { - "initializeFromBackupFile": [ - true - ] - }, "widget": { "id": "datalist", "data": [ diff --git a/ui/src/assets/schema/provider/filebacked-http-common.schema.json b/ui/src/assets/schema/provider/filebacked-http-common.schema.json index 2b318c44c..eb2dab07e 100644 --- a/ui/src/assets/schema/provider/filebacked-http-common.schema.json +++ b/ui/src/assets/schema/provider/filebacked-http-common.schema.json @@ -11,34 +11,7 @@ "useDefaultPredicateRegistry", "satisfyAnyPredicates" ], - "required": ["xmlId", "metadataURL"], - "dependencies": { - "initializeFromBackupFile": {"required": ["backingFile", "backupFileInitNextRefreshDelay"]} - }, - "anyOf": [ - { - "properties": { - "initializeFromBackupFile": { - "enum": [ - true - ] - } - }, - "required": [ - "backingFile", - "backupFileInitNextRefreshDelay" - ] - }, - { - "properties": { - "initializeFromBackupFile": { - "enum": [ - false - ] - } - } - } - ], + "required": ["xmlId", "metadataURL", "backingFile", "backupFileInitNextRefreshDelay"], "properties": { "xmlId": { "title": "ID", @@ -81,22 +54,12 @@ "title": "Backing File", "description": "Specifies where the backing file is located. If the remote server is unavailable at startup, the backing file is loaded instead.", "type": "string", - "default": "", - "visibleIf": { - "initializeFromBackupFile": [ - true - ] - } + "default": "" }, "backupFileInitNextRefreshDelay": { "title": "Backup File Init Next Refresh Delay", "description": "Delay duration after which to schedule next HTTP refresh when initialized from the backing file.", "type": "string", - "visibleIf": { - "initializeFromBackupFile": [ - true - ] - }, "widget": { "id": "datalist", "data": [ From 80b82d5bd34e05bb327140df073d768d430fe161 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 28 Aug 2018 09:59:21 -0700 Subject: [PATCH 4/4] SHIBUI-784 Implemented fixes for a11y issues --- .../component/provider-wizard-summary.component.html | 4 ++-- .../provider/component/summary-property.component.html | 2 +- ui/src/app/schema-form/widget/array/array.component.html | 7 ++++--- .../app/schema-form/widget/check/checklist.component.html | 4 +++- .../schema-form/widget/datalist/datalist.component.html | 5 +++-- .../widget/filter-target/filter-target.component.html | 8 ++++---- .../app/shared/autocomplete/autocomplete.component.html | 5 +++-- ui/src/app/shared/autocomplete/autocomplete.component.ts | 1 + ui/src/assets/schema/filter/entity-attributes.schema.json | 2 ++ 9 files changed, 23 insertions(+), 15 deletions(-) diff --git a/ui/src/app/metadata/provider/component/provider-wizard-summary.component.html b/ui/src/app/metadata/provider/component/provider-wizard-summary.component.html index a60ef4c1e..651bbb607 100644 --- a/ui/src/app/metadata/provider/component/provider-wizard-summary.component.html +++ b/ui/src/app/metadata/provider/component/provider-wizard-summary.component.html @@ -1,12 +1,12 @@
-
+
- +
diff --git a/ui/src/app/metadata/provider/component/summary-property.component.html b/ui/src/app/metadata/provider/component/summary-property.component.html index 664dcb82a..98b35d151 100644 --- a/ui/src/app/metadata/provider/component/summary-property.component.html +++ b/ui/src/app/metadata/provider/component/summary-property.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/ui/src/app/schema-form/widget/array/array.component.html b/ui/src/app/schema-form/widget/array/array.component.html index c85b8f8de..f36f2ed92 100644 --- a/ui/src/app/schema-form/widget/array/array.component.html +++ b/ui/src/app/schema-form/widget/array/array.component.html @@ -16,11 +16,12 @@
-
- -
+
+ +
diff --git a/ui/src/app/schema-form/widget/check/checklist.component.html b/ui/src/app/schema-form/widget/check/checklist.component.html index 7285f1bb9..ec85c8459 100644 --- a/ui/src/app/schema-form/widget/check/checklist.component.html +++ b/ui/src/app/schema-form/widget/check/checklist.component.html @@ -23,7 +23,9 @@ role="checkbox" aria-checked="false" /> - +
diff --git a/ui/src/app/schema-form/widget/datalist/datalist.component.html b/ui/src/app/schema-form/widget/datalist/datalist.component.html index ea1af0ec9..e66040336 100644 --- a/ui/src/app/schema-form/widget/datalist/datalist.component.html +++ b/ui/src/app/schema-form/widget/datalist/datalist.component.html @@ -1,6 +1,6 @@
-