*/
package org.sonar.server.component.ws;
+import com.google.common.html.HtmlEscapers;
import com.google.common.io.Resources;
import java.util.Collection;
import java.util.List;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.component.index.ComponentHit;
import org.sonar.server.component.index.ComponentHitsPerQualifier;
import org.sonar.server.component.index.ComponentIndex;
import org.sonar.server.component.index.ComponentIndexQuery;
@Override
public void define(WebService.NewController context) {
NewAction action = context.createAction(ACTION_SUGGESTIONS)
- .setDescription("Internal WS for the top-right search engine")
+ .setDescription(
+ "Internal WS for the top-right search engine. The result will contain component search results, grouped by their qualifiers.<p>"
+ + "Each result contains:"
+ + "<ul>"
+ + "<li>the organization key</li>"
+ + "<li>the component key</li>"
+ + "<li>the component's name (unescaped)</li>"
+ + "<li>optionally a display name, which puts emphasis to matching characters (this text contains html tags and parts of the html-escaped name)</li>"
+ + "</ul>")
.setSince("4.2")
.setInternal(true)
.setHandler(this)
Map<String, ComponentDto> projectsByUuids;
try (DbSession dbSession = dbClient.openSession(false)) {
Set<String> componentUuids = componentsPerQualifiers.stream()
- .map(ComponentHitsPerQualifier::getComponentUuids)
+ .map(ComponentHitsPerQualifier::getHits)
.flatMap(Collection::stream)
+ .map(ComponentHit::getUuid)
.collect(toSet());
componentsByUuids = dbClient.componentDao().selectByUuids(dbSession, componentUuids).stream()
.collect(MoreCollectors.uniqueIndex(ComponentDto::uuid));
Map<String, OrganizationDto> organizationByUuids, Map<String, ComponentDto> projectsByUuids) {
return componentsPerQualifiers.stream().map(qualifier -> {
- List<Suggestion> suggestions = qualifier.getComponentUuids().stream()
- .map(componentsByUuids::get)
- .map(dto -> toSuggestion(dto, organizationByUuids, projectsByUuids))
+ List<Suggestion> suggestions = qualifier.getHits().stream()
+ .map(hit -> toSuggestion(hit, componentsByUuids, organizationByUuids, projectsByUuids))
.collect(toList());
return Category.newBuilder()
}).collect(toList());
}
- private static Suggestion toSuggestion(ComponentDto result, Map<String, OrganizationDto> organizationByUuid, Map<String, ComponentDto> projectsByUuids) {
- String organizationKey = organizationByUuid.get(result.getOrganizationUuid()).getKey();
+ private static Suggestion toSuggestion(ComponentHit hit, Map<String, ComponentDto> componentsByUuids, Map<String, OrganizationDto> organizationByUuids,
+ Map<String, ComponentDto> projectsByUuids) {
+ ComponentDto result = componentsByUuids.get(hit.getUuid());
+ String organizationKey = organizationByUuids.get(result.getOrganizationUuid()).getKey();
checkState(organizationKey != null, "Organization with uuid '%s' not found", result.getOrganizationUuid());
String projectKey = ofNullable(result.projectUuid()).map(projectsByUuids::get).map(ComponentDto::getKey).orElse("");
return Suggestion.newBuilder()
.setProject(projectKey)
.setKey(result.getKey())
.setName(result.longName())
+ .setMatch(hit.getHighlightedText().orElse(HtmlEscapers.htmlEscaper().escape(result.longName())))
.build();
}
/*
* SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
public class ComponentIndexHighlightTest extends ComponentIndexTest {
+ @Test
+ public void should_escape_html() {
+ assertHighlighting("quick< brown fox", "brown", "quick< <mark>brown</mark> fox");
+ }
+
+ @Test
+ public void should_highlight_partial_name() {
+ assertHighlighting("quickbrownfox", "brown", "quick<mark>brown</mark>fox");
+ }
+
@Test
public void should_highlight_prefix() {
- assertHighlighting("quick brown fox", "brown", "quick <mark>brown</mark> fox");
+ assertHighlighting("quickbrownfox", "quick", "<mark>quick</mark>brownfox");
}
@Test
- public void should_escape_html() {
- assertHighlighting("quick< brown fox", "brown", "quick< <mark>brown</mark> fox");
+ public void should_highlight_suffix() {
+ assertHighlighting("quickbrownfox", "fox", "quickbrown<mark>fox</mark>");
+ }
+
+ @Test
+ public void should_highlight_multiple_words() {
+ assertHighlighting("quickbrownfox", "fox bro", "quick<mark>bro</mark>wn<mark>fox</mark>");
+ }
+
+ @Test
+ public void should_highlight_multiple_connected_words() {
+ assertHighlighting("quickbrownfox", "fox brown", "quick<mark>brownfox</mark>");
}
private void assertHighlighting(String fileName, String search, String expectedHighlighting) {