diff --git a/pom.xml b/pom.xml index 08e443d529b9736f1d25038289d6b438cff0dcd1..8c8e43180a378c8e378f43bcf2ab6fded310ecfd 100644 --- a/pom.xml +++ b/pom.xml @@ -158,7 +158,7 @@ <dependency> <groupId>org.codeability.sharing</groupId> <artifactId>SharingPluginPlatformAPI</artifactId> - <version>0.4.9</version> + <version>0.4.10-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springdoc</groupId> diff --git a/src/main/docs/requirements-docs.txt b/src/main/docs/requirements-docs.txt index d0d8a26e8921ec72e54667bb24c1a9bd89d7f084..bb4eeb5a9a7f3c2f7bdef38ab661c302584bc490 100644 --- a/src/main/docs/requirements-docs.txt +++ b/src/main/docs/requirements-docs.txt @@ -1,3 +1,3 @@ # docs -Sphinx==3.2.1 -sphinx-rtd-theme==0.5.0 +Sphinx==7.1.2 +sphinx-rtd-theme==2.0.0 diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepository.java b/src/main/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepository.java index aa60a32e424e38d717ccae3cbc3ae96be1fd1789..91f8272463889c4825bd4c629bc39295fea12fcf 100644 --- a/src/main/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepository.java +++ b/src/main/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepository.java @@ -7,6 +7,10 @@ import at.ac.uibk.gitsearch.repository.jpa.StatisticsRepository; import at.ac.uibk.gitsearch.service.dto.AutoCompleteEntry; import at.ac.uibk.gitsearch.service.dto.StatisticsDTO; import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.ScriptSortType; +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch._types.SortOptionsBuilders; +import co.elastic.clients.elasticsearch._types.SortOrder; import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; import co.elastic.clients.elasticsearch._types.query_dsl.ExistsQuery; import co.elastic.clients.elasticsearch._types.query_dsl.MatchPhrasePrefixQuery; @@ -56,10 +60,12 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; import javax.annotation.PostConstruct; import javax.ws.rs.NotFoundException; +import liquibase.repackaged.org.apache.commons.text.StringSubstitutor; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.codeability.sharing.plugins.api.search.SearchInputDTO; +import org.codeability.sharing.plugins.api.search.SearchOrdering; import org.codeability.sharing.plugins.api.search.SearchResultDTO; import org.codeability.sharing.plugins.api.search.SearchResultsDTO; import org.codeability.sharing.plugins.api.search.UserProvidedMetadataDTO.Person; @@ -305,7 +311,7 @@ public class MetaDataRepository { } }); - reloadedCachedCompletions.values().forEach(c -> simplify(c)); + reloadedCachedCompletions.values().forEach(this::simplify); cachedCompletions = reloadedCachedCompletions; } catch (co.elastic.clients.elasticsearch._types.ElasticsearchException | ElasticsearchException | ConnectException ex) { // this may happen mainly during testing, when @@ -330,7 +336,7 @@ public class MetaDataRepository { entries.forEach((lowerCaseValue, upperCaseValues) -> { final Optional<Entry<String, Integer>> max = upperCaseValues.entrySet().stream().max((e1, e2) -> e1.getValue() - e2.getValue()); max.ifPresent(maxEntry -> { - int sum = upperCaseValues.entrySet().stream().mapToInt(o -> o.getValue()).sum(); + int sum = upperCaseValues.entrySet().stream().mapToInt(Entry::getValue).sum(); entries.put(lowerCaseValue, Collections.singletonMap(maxEntry.getKey(), sum)); }); }); @@ -532,8 +538,6 @@ public class MetaDataRepository { * parameters. * * @param searchInputDTO the query parameters. - * @param pageSize the size of the page (i.e. the maximum of returned - * hits). * @param user the user that queries. * @return the search hits. * @throws IOException when search index is not available. @@ -565,7 +569,13 @@ public class MetaDataRepository { final BoolQuery query = queryBuilder.build(); // LOGGER.info("ElasticSearch Query using Java Client API:\n{}", query) co.elastic.clients.elasticsearch.core.SearchResponse<SearchResultDTO> searchResponse = elasticsearchAPIClient.search( - search -> search.index(SearchRepositoryConstants.INDEX_METADATA).query(q -> q.bool(query)).from(from).size(pageSize), + search -> + search + .index(SearchRepositoryConstants.INDEX_METADATA) + .query(q -> q.bool(query)) + .sort(getSortStrategy(searchInputDTO.getOrdering())) + .from(from) + .size(pageSize), SearchResultDTO.class ); @@ -583,6 +593,48 @@ public class MetaDataRepository { } } + private List<SortOptions> getSortStrategy(SearchOrdering searchOrdering) { + String sortingScriptTemplate = + "double boost = 0;" + + "if (doc.containsKey('${badgeRewarded}') && doc['${badgeRewarded}'].size() > 0) {" + + " boost = boost + (doc['${badgeRewarded}'].value ? 1.0 : 0.0) * params.factorBadge;" + + "}" + + "if (doc.containsKey('${downloads}') && doc['${downloads}'].size() > 0) {" + + " boost = boost + doc['${downloads}'].value * params.factorDownloads;" + + "}" + + "if (doc.containsKey('${views}') && doc['${views}'].size() > 0) {" + + " boost = boost + doc['${views}'].value * params.factorViews;" + + "}" + + "return boost;"; + + Map<String, String> templateValueMapping = Map.of( + "badgeRewarded", + SearchRepositoryConstants.SEARCHSTATISTICS_BADGEREWARDED, + "downloads", + SearchRepositoryConstants.SEARCHSTATISTICS_DOWNLOADS, + "views", + SearchRepositoryConstants.SEARCHSTATISTICS_VIEWS + ); + + var scriptSort = SortOptionsBuilders.script(b -> + b + .type(ScriptSortType.Number) + .order(SortOrder.Desc) + .script(sb -> + sb.inline(isb -> + isb + .lang("painless") + .source(StringSubstitutor.replace(sortingScriptTemplate, templateValueMapping)) + .params("factorBadge", JsonData.of(searchOrdering.getFactorBadge())) + .params("factorDownloads", JsonData.of(searchOrdering.getFactorDownloads())) + .params("factorViews", JsonData.of(searchOrdering.getFactorViews())) + ) + ) + ); + + return List.of(SortOptionsBuilders.score(s -> s.order(SortOrder.Desc)), scriptSort); + } + /** * creates the query (builder) for the searchInput and the user authorization * @@ -774,12 +826,14 @@ public class MetaDataRepository { */ private void addAuthorizationQueryWithJavaApi(Optional<User> user, BoolQuery.Builder queryBuilder) { // Authorization restrictions + // the boost value of 0 tells elastic to exclude these restrictions from the scoring process final TermQuery.Builder simplePublicQuery = new TermQuery.Builder() .value("public") .field(SearchRepositoryConstants.PROJECT_VISIBILITY) .boost(0.0f); final ExistsQuery.Builder publicVisibilityQuery = new ExistsQuery.Builder() - .field(SearchRepositoryConstants.METADATA_PUBLICVISIBILITY); + .field(SearchRepositoryConstants.METADATA_PUBLICVISIBILITY) + .boost(0.0f); final BoolQuery.Builder publicQuery = new BoolQuery.Builder() .should(q -> q.term(simplePublicQuery.build())) .should(q -> q.exists(publicVisibilityQuery.build())); diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/search/SearchRepositoryConstants.java b/src/main/java/at/ac/uibk/gitsearch/repository/search/SearchRepositoryConstants.java index f907f768fb5b0487507b0d376ecef38f7d53bfe0..3fe6f2cba967f71d44b2ec81b1e11aed6251560c 100644 --- a/src/main/java/at/ac/uibk/gitsearch/repository/search/SearchRepositoryConstants.java +++ b/src/main/java/at/ac/uibk/gitsearch/repository/search/SearchRepositoryConstants.java @@ -41,6 +41,10 @@ public final class SearchRepositoryConstants { public static final String PROJECT_GROUPS = "project.groups"; public static final String PROJECT_LASTACTIVITYAT = "project.last_activity_at"; + public static final String SEARCHSTATISTICS_VIEWS = "searchStatistics.views"; + public static final String SEARCHSTATISTICS_DOWNLOADS = "searchStatistics.downloads"; + public static final String SEARCHSTATISTICS_BADGEREWARDED = "searchStatistics.badgeRewarded"; + private SearchRepositoryConstants() { // just a list of constants } diff --git a/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java b/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java index 0203392b5e1c6cc54fadf8817597d09f8e681a41..f558766a8b2d11b71a7ec862acaaeeb3363f6fbe 100644 --- a/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java +++ b/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java @@ -148,15 +148,20 @@ public class GitlabService { RepositoryFile file; try (GitLabApi gitLabApi = gitLabRepository.getGitLabApi(tokenProvider.getGitLabAccessInfo());) { - file = - gitLabApi - .getRepositoryFileApi() - .getFile( - Long.valueOf(exercise.getProject().getProject_id()), - exerciseId.extendPath(filePath), - exercise.getFile().getCommit_id() - ); - return new ByteArrayInputStream(file.getDecodedContentAsBytes()); + if (exercise.getFile().getCommit_id() == null) { + log.warn("commit_id must not be null for {}", exerciseId); + return null; + } else { + file = + gitLabApi + .getRepositoryFileApi() + .getFile( + Long.valueOf(exercise.getProject().getProject_id()), + exerciseId.extendPath(filePath), + exercise.getFile().getCommit_id() + ); + return new ByteArrayInputStream(file.getDecodedContentAsBytes()); + } } catch (GitLabApiException e) { log.warn( "GitlabAPI Exception when looking for {} {} {} with message {}", diff --git a/src/main/java/at/ac/uibk/gitsearch/service/gitlab_events/GitlabEventService.java b/src/main/java/at/ac/uibk/gitsearch/service/gitlab_events/GitlabEventService.java index ae56cd2ddf2307b0580fe8b730bd7a002f5e49ca..5e5ea5ee71e0adc27d29ce176898ccdf0943534b 100644 --- a/src/main/java/at/ac/uibk/gitsearch/service/gitlab_events/GitlabEventService.java +++ b/src/main/java/at/ac/uibk/gitsearch/service/gitlab_events/GitlabEventService.java @@ -47,8 +47,8 @@ import java.util.stream.Stream; import javax.annotation.PostConstruct; import javax.el.MethodNotFoundException; import org.apache.commons.lang3.StringUtils; +import org.codeability.sharing.plugins.api.search.GitProjectDTO; import org.codeability.sharing.plugins.api.search.SearchResultDTO; -import org.codeability.sharing.plugins.api.search.SearchResultDTO.GitProject; import org.codeability.sharing.plugins.api.search.SearchResultDTO.MetadataFile; import org.codeability.sharing.plugins.api.search.UserProvidedMetadataDTO; import org.codeability.sharing.plugins.api.search.UserProvidedMetadataDTO.InteractivityTypeXXX; @@ -63,7 +63,6 @@ import org.gitlab4j.api.models.ProjectSharedGroup; import org.gitlab4j.api.models.RepositoryFile; import org.gitlab4j.api.models.TreeItem; import org.gitlab4j.api.models.User; -import org.gitlab4j.api.models.Visibility; import org.gitlab4j.api.systemhooks.GroupSystemHookEvent; import org.gitlab4j.api.systemhooks.ProjectSystemHookEvent; import org.gitlab4j.api.systemhooks.PushSystemHookEvent; @@ -255,99 +254,6 @@ public class GitlabEventService { } } - /** - * just to add further meta data - * - * @author Micha - * - */ - @SuppressWarnings({ "PMD.MethodNamingConventions", "PMD.FormalParameterNamingConventions" }) - private static class ExtendedGitProject extends GitProject { - - private Visibility visibility; - private List<String> users; - private List<String> groups; - private int star_count; - private int open_issues_count; - private int forks_count; - - private String description; - private boolean archived; - - @SuppressWarnings({ "unused", "PMD.AvoidDuplicateLiterals" }) - public Visibility getVisibility() { - return visibility; - } - - public void setVisibility(Visibility visibility) { - this.visibility = visibility; - } - - @SuppressWarnings("unused") - public List<String> getUsers() { - return users; - } - - public void setUsers(List<String> users) { - this.users = users; - } - - @SuppressWarnings("unused") - public List<String> getGroups() { - return groups; - } - - public void setGroups(List<String> groups) { - this.groups = groups; - } - - @SuppressWarnings("unused") - public int getStar_count() { - return star_count; - } - - @SuppressWarnings("unused") - public void setStar_count(int star_count) { - this.star_count = star_count; - } - - @SuppressWarnings("unused") - public int getOpen_issues_count() { - return open_issues_count; - } - - public void setOpen_issues_count(int open_issues_count) { - this.open_issues_count = open_issues_count; - } - - @SuppressWarnings("unused") - public int getForks_count() { - return forks_count; - } - - public void setForks_count(int forks_count) { - this.forks_count = forks_count; - } - - @SuppressWarnings("unused") - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - @SuppressWarnings("unused") - public boolean isArchived() { - return archived; - } - - public void setArchived(boolean archived) { - this.archived = archived; - } - } - /** * Represents a link to another Item (either relative to this path, to another * project, @@ -743,12 +649,12 @@ public class GitlabEventService { indexMetadataNode(projectPermissions, metadataTree); } - protected ExtendedGitProject mapProject(Project p, ProjectPermissions perm) { + protected GitProjectDTO mapProject(Project p, ProjectPermissions perm) { final String[] split = p.getPathWithNamespace().split("/"); String mainGroup = split[0]; String subGroup = split.length > 2 ? split[1] : null; - ExtendedGitProject gp = new ExtendedGitProject(); + GitProjectDTO gp = new GitProjectDTO(); gp.setLast_activity_at(p.getLastActivityAt().toInstant()); gp.setMain_group(mainGroup); gp.setNamespace(p.getPathWithNamespace()); diff --git a/src/test/java/at/ac/uibk/gitsearch/repository/search/testESService/ElasticSearchTestConfiguration.java b/src/test/java/at/ac/uibk/gitsearch/repository/search/testESService/ElasticSearchTestConfiguration.java index 6476b8479c352a8a8ce7177e8f7dd0b1e20c67fc..8504f9d982002cb77358d621e509522c67c56c91 100644 --- a/src/test/java/at/ac/uibk/gitsearch/repository/search/testESService/ElasticSearchTestConfiguration.java +++ b/src/test/java/at/ac/uibk/gitsearch/repository/search/testESService/ElasticSearchTestConfiguration.java @@ -41,8 +41,8 @@ import java.util.function.Consumer; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpHost; +import org.codeability.sharing.plugins.api.search.GitProjectDTO; import org.codeability.sharing.plugins.api.search.SearchResultDTO; -import org.codeability.sharing.plugins.api.search.SearchResultDTO.GitProject; import org.codeability.sharing.plugins.api.search.SearchResultDTO.MetadataFile; import org.codeability.sharing.plugins.api.search.UserProvidedMetadataDTO; import org.codeability.sharing.plugins.api.search.util.ExerciseId; @@ -51,6 +51,7 @@ import org.elasticsearch.node.InternalSettingsPreparer; import org.elasticsearch.node.Node; import org.elasticsearch.node.NodeValidationException; import org.elasticsearch.plugins.Plugin; +import org.gitlab4j.api.models.Visibility; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -202,130 +203,6 @@ public class ElasticSearchTestConfiguration { return new HttpHost(ipAddress, esPort); } - /** - * just a helper class to store also data that is not exported to the user - * - * @author Michael Breu - * - */ - public static class ExtendedGitProject extends GitProject { - - private String visibility; - private boolean archived; - private int star_count; - private int open_issues_count; - private int forks_count; - private String description; - private String[] groups; - private String commit_id; - - /** - * @return the visibility - */ - public String getVisibility() { - return visibility; - } - - /** - * @param visibility the visibility to set - */ - public void setVisibility(String visibility) { - this.visibility = visibility; - } - - /** - * @return the archived - */ - public boolean isArchived() { - return archived; - } - - /** - * @param archived the archived to set - */ - public void setArchived(boolean archived) { - this.archived = archived; - } - - /** - * @return the star_count - */ - @SuppressWarnings("PMD") - public int getStar_count() { - return star_count; - } - - /** - * @param star_count the star_count to set - */ - @SuppressWarnings("PMD") - public void setStar_count(int star_count) { - this.star_count = star_count; - } - - /** - * @return the open_issues_count - */ - @SuppressWarnings("PMD") - public int getOpen_issues_count() { - return open_issues_count; - } - - /** - * @param open_issues_count the open_issues_count to set - */ - @SuppressWarnings("PMD") - public void setOpen_issues_count(int open_issues_count) { - this.open_issues_count = open_issues_count; - } - - /** - * @return the forks_count - */ - @SuppressWarnings("PMD") - public int getForks_count() { - return forks_count; - } - - /** - * @param forks_count the forks_count to set - */ - @SuppressWarnings("PMD") - public void setForks_count(int forks_count) { - this.forks_count = forks_count; - } - - /** - * @return the description - */ - public String getDescription() { - return description; - } - - /** - * @param description the description to set - */ - public void setDescription(String description) { - this.description = description; - } - - public String[] getGroups() { - return groups; - } - - public void setGroups(String[] groups) { - this.groups = groups; - } - - public String getCommit_id() { - return commit_id; - } - - public void setCommit_id(String commit_id) { - this.commit_id = commit_id; - } - } - private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ElasticSearchTestConfiguration.class); private TestESNode testNode; @@ -383,7 +260,7 @@ public class ElasticSearchTestConfiguration { contentCount = 0; visitSubdirectories( metaDataTestDir, - (File metaDataFile, List<TreeNode<SearchResultDTO>> ancestors, String relativePath, ExtendedGitProject gitProject) -> { + (File metaDataFile, List<TreeNode<SearchResultDTO>> ancestors, String relativePath, GitProjectDTO gitProject) -> { try { final UserProvidedMetadataDTO userMetaData = parseMetaDataFile(metaDataFile); @@ -393,11 +270,11 @@ public class ElasticSearchTestConfiguration { final ExerciseId exerciseId = calculateProjectId(relativePath); if (gitProject == null) { - gitProject = new ExtendedGitProject(); + gitProject = new GitProjectDTO(); gitProject.setProject_id(Integer.parseInt(exerciseId.getProjectId())); gitProject.setProject_name("SomeGitProject" + exerciseId.getProjectId()); - gitProject.setVisibility("public"); + gitProject.setVisibility(Visibility.PUBLIC); } toIndex.setProject(gitProject); @@ -413,10 +290,7 @@ public class ElasticSearchTestConfiguration { : (relativePath.substring(slashPos + 1) + "/" + metaDataFile.getName()); file.setPath(projectRelativePath); file.setIndexing_date(Instant.now()); - file.setCommit_id("Unused Commit Id"); - if (gitProject.getCommit_id() != null) { - file.setCommit_id(gitProject.getCommit_id()); - } + file.setCommit_id(gitProject.getCommit_id()); file.setChildren( ancestors.stream().map(node -> node.getData().getExerciseId()).collect(Collectors.toList()).toArray(new String[] {}) ); @@ -512,34 +386,10 @@ public class ElasticSearchTestConfiguration { } } - private ExtendedGitProject parseProjectFile(File projectFile) throws JsonParseException, JsonMappingException, IOException { + private GitProjectDTO parseProjectFile(File projectFile) throws JsonParseException, JsonMappingException, IOException { ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); mapper.findAndRegisterModules(); - ExtendedGitProject result = mapper.readValue(projectFile, ExtendedGitProject.class); - return result; - } - - /** - * an interface that is invoked on each node of the tree with a metadata - * - * @author Michael Breu - * - * @param <T> the harvested data type - */ - private interface Harvester<T> { - /** - * - * @param f - * the metaData File - * @param subResults - * the tree of results of the next node level - * @param relativePath - * the relative path from test root - * @param gitProject - * the information about the git project. - * @return - */ - T harvest(File f, List<TreeNode<T>> subResults, String relativePath, ExtendedGitProject gitProject); + return mapper.readValue(projectFile, GitProjectDTO.class); } /** @@ -558,8 +408,8 @@ public class ElasticSearchTestConfiguration { * the gitProject that may be collected from root. * @return the tree of descendants */ - private <T> TreeNode<T> visitSubdirectories(File dir, Harvester<T> harvester, String relativePath, ExtendedGitProject gitProject) { - ExtendedGitProject currentGitProject = gitProject; + private <T> TreeNode<T> visitSubdirectories(File dir, Harvester<T> harvester, String relativePath, GitProjectDTO gitProject) { + GitProjectDTO currentGitProject = gitProject; Assert.state(dir.exists() && dir.isDirectory(), "must be a directory"); List<TreeNode<T>> children = new ArrayList<TreeNode<T>>(); for (File containedFile : dir.listFiles()) { @@ -607,6 +457,23 @@ public class ElasticSearchTestConfiguration { return root; } + /** + * an interface that is invoked on each node of the tree with a metadata + * + * @param <T> the harvested data type + * @author Michael Breu + */ + private interface Harvester<T> { + /** + * @param f the metaData File + * @param subResults the tree of results of the next node level + * @param relativePath the relative path from test root + * @param gitProject the information about the git project. + * @return + */ + T harvest(File f, List<TreeNode<T>> subResults, String relativePath, GitProjectDTO gitProject); + } + /** * replaces existing metadata by new content * diff --git a/src/test/java/at/ac/uibk/gitsearch/service/EditorialPagesServiceIT.java b/src/test/java/at/ac/uibk/gitsearch/service/EditorialPagesServiceIT.java index 0c2b89734c5d0ddb3c0b61d7465db08b7a292b8b..309884cc149e8d3a986639f108e021dd0befa705 100644 --- a/src/test/java/at/ac/uibk/gitsearch/service/EditorialPagesServiceIT.java +++ b/src/test/java/at/ac/uibk/gitsearch/service/EditorialPagesServiceIT.java @@ -6,9 +6,11 @@ import static org.hamcrest.Matchers.not; import at.ac.uibk.gitsearch.GitsearchApp; import at.ac.uibk.gitsearch.service.dto.EditorialPageDTO; +import java.util.concurrent.TimeUnit; import org.gitlab4j.api.GitLabApiException; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; @@ -18,13 +20,14 @@ import org.springframework.transaction.annotation.Transactional; */ @SpringBootTest(classes = GitsearchApp.class) @Transactional -public class EditorialPagesServiceIT { +class EditorialPagesServiceIT { @Autowired private EditorialPagesService editorialPagesService; @Test - public void testSimplePage() throws GitLabApiException { + @Timeout(value = 60, unit = TimeUnit.SECONDS) // gitlab may respond slowly + void testSimplePage() throws GitLabApiException { EditorialPageDTO page = editorialPagesService.getContent("/de/start"); MatcherAssert.assertThat(page.getContent(), not(is(emptyOrNullString()))); } diff --git a/src/test/java/at/ac/uibk/gitsearch/service/SearchServiceIT.java b/src/test/java/at/ac/uibk/gitsearch/service/SearchServiceIT.java index 743279bd4f909aed5c1148773417011c0fd6a94e..eab4854181f4058a9e7185be627cb9af5beada7e 100644 --- a/src/test/java/at/ac/uibk/gitsearch/service/SearchServiceIT.java +++ b/src/test/java/at/ac/uibk/gitsearch/service/SearchServiceIT.java @@ -17,21 +17,24 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import at.ac.uibk.gitsearch.IntegrationTest; -import at.ac.uibk.gitsearch.domain.Statistics; import at.ac.uibk.gitsearch.repository.gitlab.GitLabRepository; import at.ac.uibk.gitsearch.repository.jpa.StatisticsRepository; +import at.ac.uibk.gitsearch.repository.search.ElasticSearchRepository; import at.ac.uibk.gitsearch.repository.search.MetaDataRepository; import at.ac.uibk.gitsearch.repository.search.testESService.ElasticSearchTestConfiguration; import at.ac.uibk.gitsearch.security.jwt.TokenProvider; import at.ac.uibk.gitsearch.service.PluginManagementService.ConnectorConfigWrapper; import at.ac.uibk.gitsearch.service.dto.AutoCompleteEntry; +import at.ac.uibk.gitsearch.service.dto.StatisticsDTO; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import javax.ws.rs.NotFoundException; @@ -78,6 +81,12 @@ public class SearchServiceIT { @Autowired StatisticsRepository statisticsRepository; + @Autowired + StatisticsService statisticsService; + + @Autowired + ElasticSearchRepository elasticSearchRepository; + @MockBean RepositoryApi mockedRepositoryApi; @@ -91,7 +100,8 @@ public class SearchServiceIT { static ElasticSearchTestConfiguration staticElasticSearchTestConfiguration; - @BeforeEach // must be started as BeforeEach, in order to autowire the ElasticSearchTestConfiguration + @BeforeEach // must be started as BeforeEach, in order to autowire the + // ElasticSearchTestConfiguration @Timeout(value = 3, unit = TimeUnit.MINUTES) public void setUpESServer() throws IOException, NodeValidationException { if (staticElasticSearchTestConfiguration == null) { @@ -152,41 +162,78 @@ public class SearchServiceIT { } @Test - @Timeout(value = 1, unit = TimeUnit.MINUTES) + @Timeout(value = 10, unit = TimeUnit.MINUTES) @Transactional void testAllSearchWithStatistics() throws Exception { // prepare final SearchInputMetadataDTO searchMetadata = new SearchInputMetadataDTO(null, null, null, null, null, null); SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, null, null, 0); + searchQuery.setPageSize(500); SearchResultsDTO searchResultPage = searchService.searchResultPage(searchQuery); - AtomicInteger count = new AtomicInteger(0); + AtomicInteger downloadsCount = new AtomicInteger(0); + AtomicBoolean badgeRewarded = new AtomicBoolean(false); + AtomicInteger viewsCount = new AtomicInteger(0); // setup statistics data searchResultPage .getSearchResult() - .forEach(sr -> { + .stream() + .map(sr -> { assertThat(sr.getSearchStatistics()).isNull(); // this may be critical, if some other test already - // defined - // statistics - Statistics stat = new Statistics(); + return sr; + }) + .skip(1) // leave one stat empty + .forEach(sr -> { + // define statistics + StatisticsDTO stat = new StatisticsDTO(); stat.setExerciseID(sr.getExerciseId()); - stat.setDownloads(100 + count.get()); - stat.setBadgeRewarded(count.get() % 2 == 1 ? true : false); - stat.setViews(1000 + (count.get())); - count.addAndGet(1); - statisticsRepository.save(stat); + stat.setDownloads(5 + downloadsCount.get()); + stat.setBadgeRewarded(badgeRewarded.get()); + stat.setViews(95 + (viewsCount.get())); + if (badgeRewarded.getAndSet(!badgeRewarded.get())) { + downloadsCount.getAndIncrement(); + } + viewsCount.getAndIncrement(); + statisticsService.save(stat); }); + statisticsRepository.flush(); + elasticSearchRepository.waitForIndexingFinished(); SearchResultsDTO searchResultPage2 = searchService.searchResultPage(searchQuery); - - searchResultPage2 - .getSearchResult() - .forEach(sr -> { + // it occurs that upon requesting elastic search right after updating the + // indices, the old order is retrieved + + Comparator<SearchResultDTO> orderComparator = Comparator.comparing(sr -> { + if (sr.getSearchStatistics() == null) { + return 0D; + } + return ( + (sr.getSearchStatistics().getBadgeRewarded() ? 1 : 0) * + searchQuery.getOrdering().getFactorBadge() + + sr.getSearchStatistics().getDownloads() * + searchQuery.getOrdering().getFactorDownloads() + + sr.getSearchStatistics().getViews() * + searchQuery.getOrdering().getFactorViews() + ); + }); + + SearchResultDTO previousSearchResult = null; + var searchResultsIterator = searchResultPage2.getSearchResult().iterator(); + while (searchResultsIterator.hasNext()) { + var sr = searchResultsIterator.next(); + if (!searchResultsIterator.hasNext()) { + assertThat(sr.getSearchStatistics()).isNull(); + } else { assertThat(sr.getSearchStatistics()).isNotNull(); - assertThat(sr.getSearchStatistics().getDownloads()).isGreaterThanOrEqualTo(100); - assertThat(sr.getSearchStatistics().getViews()).isGreaterThanOrEqualTo(1000); - }); + assertThat(sr.getSearchStatistics().getDownloads()).isGreaterThanOrEqualTo(5); + assertThat(sr.getSearchStatistics().getViews()).isGreaterThanOrEqualTo(95); + } + if (previousSearchResult != null) { + assertThat(orderComparator.compare(previousSearchResult, sr)).isPositive(); + } + previousSearchResult = sr; + } } @Test @@ -283,19 +330,24 @@ public class SearchServiceIT { } // does not work with metadata version 0.4 - // @Test - // void testTypesSearch() throws IOException { - // final SearchInputMetadataDTO searchMetadata = new SearchInputMetadataDTO(null, "latex", null, null, null,null); - // searchMetadata.setTypes(Collections.singletonList(ExerciseType.COLLECTION)); - // SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, null, null, 0); - // SearchResultsDTO searchResultPage = searchService.searchResultPage(searchQuery); + // @Test + // void testTypesSearch() throws IOException { + // final SearchInputMetadataDTO searchMetadata = new + // SearchInputMetadataDTO(null, "latex", null, null, null,null); + // searchMetadata.setTypes(Collections.singletonList(ExerciseType.COLLECTION)); + // SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, + // null, null, 0); + // SearchResultsDTO searchResultPage = + // searchService.searchResultPage(searchQuery); // - // org.junit.Assert.assertNotNull(searchResultPage.getSearchResult()); - // org.junit.Assert.assertTrue("At least one test hit", searchResultPage.getHitCount() >= 1); - // assertThat(searchResultPage.getSearchResult(), - // everyItem(hasProperty("metadata", hasProperty("keyword", hasItemInArray(containsString("latex")))))); + // org.junit.Assert.assertNotNull(searchResultPage.getSearchResult()); + // org.junit.Assert.assertTrue("At least one test hit", + // searchResultPage.getHitCount() >= 1); + // assertThat(searchResultPage.getSearchResult(), + // everyItem(hasProperty("metadata", hasProperty("keyword", + // hasItemInArray(containsString("latex")))))); // - // } + // } @Test @Timeout(value = 1, unit = TimeUnit.MINUTES) @@ -344,20 +396,24 @@ public class SearchServiceIT { } // does not work with metadata version 0.4 - // @Test - // void testTypesSearchNegative() throws IOException { - // final SearchInputMetadataDTO searchMetadata = new SearchInputMetadataDTO(null, "latex", null, null, null,null); - // searchMetadata.setTypes(Collections.singletonList(ExerciseType.OTHER)); - // SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, null, null, 0); - // SearchResultsDTO searchResultPage = searchService.searchResultPage(searchQuery); + // @Test + // void testTypesSearchNegative() throws IOException { + // final SearchInputMetadataDTO searchMetadata = new + // SearchInputMetadataDTO(null, "latex", null, null, null,null); + // searchMetadata.setTypes(Collections.singletonList(ExerciseType.OTHER)); + // SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, + // null, null, 0); + // SearchResultsDTO searchResultPage = + // searchService.searchResultPage(searchQuery); // - // org.junit.Assert.assertNotNull(searchResultPage.getSearchResult()); - // org.junit.Assert.assertTrue("We expect no hit for \"latex\" and ExerciseType.OTHER", searchResultPage.getHitCount() == 0); + // org.junit.Assert.assertNotNull(searchResultPage.getSearchResult()); + // org.junit.Assert.assertTrue("We expect no hit for \"latex\" and + // ExerciseType.OTHER", searchResultPage.getHitCount() == 0); // - // } + // } - // @Test() - // @Ignore() // Test funktioniert momentan nicht? + // @Test() + // @Ignore() // Test funktioniert momentan nicht? void testLicenseSearch() throws IOException { final SearchInputMetadataDTO searchMetadata = new SearchInputMetadataDTO(null, null, null, "MIT", null, null); SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, null, null, 0); diff --git a/src/test/java/at/ac/uibk/gitsearch/service/dto/VariousDTOTest.java b/src/test/java/at/ac/uibk/gitsearch/service/dto/VariousDTOTest.java index e268ae03fe70805b742014e99b6b31bb76c685c0..ba2a28a0dad1c3ffd11aa825322dd9a3d0ca5f26 100644 --- a/src/test/java/at/ac/uibk/gitsearch/service/dto/VariousDTOTest.java +++ b/src/test/java/at/ac/uibk/gitsearch/service/dto/VariousDTOTest.java @@ -8,8 +8,8 @@ import at.ac.uibk.gitsearch.testingUtilities.PropertiesTester; import java.lang.reflect.InvocationTargetException; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; +import org.codeability.sharing.plugins.api.search.GitProjectDTO; import org.codeability.sharing.plugins.api.search.SearchResultDTO; -import org.codeability.sharing.plugins.api.search.SearchResultDTO.GitProject; import org.codeability.sharing.plugins.api.search.SearchResultsDTO; import org.codeability.sharing.plugins.api.search.UserProvidedMetadataDTO; import org.codeability.sharing.plugins.api.search.UserProvidedMetadataDTO.Person; @@ -53,7 +53,7 @@ class VariousDTOTest { propertiesTester.testProperties(SearchResultsDTO.class); propertiesTester.testProperties(SearchResultDTO.class); propertiesTester.testProperties(Person.class); - propertiesTester.testProperties(GitProject.class); + propertiesTester.testProperties(GitProjectDTO.class); // just for test coverage @SuppressWarnings("unused") @@ -85,19 +85,36 @@ class VariousDTOTest { @SuppressWarnings("deprecation") @org.junit.jupiter.api.Test void testVariousEquals() throws IllegalAccessException, InvocationTargetException { - GitProject p1 = new GitProject(); + GitProjectDTO p1 = new GitProjectDTO(); p1.setProjectId(10); - GitProject p2 = new GitProject(); + GitProjectDTO p2 = new GitProjectDTO(); p2.setProjectId(10); - GitProject p3 = new GitProject(); + GitProjectDTO p3 = new GitProjectDTO(); p3.setProjectId(20); Assert.assertEquals(p1, p2); Assert.assertNotEquals(p1, p3); EqualsVerifier - .forClass(GitProject.class) + .forClass(GitProjectDTO.class) .suppress(Warning.NONFINAL_FIELDS) - .withIgnoredFields("project_name", "namespace", "main_group", "last_activity_at", "url", "sub_group") + .withIgnoredFields( + "project_name", + "namespace", + "main_group", + "sub_group", + "url", + "notPresent", + "last_activity_at", + "visibility", + "users", + "groups", + "star_count", + "open_issues_count", + "forks_count", + "description", + "archived", + "commit_id" + ) .verify(); EqualsVerifier.forClass(Person.class).suppress(Warning.NONFINAL_FIELDS).verify(); } diff --git a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/1/project.yaml b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/1/project.yaml index ea1d730c89e72766ee42cba2b1de3d619c211320..cbba1840963ce52c7f1f61b35ec026369498aac6 100644 --- a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/1/project.yaml +++ b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/1/project.yaml @@ -13,3 +13,5 @@ open_issues_count: 0 forks_count: 1 last_activity_at: 2021-02-17T15:13:27.123Z description: +commit_id: "01e2fabbd79b913d7180453ff3e32c68cb57d660" + diff --git a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/143/project.yaml b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/143/project.yaml index 8e6d3e1fe58c8b0385929eacb4f612a05f957cbf..f3e36797775ac235f5d6bb081aa0241d4ef384bc 100644 --- a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/143/project.yaml +++ b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/143/project.yaml @@ -13,3 +13,4 @@ open_issues_count: 0 forks_count: 1 last_activity_at: 2021-04-08T15:13:27.123Z description: Just a test project +commit_id: "01e2fabbd79b913d7180453ff3e32c68cb57d660" diff --git a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/2/project.yaml b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/2/project.yaml index c4c63330fb72acc19b936f36106653eef87909bd..0452323f1d2403f49582cfdf2c280bd079db4f31 100644 --- a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/2/project.yaml +++ b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/2/project.yaml @@ -1,4 +1,4 @@ -project_id: 1 +project_id: 2 project_name: latex namespace: sharing/vienna-universityof-technology/latex main_group: sharing @@ -13,3 +13,5 @@ open_issues_count: 0 forks_count: 1 last_activity_at: 2021-02-17T15:13:27.123Z description: +commit_id: "01e2fabbd79b913d7180453ff3e32c68cb57d660" + diff --git a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/272/project.yaml b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/272/project.yaml index e289b4c262a2cc06a1dd1b185ba3a816b6b52998..3ee38c7459d5ddb095012510a07cb36a518a0e5a 100644 --- a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/272/project.yaml +++ b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/272/project.yaml @@ -13,3 +13,4 @@ open_issues_count: 0 forks_count: 1 last_activity_at: 2021-04-08T15:13:27.123Z description: Just a test project +commit_id: "01e2fabbd79b913d7180453ff3e32c68cb57d660" diff --git a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/274/project.yaml b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/274/project.yaml index 5931ccfc69e318919d0ec3fcf29e82173302d676..0f507fe85e26b2f642bf89cff71cca53ded4054e 100644 --- a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/274/project.yaml +++ b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/274/project.yaml @@ -13,3 +13,5 @@ open_issues_count: 0 forks_count: 1 last_activity_at: 2021-04-08T15:13:27.123Z description: Just a test project +commit_id: "35573081a2232dd8e71ab6375f1adb69e65486c8" + diff --git a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/3/project.yaml b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/3/project.yaml index ab56c4d27021850d03126658351afef0c38b465c..ea2ad2b482698ff1910e89092ef793c973c8085d 100644 --- a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/3/project.yaml +++ b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/3/project.yaml @@ -1,4 +1,4 @@ -project_id: 272 +project_id: 3 project_name: latex namespace: sharing/vienna-universityof-technology/latex main_group: sharing @@ -13,3 +13,4 @@ open_issues_count: 0 forks_count: 1 last_activity_at: 2021-02-17T15:13:27.123Z description: +commit_id: "35573081a2232dd8e71ab6375f1adb69e65486c8" diff --git a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/333/project.yaml b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/333/project.yaml index 2e1508644f2f849c4a7ee4ef0d01f95f43d8d9f0..8fb1f4e2478c133f6ae7ff3fe36ef7e7c4ac7ed5 100644 --- a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/333/project.yaml +++ b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData/333/project.yaml @@ -13,3 +13,4 @@ open_issues_count: 0 forks_count: 1 last_activity_at: 2021-04-08T15:13:27.123Z description: Just a test project +commit_id: "35573081a2232dd8e71ab6375f1adb69e65486c8"