diff --git a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy index b60c9b0c8..f63dbad31 100644 --- a/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy +++ b/backend/src/main/groovy/edu/internet2/tier/shibboleth/admin/ui/service/JPAMetadataResolverServiceImpl.groovy @@ -43,6 +43,9 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { @Autowired private OpenSamlObjects openSamlObjects + @Autowired + private MetadataResolversPositionOrderContainerService resolversPositionOrderContainerService + // TODO: enhance @Override void reloadFilters(String metadataResolverName) { @@ -98,8 +101,11 @@ class JPAMetadataResolverServiceImpl implements MetadataResolverService { 'xsi:type': 'ChainingMetadataProvider', 'xsi:schemaLocation': 'urn:mace:shibboleth:2.0:metadata http://shibboleth.net/schema/idp/shibboleth-metadata.xsd urn:mace:shibboleth:2.0:resource http://shibboleth.net/schema/idp/shibboleth-resource.xsd urn:mace:shibboleth:2.0:security http://shibboleth.net/schema/idp/shibboleth-security.xsd urn:oasis:names:tc:SAML:2.0:metadata http://docs.oasis-open.org/security/saml/v2.0/saml-schema-metadata-2.0.xsd urn:oasis:names:tc:SAML:2.0:assertion http://docs.oasis-open.org/security/saml/v2.0/saml-schema-assertion-2.0.xsd' ) { - metadataResolverRepository.findAll().each { edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr -> - //TODO: We cannot/do not currently have the code to marshall the internal incommon chaining resolver + + + resolversPositionOrderContainerService.allMetadataResolversInDefinedOrderOrUnordered.each { + edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver mr -> + //TODO: We do not currently marshall the internal incommon chaining resolver (with BaseMetadataResolver type) if ((mr.type != 'BaseMetadataResolver') && (mr.enabled)) { constructXmlNodeForResolver(mr, delegate) { MetadataFilter( diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java index ca7be77ea..c630752f8 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/CoreShibUiConfiguration.java @@ -1,8 +1,11 @@ package edu.internet2.tier.shibboleth.admin.ui.configuration; import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityIdsSearchResultRepresentation; +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer; import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects; import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository; +import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; +import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolversPositionOrderContainerRepository; import edu.internet2.tier.shibboleth.admin.ui.scheduled.EntityDescriptorFilesScheduledTasks; import edu.internet2.tier.shibboleth.admin.ui.service.*; import edu.internet2.tier.shibboleth.admin.util.AttributeUtility; @@ -162,4 +165,14 @@ public void addInterceptors(InterceptorRegistry registry) { } }; } + + @Bean + public MetadataResolversPositionOrderContainerService + metadataResolversPositionOrderContainerService(MetadataResolversPositionOrderContainerRepository + positionOrderContainerRepository, + MetadataResolverRepository resolverRepository) { + + return new DefaultMetadataResolversPositionOrderContainerService(positionOrderContainerRepository, resolverRepository); + + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java index 9705ddacc..2b1ddc6b5 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java @@ -5,6 +5,7 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidationService; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; +import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolversPositionOrderContainerService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -30,6 +31,7 @@ import java.io.IOException; import java.io.StringWriter; import java.net.URI; +import java.util.List; import static edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolverValidator.ValidationResult; @@ -47,6 +49,9 @@ public class MetadataResolversController { @Autowired MetadataResolverService metadataResolverService; + @Autowired + MetadataResolversPositionOrderContainerService positionOrderContainerService; + @ExceptionHandler({InvalidTypeIdException.class, IOException.class, HttpMessageNotReadableException.class}) public ResponseEntity unableToParseJson(Exception ex) { return ResponseEntity.badRequest().body(new ErrorResponse(HttpStatus.BAD_REQUEST.toString(), ex.getMessage())); @@ -55,7 +60,7 @@ public ResponseEntity unableToParseJson(Exception ex) { @GetMapping("/MetadataResolvers") @Transactional(readOnly = true) public ResponseEntity getAll() { - Iterable resolvers = resolverRepository.findAll(); + List resolvers = positionOrderContainerService.getAllMetadataResolversInDefinedOrderOrUnordered(); resolvers.forEach(MetadataResolver::updateVersion); return ResponseEntity.ok(resolvers); } @@ -99,6 +104,7 @@ public ResponseEntity create(@RequestBody MetadataResolver newResolver) { newResolver.convertFiltersFromTransientRepresentationIfNecessary(); MetadataResolver persistedResolver = resolverRepository.save(newResolver); + positionOrderContainerService.appendPositionOrderForNew(persistedResolver); persistedResolver.updateVersion(); persistedResolver.convertFiltersIntoTransientRepresentationIfNecessary(); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversPositionOrderController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversPositionOrderController.java new file mode 100644 index 000000000..ab78e4a52 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversPositionOrderController.java @@ -0,0 +1,34 @@ +package edu.internet2.tier.shibboleth.admin.ui.controller; + + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer; +import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolversPositionOrderContainerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author Dmitriy Kopylenko + */ +@RestController +@RequestMapping("/api/MetadataResolversPositionOrder") +public class MetadataResolversPositionOrderController { + + @Autowired + MetadataResolversPositionOrderContainerService positionOrderContainerService; + + @PostMapping + public ResponseEntity createOrUpdate(@RequestBody MetadataResolversPositionOrderContainer metadataResolversPositionOrderContainer) { + positionOrderContainerService.addOrUpdatePositionOrderContainer(metadataResolversPositionOrderContainer); + return ResponseEntity.noContent().build(); + } + + @GetMapping + public ResponseEntity getPositionOrderContainer() { + return ResponseEntity.ok(positionOrderContainerService.retrieveExistingOrEmpty()); + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolversOrderContainer.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolversOrderContainer.java new file mode 100644 index 000000000..5b5d0d87f --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolversOrderContainer.java @@ -0,0 +1,14 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers; + +/** + * This is a persistent entity abstraction encapsulating a collection of metadata resolver ids + * for the purpose of maintaining an order of all persistent metadata resolvers which becomes significant during + * generation of XML metadata for the resolvers. + * + * Maintaining this separate entity enables UI layer for example to explicitly manipulate ordering e.g. use REST + * API to reorder resolvers, etc. + * + * @author Dmitriy + */ +public class MetadataResolversOrderContainer { +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolversPositionOrderContainer.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolversPositionOrderContainer.java new file mode 100644 index 000000000..883069531 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolversPositionOrderContainer.java @@ -0,0 +1,50 @@ +package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OrderColumn; +import java.util.ArrayList; +import java.util.List; + +/** + * This is a persistent entity abstraction encapsulating a collection of metadata resolver ids + * for the purpose of maintaining an order of all persistent metadata resolvers which becomes significant during + * generation of XML metadata for the resolvers. + * + * Maintaining this separate entity enables UI layer for example to explicitly manipulate ordering e.g. use REST + * API to reorder resolvers, etc. + * + * @author Dmitriy Kopylenko + */ +@Entity +@EqualsAndHashCode +@NoArgsConstructor +@Getter +@Setter +@ToString +public class MetadataResolversPositionOrderContainer { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + @JsonIgnore + protected Long id; + + @ElementCollection + @CollectionTable(name="METADATA_RESOLVER_POSITION_ORDER", joinColumns=@JoinColumn(name="METADATA_RESOLVER_POSITION_ORDER_CONTAINER_ID")) + @Column(name="METADATA_RESOLVER_RESOURCE_ID") + @OrderColumn + private List resourceIds = new ArrayList<>(); +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolversPositionOrderContainerRepository.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolversPositionOrderContainerRepository.java new file mode 100644 index 000000000..6afe35ded --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/repository/MetadataResolversPositionOrderContainerRepository.java @@ -0,0 +1,13 @@ +package edu.internet2.tier.shibboleth.admin.ui.repository; + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer; +import org.springframework.data.repository.CrudRepository; + +/** + * Spring Data Repository API for persistence operations on instances of {@link MetadataResolversPositionOrderContainer}. + * + * @author Dmitriy Kopylenko + */ +public interface MetadataResolversPositionOrderContainerRepository + extends CrudRepository { +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/DefaultMetadataResolversPositionOrderContainerService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/DefaultMetadataResolversPositionOrderContainerService.java new file mode 100644 index 000000000..76ac86cea --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/DefaultMetadataResolversPositionOrderContainerService.java @@ -0,0 +1,77 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer; +import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; +import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolversPositionOrderContainerRepository; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Iterator; +import java.util.List; +import java.util.Optional; + +import static com.google.common.collect.FluentIterable.from; +import static java.util.stream.Collectors.toList; + + +/** + * Default implementation of {@link MetadataResolversPositionOrderContainer}. + * + * @author Dmitriy Kopylenko + */ +public class DefaultMetadataResolversPositionOrderContainerService implements MetadataResolversPositionOrderContainerService { + + private MetadataResolversPositionOrderContainerRepository positionOrderContainerRepository; + + private MetadataResolverRepository metadataResolverRepository; + + public DefaultMetadataResolversPositionOrderContainerService(MetadataResolversPositionOrderContainerRepository positionOrderRepository, + MetadataResolverRepository metadataResolverRepository) { + this.positionOrderContainerRepository = positionOrderRepository; + this.metadataResolverRepository = metadataResolverRepository; + } + + @Override + @Transactional + public void addOrUpdatePositionOrderContainer(MetadataResolversPositionOrderContainer metadataResolversPositionOrderContainer) { + MetadataResolversPositionOrderContainer existingPositionOrder = getPositionOrderContainerIfExists().orElse(null); + if (existingPositionOrder != null) { + existingPositionOrder.setResourceIds(metadataResolversPositionOrderContainer.getResourceIds()); + positionOrderContainerRepository.save(existingPositionOrder); + return; + } + positionOrderContainerRepository.save(metadataResolversPositionOrderContainer); + } + + @Override + @Transactional(readOnly = true) + public List getAllMetadataResolversInDefinedOrderOrUnordered() { + Optional orderContainer = getPositionOrderContainerIfExists(); + if(orderContainer.isPresent()) { + return orderContainer.get().getResourceIds() + .stream() + .map(metadataResolverRepository::findByResourceId) + .collect(toList()); + } + + return from(metadataResolverRepository.findAll()).toList(); + } + + @Override + public MetadataResolversPositionOrderContainer retrieveExistingOrEmpty() { + return getPositionOrderContainerIfExists().orElseGet(MetadataResolversPositionOrderContainer::new); + } + + @Override + @Transactional + public void appendPositionOrderForNew(MetadataResolver metadataResolver) { + MetadataResolversPositionOrderContainer positionOrderContainer = retrieveExistingOrEmpty(); + positionOrderContainer.getResourceIds().add(metadataResolver.getResourceId()); + positionOrderContainerRepository.save(positionOrderContainer); + } + + private Optional getPositionOrderContainerIfExists() { + Iterator iter = positionOrderContainerRepository.findAll().iterator(); + return iter.hasNext() ? Optional.of(iter.next()) : Optional.empty(); + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolversPositionOrderContainerService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolversPositionOrderContainerService.java new file mode 100644 index 000000000..7de6490af --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolversPositionOrderContainerService.java @@ -0,0 +1,23 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer; + +import java.util.List; + +/** + * Service interface for manipulation of instances of {@link MetadataResolversPositionOrderContainer} and + * to abstract away MetadataResolvers ordering logic. + * + * @author Dmitriy Kopylenko + */ +public interface MetadataResolversPositionOrderContainerService { + + MetadataResolversPositionOrderContainer retrieveExistingOrEmpty(); + + void addOrUpdatePositionOrderContainer(MetadataResolversPositionOrderContainer metadataResolversPositionOrderContainer); + + List getAllMetadataResolversInDefinedOrderOrUnordered(); + + void appendPositionOrderForNew(MetadataResolver metadataResolver); +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolversPositionOrderContainerServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolversPositionOrderContainerServiceTests.groovy new file mode 100644 index 000000000..de4af7e8e --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/MetadataResolversPositionOrderContainerServiceTests.groovy @@ -0,0 +1,53 @@ +package edu.internet2.tier.shibboleth.admin.ui.service + +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolversPositionOrderContainer +import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository +import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolversPositionOrderContainerRepository +import spock.lang.Specification +import spock.lang.Subject + +/** + * @author Dmitriy Kopylenko + */ +class MetadataResolversPositionOrderContainerServiceTests extends Specification { + + def "no order container has been provided and saved, so using unordered persisted resolvers"() { + given: + def resolverRepo = Mock(MetadataResolverRepository) + resolverRepo.findAll() >> [new MetadataResolver(resourceId: 'second'), new MetadataResolver(resourceId: 'first')] + def positionOrderRepo = Mock(MetadataResolversPositionOrderContainerRepository) + positionOrderRepo.findAll() >> [] + @Subject + def positionContainerSvc = new DefaultMetadataResolversPositionOrderContainerService(positionOrderRepo, resolverRepo) + + when: + def unorderedResolvers = positionContainerSvc.getAllMetadataResolversInDefinedOrderOrUnordered() + + then: + unorderedResolvers[0].resourceId == 'second' + unorderedResolvers[1].resourceId == 'first' + + } + + def "an order container has been provided and saved, so using resolvers with order defined in that position order container"() { + given: + def resolverRepo = Mock(MetadataResolverRepository) + resolverRepo.findAll() >> [new MetadataResolver(resourceId: 'second'), new MetadataResolver(resourceId: 'first')] + resolverRepo.findByResourceId('first') >> new MetadataResolver(resourceId: 'first') + resolverRepo.findByResourceId('second') >> new MetadataResolver(resourceId: 'second') + def positionOrderRepo = Mock(MetadataResolversPositionOrderContainerRepository) + positionOrderRepo.findAll() >> [new MetadataResolversPositionOrderContainer(resourceIds: ['first', 'second'])] + positionOrderRepo.findAll() >> [] + @Subject + def positionContainerSvc = new DefaultMetadataResolversPositionOrderContainerService(positionOrderRepo, resolverRepo) + + when: + def orderedResolvers = positionContainerSvc.getAllMetadataResolversInDefinedOrderOrUnordered() + + then: + orderedResolvers[0].resourceId == 'first' + orderedResolvers[1].resourceId == 'second' + + } +} diff --git a/ui/src/app/metadata/filter/effect/filter.effect.ts b/ui/src/app/metadata/filter/effect/filter.effect.ts index a83a3e9ab..0c0ce98fe 100644 --- a/ui/src/app/metadata/filter/effect/filter.effect.ts +++ b/ui/src/app/metadata/filter/effect/filter.effect.ts @@ -57,7 +57,7 @@ export class FilterEffects { ) ); - @Effect({dispatch: false}) + @Effect({ dispatch: false }) cancelChanges$ = this.actions$.pipe( ofType(FilterActionTypes.CANCEL_CREATE_FILTER), combineLatest(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), diff --git a/ui/src/app/metadata/provider/action/collection.action.ts b/ui/src/app/metadata/provider/action/collection.action.ts index 7eaf7d1df..6746ffad5 100644 --- a/ui/src/app/metadata/provider/action/collection.action.ts +++ b/ui/src/app/metadata/provider/action/collection.action.ts @@ -45,7 +45,7 @@ export class LoadProviderError implements Action { export class SelectProviderRequest implements Action { readonly type = ProviderCollectionActionTypes.SELECT_PROVIDER_REQUEST; - constructor(public payload: any) { } + constructor(public payload: string) { } } export class SelectProviderSuccess implements Action { diff --git a/ui/src/app/metadata/provider/effect/collection.effect.ts b/ui/src/app/metadata/provider/effect/collection.effect.ts index a3f8aa723..df8fc574d 100644 --- a/ui/src/app/metadata/provider/effect/collection.effect.ts +++ b/ui/src/app/metadata/provider/effect/collection.effect.ts @@ -23,6 +23,8 @@ import { } from '../action/collection.action'; 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'; /* istanbul ignore next */ @@ -114,10 +116,21 @@ export class CollectionEffects { map(provider => new LoadProviderRequest()) ); + @Effect({ dispatch: false }) + newFilterSuccessUpdate = this.actions$.pipe( + ofType(FilterCollectionActionTypes.ADD_FILTER_SUCCESS), + map(action => action.payload), + withLatestFrom(this.store.select(fromProvider.getSelectedProviderId)), + map(([filter, id]) => id), + tap(id => { + this.store.dispatch(new SelectProviderRequest(id)); + }) + ); + constructor( private actions$: Actions, private router: Router, - private store: Store, + private store: Store, private providerService: MetadataProviderService ) { } } /* istanbul ignore next */ 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 1dc7c42c4..61b522b0c 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 @@ -47,10 +47,11 @@
+

You must add at least one entity id target.

  • {{ 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 badc78eec..5912c9cd5 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 @@ -97,6 +97,11 @@ export class FilterTargetComponent extends ObjectWidget implements OnDestroy, Af this.search.setValue(null); } + removeId(id: string): void { + let rest = this.targets.filter(target => target !== id); + this.setTargetValue(rest); + } + setTargetValue(value: string[]): void { this.formProperty.getProperty('value').setValue(value); } diff --git a/ui/src/app/schema-form/widget/select/select.component.html b/ui/src/app/schema-form/widget/select/select.component.html index 4a60d6425..174f6798d 100644 --- a/ui/src/app/schema-form/widget/select/select.component.html +++ b/ui/src/app/schema-form/widget/select/select.component.html @@ -18,7 +18,6 @@