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 52122b64f..2ec5f01ab 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 @@ -14,6 +14,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -21,12 +23,16 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.net.URI; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.springframework.http.HttpStatus.NOT_FOUND; + @RestController @RequestMapping("/api/MetadataResolvers/{metadataResolverId}") public class MetadataFiltersController { @@ -40,33 +46,33 @@ public class MetadataFiltersController { @Autowired private FilterRepository filterRepository; + private static final Supplier HTTP_404_CLIENT_ERROR_EXCEPTION = () -> new HttpClientErrorException(NOT_FOUND); + + @ExceptionHandler + public ResponseEntity notFoundHandler(HttpClientErrorException ex) { + if(ex.getStatusCode() == NOT_FOUND) { + return ResponseEntity.notFound().build(); + } + throw ex; + } + @GetMapping("/Filters") @Transactional(readOnly = true) public ResponseEntity getAll(@PathVariable String metadataResolverId) { - MetadataResolver resolver = repository.findByResourceId(metadataResolverId); - if(resolver == null) { - return ResponseEntity.notFound().build(); - } + MetadataResolver resolver = findResolverOrThrowHttp404(metadataResolverId); return ResponseEntity.ok(resolver.getMetadataFilters()); } @GetMapping("/Filters/{resourceId}") + @Transactional(readOnly = true) public ResponseEntity getOne(@PathVariable String metadataResolverId, @PathVariable String resourceId) { - MetadataResolver resolver = repository.findByResourceId(metadataResolverId); - if(resolver == null) { - return ResponseEntity.notFound().build(); - } - return ResponseEntity.ok(resolver.getMetadataFilters().stream() - .filter(f -> f.getResourceId().equals(resourceId)) - .collect(Collectors.toList()).get(0)); + MetadataResolver resolver = findResolverOrThrowHttp404(metadataResolverId); + return ResponseEntity.ok(findFilterOrThrowHttp404(resolver, resourceId)); } @PostMapping("/Filters") public ResponseEntity create(@PathVariable String metadataResolverId, @RequestBody MetadataFilter createdFilter) { - MetadataResolver metadataResolver = repository.findByResourceId(metadataResolverId); - if(metadataResolver == null) { - return ResponseEntity.notFound().build(); - } + MetadataResolver metadataResolver = findResolverOrThrowHttp404(metadataResolverId); metadataResolver.getMetadataFilters().add(createdFilter); MetadataResolver persistedMr = repository.save(metadataResolver); @@ -78,7 +84,6 @@ public ResponseEntity create(@PathVariable String metadataResolverId, @Reques return ResponseEntity .created(getResourceUriFor(persistedMr, createdFilter.getResourceId())) .body(persistedFilter); - } @PutMapping("/Filters/{resourceId}") @@ -90,10 +95,7 @@ public ResponseEntity update(@PathVariable String metadataResolverId, return ResponseEntity.notFound().build(); } - MetadataResolver metadataResolver = repository.findByResourceId(metadataResolverId); - if(metadataResolver == null) { - return ResponseEntity.notFound().build(); - } + MetadataResolver metadataResolver = findResolverOrThrowHttp404(metadataResolverId); // check to make sure that the relationship exists if (!metadataResolver.getMetadataFilters().contains(filterTobeUpdated)) { @@ -122,6 +124,40 @@ public ResponseEntity update(@PathVariable String metadataResolverId, return ResponseEntity.ok().body(persistedFilter); } + @DeleteMapping("/Filters/{resourceId}") + @Transactional + public ResponseEntity delete(@PathVariable String metadataResolverId, + @PathVariable String resourceId) { + + MetadataResolver resolver = findResolverOrThrowHttp404(metadataResolverId); + //TODO: consider implementing delete of filter directly from RDBMS via FilterRepository + boolean removed = resolver.getMetadataFilters().removeIf(f -> f.getResourceId().equals(resourceId)); + if(!removed) { + throw HTTP_404_CLIENT_ERROR_EXCEPTION.get(); + } + repository.save(resolver); + + //TODO: do we need to reload filters here?!? + //metadataResolverService.reloadFilters(persistedMr.getName()); + + return ResponseEntity.noContent().build(); + } + + private MetadataResolver findResolverOrThrowHttp404(String resolverResourceId) { + MetadataResolver resolver = repository.findByResourceId(resolverResourceId); + if(resolver == null) { + throw HTTP_404_CLIENT_ERROR_EXCEPTION.get(); + } + 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 newlyPersistedFilter(Stream filters, final String filterResourceId) { MetadataFilter persistedFilter = filters .filter(f -> f.getResourceId().equals(filterResourceId)) @@ -180,4 +216,4 @@ private static URI getResourceUriFor(MetadataResolver mr, String filterResourceI .build() .toUri(); } -} +} \ No newline at end of file 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 557fdf56e..e65a7e963 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 @@ -103,6 +103,29 @@ class MetadataFiltersControllerIntegrationTests extends Specification { updatedResultFromPUT.statusCode.value() == 200 } + def "DELETE Filter"() { + given: 'MetadataResolver with attached filter is available in data store' + def resolver = generator.buildRandomMetadataResolverOfType('FileBacked') + resolver.metadataFilters << generator.entityAttributesFilter() + def filterResourceId = resolver.metadataFilters[0].resourceId + def resolverResourceId = resolver.resourceId + metadataResolverRepository.save(resolver) + + + when: 'GET request is made with resource Id matching the existing filter' + def result = this.restTemplate.getForEntity("$BASE_URI/$resolverResourceId/Filters/$filterResourceId", String) + + then: + result.statusCode.value() == 200 + + and: 'DELETE call is made and then GET call is made for the just deleted resource' + restTemplate.delete("$BASE_URI/$resolverResourceId/Filters/$filterResourceId") + def GETResultAfterDelete = this.restTemplate.getForEntity("$BASE_URI/$resolverResourceId/Filters/$filterResourceId", String) + + then: 'The deleted resource is gone' + GETResultAfterDelete.statusCode.value() == 404 + } + private HttpEntity createRequestHttpEntityFor(Closure jsonBodySupplier) { new HttpEntity(jsonBodySupplier(), ['Content-Type': 'application/json'] as HttpHeaders) } diff --git a/ui/src/app/metadata/domain/service/filter.service.ts b/ui/src/app/metadata/domain/service/filter.service.ts index 7334ee301..92dabd797 100644 --- a/ui/src/app/metadata/domain/service/filter.service.ts +++ b/ui/src/app/metadata/domain/service/filter.service.ts @@ -18,7 +18,6 @@ export class MetadataFilterService { } find(providerId: string, filterId: string): Observable { - // console.log(id); return this.http.get(`${this.base}${this.endpoint}/${providerId}/Filters/${ filterId }`); } @@ -27,7 +26,10 @@ export class MetadataFilterService { } save(providerId: string, filter: MetadataFilter): Observable { - console.log(providerId, filter); return this.http.post(`${this.base}${this.endpoint}/${providerId}/Filters`, filter); } + + remove(providerId: string, filterId: string): Observable { + return this.http.delete(`${this.base}${this.endpoint}/${providerId}/Filters/${ filterId }`); + } } diff --git a/ui/src/app/metadata/filter/action/collection.action.ts b/ui/src/app/metadata/filter/action/collection.action.ts index c93c563d8..e2b38793d 100644 --- a/ui/src/app/metadata/filter/action/collection.action.ts +++ b/ui/src/app/metadata/filter/action/collection.action.ts @@ -99,19 +99,19 @@ export class AddFilterFail implements Action { export class RemoveFilterRequest implements Action { readonly type = FilterCollectionActionTypes.REMOVE_FILTER_REQUEST; - constructor(public payload: MetadataFilter) { } + constructor(public payload: string) { } } export class RemoveFilterSuccess implements Action { readonly type = FilterCollectionActionTypes.REMOVE_FILTER_SUCCESS; - constructor(public payload: MetadataFilter) { } + constructor(public payload: string) { } } export class RemoveFilterFail implements Action { readonly type = FilterCollectionActionTypes.REMOVE_FILTER_FAIL; - constructor(public payload: MetadataFilter) { } + constructor(public error: Error) { } } export type FilterCollectionActionsUnion = diff --git a/ui/src/app/metadata/filter/effect/collection.effect.ts b/ui/src/app/metadata/filter/effect/collection.effect.ts index cc41754c2..779bb088f 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), @@ -104,6 +113,27 @@ export class FilterCollectionEffects { tap(([filter, provider]) => this.router.navigate(['/', 'metadata', 'provider', provider, 'filters'])) ); + @Effect() + removeFilterRequest$ = this.actions$.pipe( + ofType(FilterCollectionActionTypes.REMOVE_FILTER_REQUEST), + map(action => action.payload), + withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), + switchMap(([filterId, providerId]) => + this.filterService.remove(providerId, filterId).pipe( + map(removed => new actions.RemoveFilterSuccess(removed)), + catchError(err => of(new actions.RemoveFilterFail(err))) + ) + ) + ); + + @Effect() + removeFilterSuccess$ = this.actions$.pipe( + ofType(FilterCollectionActionTypes.REMOVE_FILTER_SUCCESS), + map(action => action.payload), + withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), + map(([filter, providerId]) => new actions.LoadFilterRequest(providerId)) + ); + constructor( private actions$: Actions, private router: Router, diff --git a/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts b/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts index dfe5fc107..8ff807401 100644 --- a/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts +++ b/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts @@ -10,7 +10,8 @@ import { UpdateFilterRequest, AddFilterSuccess, AddFilterFail, - UpdateFilterFail + UpdateFilterFail, + RemoveFilterFail } from '../action/collection.action'; import { EntityAttributesFilterEntity } from '../../domain/entity/filter/entity-attributes-filter'; @@ -93,6 +94,13 @@ describe('Filter Reducer', () => { }); }); + describe(`${FilterCollectionActionTypes.REMOVE_FILTER_FAIL}`, () => { + it('should set saving to false', () => { + const action = new RemoveFilterFail(new Error('foo')); + expect(reducer(snapshot, action).saving).toBe(false); + }); + }); + describe(`${FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS}`, () => { it('should update the filter in the collection', () => { spyOn(fromFilter.adapter, 'updateOne').and.callThrough(); diff --git a/ui/src/app/metadata/filter/reducer/collection.reducer.ts b/ui/src/app/metadata/filter/reducer/collection.reducer.ts index 35074b696..1f3788c54 100644 --- a/ui/src/app/metadata/filter/reducer/collection.reducer.ts +++ b/ui/src/app/metadata/filter/reducer/collection.reducer.ts @@ -58,6 +58,7 @@ export function reducer(state = initialState, action: FilterCollectionActionsUni case FilterCollectionActionTypes.ADD_FILTER_SUCCESS: case FilterCollectionActionTypes.ADD_FILTER_FAIL: + case FilterCollectionActionTypes.REMOVE_FILTER_FAIL: case FilterCollectionActionTypes.UPDATE_FILTER_FAIL: { return { ...state, @@ -65,6 +66,20 @@ export function reducer(state = initialState, action: FilterCollectionActionsUni }; } + case FilterCollectionActionTypes.REMOVE_FILTER_SUCCESS: { + return adapter.removeOne(action.payload, { + ...state, + saving: false + }); + } + + case FilterCollectionActionTypes.REMOVE_FILTER_REQUEST: { + return { + ...state, + saving: true + }; + } + case FilterCollectionActionTypes.SELECT_FILTER_REQUEST: { return { ...state, diff --git a/ui/src/app/metadata/filter/reducer/index.ts b/ui/src/app/metadata/filter/reducer/index.ts index 5d9a4ecbe..60cbe4b34 100644 --- a/ui/src/app/metadata/filter/reducer/index.ts +++ b/ui/src/app/metadata/filter/reducer/index.ts @@ -52,7 +52,7 @@ export const getAllFilters = createSelector(getCollectionState, fromCollection.s export const getCollectionSaving = createSelector(getCollectionState, fromCollection.getIsSaving); export const notAddtlFilters = ['RequiredValidUntil', 'SignatureValidation', 'EntityRoleWhiteList']; -export const filterTypeFn = filters => filters.filter(f => notAddtlFilters.indexOf(f['@type']) === -1); +export const filterTypeFn = filters => [...filters.filter(f => notAddtlFilters.indexOf(f['@type']) === -1)]; export const getAdditionalFilters = createSelector(getAllFilters, filterTypeFn); diff --git a/ui/src/app/metadata/provider/container/provider-filter-list.component.html b/ui/src/app/metadata/provider/container/provider-filter-list.component.html index 53e99847c..3f29393c0 100644 --- a/ui/src/app/metadata/provider/container/provider-filter-list.component.html +++ b/ui/src/app/metadata/provider/container/provider-filter-list.component.html @@ -40,7 +40,7 @@ Filter Type Enabled? Edit - + Delete @@ -79,14 +79,12 @@ Edit - diff --git a/ui/src/app/metadata/provider/container/provider-filter-list.component.spec.ts b/ui/src/app/metadata/provider/container/provider-filter-list.component.spec.ts index f24673419..7b850fc33 100644 --- a/ui/src/app/metadata/provider/container/provider-filter-list.component.spec.ts +++ b/ui/src/app/metadata/provider/container/provider-filter-list.component.spec.ts @@ -59,4 +59,11 @@ describe('Provider Filter List Component', () => { it('should instantiate the component', async(() => { expect(app).toBeTruthy(); })); + + describe('remove method', () => { + it('should dispatch an action to the store', () => { + app.remove('foo'); + expect(store.dispatch).toHaveBeenCalled(); + }); + }); }); diff --git a/ui/src/app/metadata/provider/container/provider-filter-list.component.ts b/ui/src/app/metadata/provider/container/provider-filter-list.component.ts index 92c346505..0b1fee37b 100644 --- a/ui/src/app/metadata/provider/container/provider-filter-list.component.ts +++ b/ui/src/app/metadata/provider/container/provider-filter-list.component.ts @@ -7,7 +7,7 @@ import * as fromFilter from '../../filter/reducer'; import { MetadataFilter, MetadataProvider } from '../../domain/model'; import { NAV_FORMATS } from '../component/provider-editor-nav.component'; import { SetIndex } from '../../../wizard/action/wizard.action'; -import { UpdateFilterRequest, LoadFilterRequest } from '../../filter/action/collection.action'; +import { UpdateFilterRequest, LoadFilterRequest, RemoveFilterRequest } from '../../filter/action/collection.action'; @Component({ selector: 'provider-filter-list', @@ -30,7 +30,9 @@ export class ProviderFilterListComponent implements OnDestroy { this.filters$ = this.store.select(fromFilter.getAdditionalFilters); this.provider$ = this.store.select(fromProvider.getSelectedProvider).pipe(skipWhile(p => !p)); this.provider$ - .pipe(takeUntil(this.ngUnsubscribe)) + .pipe( + takeUntil(this.ngUnsubscribe) + ) .subscribe(p => { this.store.dispatch(new LoadFilterRequest(p.resourceId)); }); @@ -44,6 +46,10 @@ export class ProviderFilterListComponent implements OnDestroy { this.store.dispatch(new UpdateFilterRequest({ ...filter, filterEnabled: !filter.filterEnabled })); } + remove(id: string): void { + this.store.dispatch(new RemoveFilterRequest(id)); + } + ngOnDestroy(): void { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); diff --git a/ui/src/app/metadata/provider/container/provider-select.component.html b/ui/src/app/metadata/provider/container/provider-select.component.html index 90c6b6463..5389bd7e9 100644 --- a/ui/src/app/metadata/provider/container/provider-select.component.html +++ b/ui/src/app/metadata/provider/container/provider-select.component.html @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file 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..5124e7fac 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 @@ -1,7 +1,17 @@
-
+
+ -   -

-
-
- - - - You must add at least one entity id target. - - -

- - Required for Scripts -   - -
- - - - Required for Regex -   - - -
-
- +
+ +
+
+ + + + + You must add at least one entity id target and they must each be unique. + + + +

+ + Required for Scripts +   + +
+ + + + Required for Regex +   + + +
+
+ +
-
-
    +
    + +
    +
    • {{ id }}
    diff --git a/ui/src/app/shared/autocomplete/autocomplete.component.ts b/ui/src/app/shared/autocomplete/autocomplete.component.ts index d6237742a..51fc555fb 100644 --- a/ui/src/app/shared/autocomplete/autocomplete.component.ts +++ b/ui/src/app/shared/autocomplete/autocomplete.component.ts @@ -40,13 +40,12 @@ const INPUT_FIELD_INDEX = -1; } ] }) -export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit, ControlValueAccessor { +export class AutoCompleteComponent implements OnInit, OnDestroy, AfterViewInit, ControlValueAccessor { @Input() defaultValue = ''; @Input() matches: string[] = []; @Input() id: string; @Input() autoSelect = false; @Input() noneFoundText = 'No Options Found'; - @Input() showMoreText = 'Show More...'; @Input() limit = 0; @Input() processing = false; @Input() dropdown = false; @@ -119,12 +118,6 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte this.listItems.changes.subscribe((changes) => this.setElementReferences(changes)); } - ngOnChanges(changes: SimpleChanges): void { - if (changes.matches) { - this.showMoreAvailable = !!this.limit && this.matches && this.matches.length >= this.limit; - } - } - writeValue(value: any): void { this.input.setValue(value); } 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 98545e7e4..2b5d368e4 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 @@ -50,7 +50,8 @@ "type": "section", "fields": [ "name", - "@type" + "@type", + "enabled" ] }, { @@ -96,6 +97,12 @@ } ] }, + "enabled": { + "title": "Enable this service?", + "description": "Enable this service?", + "type": "boolean", + "default": false + }, "xmlId": { "title": "ID", "description": "Identifier for logging, identification for command line reload, etc.",