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 c630752f8..18a8a4953 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,23 +1,36 @@ 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.ui.service.DefaultMetadataResolversPositionOrderContainerService; +import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryService; +import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryServiceImpl; +import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService; +import edu.internet2.tier.shibboleth.admin.ui.service.EntityIdsSearchService; +import edu.internet2.tier.shibboleth.admin.ui.service.EntityService; +import edu.internet2.tier.shibboleth.admin.ui.service.FilterService; +import edu.internet2.tier.shibboleth.admin.ui.service.FilterTargetService; +import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityDescriptorServiceImpl; +import edu.internet2.tier.shibboleth.admin.ui.service.JPAEntityServiceImpl; +import edu.internet2.tier.shibboleth.admin.ui.service.JPAFilterServiceImpl; +import edu.internet2.tier.shibboleth.admin.ui.service.JPAFilterTargetServiceImpl; +import edu.internet2.tier.shibboleth.admin.ui.service.JPAMetadataResolverServiceImpl; +import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; +import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolversPositionOrderContainerService; import edu.internet2.tier.shibboleth.admin.util.AttributeUtility; +import edu.internet2.tier.shibboleth.admin.util.LuceneUtility; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.Document; -import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; -import org.apache.lucene.store.Directory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -83,7 +96,7 @@ public AttributeUtility attributeUtility() { Analyzer fullTokenAnalyzer; @Autowired - Directory directory; + DirectoryService directoryService; @Autowired LocaleResolver localeResolver; @@ -91,6 +104,9 @@ public AttributeUtility attributeUtility() { @Autowired ResourceBundleMessageSource messageSource; + @Autowired + LuceneUtility luceneUtility; + @Bean public EntityDescriptorFilesScheduledTasks entityDescriptorFilesScheduledTasks(EntityDescriptorRepository entityDescriptorRepository) { return new EntityDescriptorFilesScheduledTasks(this.metadataDir, entityDescriptorRepository, openSamlObjects()); @@ -98,10 +114,11 @@ public EntityDescriptorFilesScheduledTasks entityDescriptorFilesScheduledTasks(E @Bean public EntityIdsSearchService entityIdsSearchService() { - return (term, limit) -> { + return (resourceId, term, limit) -> { List entityIds = new ArrayList<>(); try { - IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(directory)); + IndexReader indexReader = luceneUtility.getIndexReader(resourceId); + IndexSearcher searcher = new IndexSearcher(indexReader); QueryParser parser = new QueryParser("content", fullTokenAnalyzer); TopDocs topDocs = searcher.search(parser.parse(term.trim()), limit); for (ScoreDoc scoreDoc : topDocs.scoreDocs) { @@ -175,4 +192,14 @@ public void addInterceptors(InterceptorRegistry registry) { return new DefaultMetadataResolversPositionOrderContainerService(positionOrderContainerRepository, resolverRepository); } + + @Bean + public DirectoryService directoryService() { + return new DirectoryServiceImpl(); + } + + @Bean + public LuceneUtility luceneUtility() { + return new LuceneUtility(); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java index 1968841c1..cdcd54f02 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/MetadataResolverConfiguration.java @@ -2,6 +2,7 @@ import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects; import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository; +import edu.internet2.tier.shibboleth.admin.ui.service.IndexWriterService; import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import net.shibboleth.utilities.java.support.resolver.ResolverException; import org.apache.http.HttpResponse; @@ -38,7 +39,7 @@ public class MetadataResolverConfiguration { OpenSamlObjects openSamlObjects; @Autowired - IndexWriter indexWriter; + IndexWriterService indexWriterService; @Autowired MetadataResolverRepository metadataResolverRepository; @@ -50,13 +51,22 @@ public MetadataResolver metadataResolver() throws ResolverException, ComponentIn List resolvers = new ArrayList<>(); + String incommonMRId = "incommonmd"; // TODO: remove this later when we allow for creation of arbitrary metadata resolvers FileBackedHTTPMetadataResolver incommonMR = new FileBackedHTTPMetadataResolver(HttpClients.createMinimal(), "http://md.incommon.org/InCommon/InCommon-metadata.xml", "/tmp/incommonmd.xml"){ @Override protected void initMetadataResolver() throws ComponentInitializationException { super.initMetadataResolver(); + IndexWriter indexWriter; + try { + indexWriter = indexWriterService.getIndexWriter(incommonMRId); + } catch (IOException e) { + throw new ComponentInitializationException(e); + } + for (String entityId: this.getBackingStore().getIndexedDescriptors().keySet()) { + Document document = new Document(); document.add(new StringField("id", entityId, Field.Store.YES)); document.add(new TextField("content", entityId, Field.Store.YES)); // TODO: change entityId to be content of entity descriptor block @@ -86,7 +96,7 @@ protected void processConditionalRetrievalHeaders(HttpResponse response) { // let's do nothing 'cause we want to allow a refresh } }; - incommonMR.setId("incommonmd"); + incommonMR.setId(incommonMRId); incommonMR.setParserPool(openSamlObjects.getParserPool()); incommonMR.setMetadataFilter(new MetadataFilterChain()); incommonMR.initialize(); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SearchConfiguration.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SearchConfiguration.java index ac5d1efb7..fab39d551 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SearchConfiguration.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/configuration/SearchConfiguration.java @@ -1,5 +1,7 @@ package edu.internet2.tier.shibboleth.admin.ui.configuration; +import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryService; +import edu.internet2.tier.shibboleth.admin.ui.service.IndexWriterService; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.LowerCaseFilter; import org.apache.lucene.analysis.StopFilter; @@ -10,18 +12,18 @@ import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.store.Directory; -import org.apache.lucene.store.RAMDirectory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; @Configuration public class SearchConfiguration { - @Bean - Directory directory() { - return new RAMDirectory(); - } + @Autowired + DirectoryService directoryService; @Bean Analyzer analyzer() { @@ -54,10 +56,23 @@ protected TokenStreamComponents createComponents(String fieldName) { }; } - @Bean - IndexWriter indexWriter() throws IOException { + private IndexWriter createIndexWriter(Directory directory) throws IOException { IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer()); indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND); - return new IndexWriter(directory(), indexWriterConfig); + return new IndexWriter(directory, indexWriterConfig); + } + + @Bean + public IndexWriterService indexWriterService() { + Map indexWriterMap = new HashMap<>(); + + return resourceId -> { + IndexWriter indexWriter = indexWriterMap.get(resourceId); + if (indexWriter == null) { + indexWriter = createIndexWriter(directoryService.getDirectory(resourceId)); + indexWriterMap.put(resourceId, indexWriter); + } + return indexWriter; + }; } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityIdsSearchController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityIdsSearchController.java index 643e626f4..034556c92 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityIdsSearchController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/EntityIdsSearchController.java @@ -19,9 +19,11 @@ public EntityIdsSearchController(EntityIdsSearchService entityIdsSearchService) } @GetMapping - ResponseEntity search(@RequestParam String term, @RequestParam(required = false) Integer limit) { + ResponseEntity search(@RequestParam(required = false) String resourceId, + @RequestParam String term, + @RequestParam(required = false) Integer limit) { //Zero indicates no-limit final int resultLimit = (limit != null ? limit : 10); - return ResponseEntity.ok(this.entityIdsSearchService.findBySearchTermAndOptionalLimit(term, resultLimit)); + return ResponseEntity.ok(this.entityIdsSearchService.findBySearchTermAndOptionalLimit(resourceId, term, resultLimit)); } } 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 be6ab51a4..b6e1f0538 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 @@ -4,9 +4,15 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; 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.IndexWriterService; import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverService; import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolversPositionOrderContainerService; import lombok.extern.slf4j.Slf4j; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.StringField; +import org.apache.lucene.index.IndexWriter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -52,6 +58,9 @@ public class MetadataResolversController { @Autowired MetadataResolversPositionOrderContainerService positionOrderContainerService; + @Autowired + IndexWriterService indexWriterService; + @ExceptionHandler({InvalidTypeIdException.class, IOException.class, HttpMessageNotReadableException.class}) public ResponseEntity unableToParseJson(Exception ex) { return ResponseEntity.badRequest().body(new ErrorResponse(HttpStatus.BAD_REQUEST.toString(), ex.getMessage())); diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/DirectoryService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/DirectoryService.java new file mode 100644 index 000000000..a3cf9fe2f --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/DirectoryService.java @@ -0,0 +1,19 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +import org.apache.lucene.store.Directory; + +import java.util.List; + +/** + * API component responsible for entity ids search. + */ +public interface DirectoryService { + /** + * Return a Directory for a given resource id. If one is not found, it will be created. + * @param resourceId the resource to get the Directory for + * @return Directory + */ + Directory getDirectory(String resourceId); + + List getDirectories(); +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/DirectoryServiceImpl.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/DirectoryServiceImpl.java new file mode 100644 index 000000000..553af3094 --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/DirectoryServiceImpl.java @@ -0,0 +1,30 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.RAMDirectory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +public class DirectoryServiceImpl implements DirectoryService { + private Map directoryMap = new HashMap<>(); + + @Override + public Directory getDirectory(String resourceId) { + Directory directory = directoryMap.get(resourceId); + if (directory == null) { + directory = new RAMDirectory(); + directoryMap.put(resourceId, directory); + } + return directory; + } + + @Override + public List getDirectories() { + return (List) directoryMap.values(); + } +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchService.java index 81b8a4b1c..6639d5b1a 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchService.java @@ -15,9 +15,10 @@ public interface EntityIdsSearchService { /** * Find a list of entity ids + * @param resourceId the id of the resource to search within * @param searchTerm for the query * @param limit optional limit of query results to return. Zero or less value indicates no limit. * @return EntityIdsSearchResultRepresentation */ - EntityIdsSearchResultRepresentation findBySearchTermAndOptionalLimit(String searchTerm, int limit); + EntityIdsSearchResultRepresentation findBySearchTermAndOptionalLimit(String resourceId, String searchTerm, int limit); } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IndexWriterService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IndexWriterService.java new file mode 100644 index 000000000..1e314ca4b --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/IndexWriterService.java @@ -0,0 +1,18 @@ +package edu.internet2.tier.shibboleth.admin.ui.service; + +import org.apache.lucene.index.IndexWriter; + +import java.io.IOException; + +/** + * API component responsible for entity ids search. + */ +@FunctionalInterface +public interface IndexWriterService { + /** + * Return a (possibly cached) index writer for a given resource id. + * @param resourceId the resource to create the IndexWriter for + * @return IndexWriter + */ + IndexWriter getIndexWriter(String resourceId) throws IOException; +} diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/LuceneUtility.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/LuceneUtility.java new file mode 100644 index 000000000..9b53ebdbb --- /dev/null +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/util/LuceneUtility.java @@ -0,0 +1,45 @@ +package edu.internet2.tier.shibboleth.admin.util; + +import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryService; +import org.apache.commons.lang.StringUtils; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.MultiReader; +import org.apache.lucene.store.Directory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +public class LuceneUtility { + private static final Logger logger = LoggerFactory.getLogger(LuceneUtility.class); + + @Autowired + private DirectoryService directoryService; + + public IndexReader getIndexReader(String resourceId) throws IOException { + IndexReader indexReader; + if (StringUtils.isBlank(resourceId)) { + List directories = directoryService.getDirectories(); + List indexReaderList = new ArrayList<>(); + directories.forEach(it -> { + try { + indexReaderList.add(DirectoryReader.open(it)); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + }); + IndexReader[] indexReaders = (IndexReader[]) indexReaderList.toArray(); + indexReader = new MultiReader(indexReaders, true); + } else { + indexReader = DirectoryReader.open(directoryService.getDirectory(resourceId)); + } + return indexReader; + } +} diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy index 73cf58307..2cb3c7d9d 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/configuration/TestConfiguration.groovy @@ -2,6 +2,7 @@ package edu.internet2.tier.shibboleth.admin.ui.configuration import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository +import edu.internet2.tier.shibboleth.admin.ui.service.IndexWriterService import net.shibboleth.ext.spring.resource.ResourceHelper import net.shibboleth.utilities.java.support.component.ComponentInitializationException import org.apache.lucene.document.Document @@ -14,20 +15,22 @@ import org.opensaml.saml.metadata.resolver.MetadataResolver import org.opensaml.saml.metadata.resolver.impl.ResourceBackedMetadataResolver import org.slf4j.Logger import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.core.io.ClassPathResource @Configuration class TestConfiguration { + @Autowired + IndexWriterService indexWriterService + final OpenSamlObjects openSamlObjects - final IndexWriter indexWriter final MetadataResolverRepository metadataResolverRepository final Logger logger = LoggerFactory.getLogger(TestConfiguration.class); - TestConfiguration(final OpenSamlObjects openSamlObjects, final IndexWriter indexWriter, final MetadataResolverRepository metadataResolverRepository) { + TestConfiguration(final OpenSamlObjects openSamlObjects, final MetadataResolverRepository metadataResolverRepository) { this.openSamlObjects =openSamlObjects - this.indexWriter = indexWriter this.metadataResolverRepository = metadataResolverRepository } @@ -35,6 +38,9 @@ class TestConfiguration { MetadataResolver metadataResolver() { ChainingMetadataResolver metadataResolver = new ChainingMetadataResolver() metadataResolver.setId("chain") + String resolverId = "test" + + IndexWriter indexWriter = indexWriterService.getIndexWriter(resolverId) def shortIncommon = new ResourceBackedMetadataResolver(ResourceHelper.of(new ClassPathResource('/metadata/incommon-short.xml'))){ @Override @@ -58,7 +64,7 @@ class TestConfiguration { } } }.with { - it.id = 'test' + it.id = resolverId TestConfiguration p = owner it.parserPool = p.openSamlObjects.parserPool it.initialize() diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityIdsSearchControllerTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityIdsSearchControllerTests.groovy index a24ed433f..77ea5ddd0 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityIdsSearchControllerTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/EntityIdsSearchControllerTests.groovy @@ -25,11 +25,14 @@ class EntityIdsSearchControllerTests extends Specification { def mockMvc = MockMvcBuilders.standaloneSetup(controller).build() + static final String RESOURCE_ID = "resourceId" + static final String RESOURCE_ID_VALUE = "test" + static final String TERM = "term" + static final String LIMIT = "limit" + def "GET /api/EntityIds/search with unicon and limit 5"() { given: - def term = "term" def termValue = "unicon" - def limit = "limit" def limitValue = "5" def expectedEntityIdsFromSearchService = new EntityIdsSearchResultRepresentation(["http://unicon.instructure.com/saml2", "https://idp.unicon.net/idp/shibboleth"]) def expectedHttpResponseStatus = status().isOk() @@ -45,12 +48,13 @@ class EntityIdsSearchControllerTests extends Specification { when: def result = mockMvc.perform(get('/api/EntityIds/search') - .param(term, termValue) - .param(limit, limitValue)) + .param(RESOURCE_ID, RESOURCE_ID_VALUE) + .param(TERM, termValue) + .param(LIMIT, limitValue)) then: result.andExpect(expectedHttpResponseStatus) - 1 * entityIdsSearchService.findBySearchTermAndOptionalLimit(termValue, Integer.valueOf(limitValue)) >> expectedEntityIdsFromSearchService + 1 * entityIdsSearchService.findBySearchTermAndOptionalLimit(RESOURCE_ID_VALUE, termValue, Integer.valueOf(limitValue)) >> expectedEntityIdsFromSearchService result.andExpect(expectedHttpResponseStatus) .andExpect(content().contentType(expectedResponseContentType)) .andExpect(content().json(expectedResponseBody, true)) @@ -58,9 +62,7 @@ class EntityIdsSearchControllerTests extends Specification { def "GET /api/EntityIds/search with unicon and limit 1"() { given: - def term = "term" def termValue = "unicon" - def limit = "limit" def limitValue = "1" def expectedEntityIdsFromSearchService = new EntityIdsSearchResultRepresentation(["http://unicon.instructure.com/saml2"]) def expectedHttpResponseStatus = status().isOk() @@ -75,12 +77,13 @@ class EntityIdsSearchControllerTests extends Specification { when: def result = mockMvc.perform(get('/api/EntityIds/search') - .param(term, termValue) - .param(limit, limitValue)) + .param(RESOURCE_ID, RESOURCE_ID_VALUE) + .param(TERM, termValue) + .param(LIMIT, limitValue)) then: result.andExpect(expectedHttpResponseStatus) - 1 * entityIdsSearchService.findBySearchTermAndOptionalLimit(termValue, Integer.valueOf(limitValue)) >> expectedEntityIdsFromSearchService + 1 * entityIdsSearchService.findBySearchTermAndOptionalLimit(RESOURCE_ID_VALUE, termValue, Integer.valueOf(limitValue)) >> expectedEntityIdsFromSearchService result.andExpect(expectedHttpResponseStatus) .andExpect(content().contentType(expectedResponseContentType)) .andExpect(content().json(expectedResponseBody, true)) @@ -88,9 +91,7 @@ class EntityIdsSearchControllerTests extends Specification { def "GET /api/EntityIds/search with shib and no limit"() { given: - def term = "term" def termValue = "shib" - def limit = "limit" def limitValue = NO_LIMIT def expectedEntityIdsFromSearchService = new EntityIdsSearchResultRepresentation(["https://shib.ucanr.org/shibboleth", "https://shibboleth2sp.tf.semcs.net/shibboleth", @@ -123,11 +124,12 @@ class EntityIdsSearchControllerTests extends Specification { when: def result = mockMvc.perform(get('/api/EntityIds/search') - .param(term, termValue)) + .param(RESOURCE_ID, RESOURCE_ID_VALUE) + .param(TERM, termValue)) then: result.andExpect(expectedHttpResponseStatus) - 1 * entityIdsSearchService.findBySearchTermAndOptionalLimit(termValue, Integer.valueOf(limitValue)) >> expectedEntityIdsFromSearchService + 1 * entityIdsSearchService.findBySearchTermAndOptionalLimit(RESOURCE_ID_VALUE, termValue, Integer.valueOf(limitValue)) >> expectedEntityIdsFromSearchService result.andExpect(expectedHttpResponseStatus) .andExpect(content().contentType(expectedResponseContentType)) .andExpect(content().json(expectedResponseBody, true)) @@ -135,9 +137,7 @@ class EntityIdsSearchControllerTests extends Specification { def "GET /api/EntityIds/search with empty term and limit 5"() { given: - def term = "term" def termValue = "" - def limit = "limit" def limitValue = "5" def expectedEntityIdsFromSearchService = new EntityIdsSearchResultRepresentation([]) def expectedHttpResponseStatus = status().isOk() @@ -150,12 +150,13 @@ class EntityIdsSearchControllerTests extends Specification { when: def result = mockMvc.perform(get('/api/EntityIds/search') - .param(term, termValue) - .param(limit, limitValue)) + .param(RESOURCE_ID, RESOURCE_ID_VALUE) + .param(TERM, termValue) + .param(LIMIT, limitValue)) then: result.andExpect(expectedHttpResponseStatus) - 1 * entityIdsSearchService.findBySearchTermAndOptionalLimit(termValue, Integer.valueOf(limitValue)) >> expectedEntityIdsFromSearchService + 1 * entityIdsSearchService.findBySearchTermAndOptionalLimit(RESOURCE_ID_VALUE, termValue, Integer.valueOf(limitValue)) >> expectedEntityIdsFromSearchService result.andExpect(expectedHttpResponseStatus) .andExpect(content().contentType(expectedResponseContentType)) .andExpect(content().json(expectedResponseBody, true)) @@ -163,9 +164,7 @@ class EntityIdsSearchControllerTests extends Specification { def "GET /api/EntityIds/search with empty term and no limit"() { given: - def term = "term" def termValue = "" - def limit = "limit" def limitValue = NO_LIMIT def expectedEntityIdsFromSearchService = new EntityIdsSearchResultRepresentation([]) def expectedHttpResponseStatus = status().isOk() @@ -178,12 +177,13 @@ class EntityIdsSearchControllerTests extends Specification { when: def result = mockMvc.perform(get('/api/EntityIds/search') - .param(term, termValue) - .param(limit, limitValue)) + .param(RESOURCE_ID, RESOURCE_ID_VALUE) + .param(TERM, termValue) + .param(LIMIT, limitValue)) then: result.andExpect(expectedHttpResponseStatus) - 1 * entityIdsSearchService.findBySearchTermAndOptionalLimit(termValue, Integer.valueOf(limitValue)) >> expectedEntityIdsFromSearchService + 1 * entityIdsSearchService.findBySearchTermAndOptionalLimit(RESOURCE_ID_VALUE, termValue, Integer.valueOf(limitValue)) >> expectedEntityIdsFromSearchService result.andExpect(expectedHttpResponseStatus) .andExpect(content().contentType(expectedResponseContentType)) .andExpect(content().json(expectedResponseBody, true)) diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy index 7b0cc9f4b..a36e7c4ae 100644 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/EntityIdsSearchServiceTests.groovy @@ -23,6 +23,8 @@ class EntityIdsSearchServiceTests extends Specification { @Autowired EntityIdsSearchService entityIdsSearchService + def test = "test" + def "searching for carmen produces one result"() { setup: def searchTerm = "carmen" @@ -31,7 +33,7 @@ class EntityIdsSearchServiceTests extends Specification { def expectedResultItem = "https://carmenwiki.osu.edu/shibboleth" when: - def actualResults = entityIdsSearchService.findBySearchTermAndOptionalLimit(searchTerm, searchLimit) + def actualResults = entityIdsSearchService.findBySearchTermAndOptionalLimit(test, searchTerm, searchLimit) then: expectedResultSize == actualResults.entityIds.size() @@ -46,7 +48,7 @@ class EntityIdsSearchServiceTests extends Specification { def expectedResults = Arrays.asList(["http://unicon.instructure.com/saml2", "https://idp.unicon.net/idp/shibboleth"]) when: - def actualResults = entityIdsSearchService.findBySearchTermAndOptionalLimit(searchTerm, searchLimit) + def actualResults = entityIdsSearchService.findBySearchTermAndOptionalLimit(test, searchTerm, searchLimit) then: expectedResultSize == actualResults.entityIds.size() @@ -60,7 +62,7 @@ class EntityIdsSearchServiceTests extends Specification { def expectedResultSize = 0 when: - def actualResults = entityIdsSearchService.findBySearchTermAndOptionalLimit(searchTerm, searchLimit) + def actualResults = entityIdsSearchService.findBySearchTermAndOptionalLimit(test, searchTerm, searchLimit) then: expectedResultSize == actualResults.entityIds.size() @@ -74,7 +76,7 @@ class EntityIdsSearchServiceTests extends Specification { def expectedResults = Arrays.asList(["http://unicon.instructure.com/saml2"]) when: - def actualResults = entityIdsSearchService.findBySearchTermAndOptionalLimit(searchTerm, searchLimit) + def actualResults = entityIdsSearchService.findBySearchTermAndOptionalLimit(test, searchTerm, searchLimit) then: expectedResultSize == actualResults.entityIds.size() @@ -87,7 +89,7 @@ class EntityIdsSearchServiceTests extends Specification { def searchLimit = 0 when: - entityIdsSearchService.findBySearchTermAndOptionalLimit(searchTerm, searchLimit) + entityIdsSearchService.findBySearchTermAndOptionalLimit(test, searchTerm, searchLimit) then: thrown IllegalArgumentException diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IndexWriterServiceTests.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IndexWriterServiceTests.groovy new file mode 100644 index 000000000..65ec74bc1 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/service/IndexWriterServiceTests.groovy @@ -0,0 +1,37 @@ +package edu.internet2.tier.shibboleth.admin.ui.service + +import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.TestConfiguration +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.autoconfigure.domain.EntityScan +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import org.springframework.data.jpa.repository.config.EnableJpaRepositories +import org.springframework.test.context.ContextConfiguration +import spock.lang.Specification + +/** + * @author Bill Smith (wsmith@unicon.net) + */ +@DataJpaTest +@ContextConfiguration(classes=[CoreShibUiConfiguration, SearchConfiguration, TestConfiguration, InternationalizationConfiguration]) +@EnableJpaRepositories(basePackages = ["edu.internet2.tier.shibboleth.admin.ui"]) +@EntityScan("edu.internet2.tier.shibboleth.admin.ui") +class IndexWriterServiceTests extends Specification { + + @Autowired + IndexWriterService service + + def "retrieving index writer for the same resource id multiple times results in the same index writer being returned"() { + given: + def resourceId = "12345" + + when: + def firstIndexWriter = service.getIndexWriter(resourceId) // causes new index writer to be created and added to map + def secondIndexWriter = service.getIndexWriter(resourceId) // retrieves the same index writer from above + + then: + firstIndexWriter == secondIndexWriter + } +}