]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8890 Restrict ps parameter of WS api/issues/tags
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Mon, 3 Jul 2017 10:06:57 +0000 (12:06 +0200)
committerTeryk Bellahsene <teryk@users.noreply.github.com>
Fri, 28 Jul 2017 15:05:09 +0000 (17:05 +0200)
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/TagsAction.java
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/TagsActionTest.java

index a19ce5bb2beb09e2543ec9149d6dabd79bca0b0a..df819023a296e3f349980a3cec969862438c0f1b 100644 (file)
@@ -70,7 +70,9 @@ import org.sonar.server.user.UserSession;
 import org.sonar.server.view.index.ViewIndexDefinition;
 
 import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Preconditions.checkArgument;
 import static java.lang.String.format;
+import static java.util.Collections.emptyList;
 import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
 import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
@@ -106,8 +108,6 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TYPES;
  */
 public class IssueIndex {
 
-  private static final String SUBSTRING_MATCH_REGEXP = ".*%s.*";
-
   public static final List<String> SUPPORTED_FACETS = ImmutableList.of(
     PARAM_SEVERITIES,
     PARAM_STATUSES,
@@ -126,23 +126,18 @@ public class IssueIndex {
     PARAM_TAGS,
     PARAM_TYPES,
     PARAM_CREATED_AT);
-
+  public static final String AGGREGATION_NAME_FOR_TAGS = "tags__issues";
+  private static final String SUBSTRING_MATCH_REGEXP = ".*%s.*";
   // TODO to be documented
   // TODO move to Facets ?
   private static final String FACET_SUFFIX_MISSING = "_missing";
-
   private static final String IS_ASSIGNED_FILTER = "__isAssigned";
-
   private static final SumBuilder EFFORT_AGGREGATION = AggregationBuilders.sum(FACET_MODE_EFFORT).field(IssueIndexDefinition.FIELD_ISSUE_EFFORT);
   private static final Order EFFORT_AGGREGATION_ORDER = Order.aggregation(FACET_MODE_EFFORT, false);
-
   private static final int DEFAULT_FACET_SIZE = 15;
   private static final Duration TWENTY_DAYS = Duration.standardDays(20L);
   private static final Duration TWENTY_WEEKS = Duration.standardDays(20L * 7L);
   private static final Duration TWENTY_MONTHS = Duration.standardDays(20L * 30L);
-
-  public static final String AGGREGATION_NAME_FOR_TAGS = "tags__issues";
-
   private final Sorting sorting;
   private final EsClient client;
   private final System2 system;
@@ -176,6 +171,160 @@ public class IssueIndex {
     this.sorting.addDefault(IssueIndexDefinition.FIELD_ISSUE_KEY);
   }
 
+  /**
+   * Optimization - do not send ES request to all shards when scope is restricted
+   * to a set of projects. Because project UUID is used for routing, the request
+   * can be sent to only the shards containing the specified projects.
+   * Note that sticky facets may involve all projects, so this optimization must be
+   * disabled when facets are enabled.
+   */
+  private static void configureRouting(IssueQuery query, SearchOptions options, SearchRequestBuilder requestBuilder) {
+    Collection<String> uuids = query.projectUuids();
+    if (!uuids.isEmpty() && options.getFacets().isEmpty()) {
+      requestBuilder.setRouting(uuids.toArray(new String[uuids.size()]));
+    }
+  }
+
+  private static void configurePagination(SearchOptions options, SearchRequestBuilder esSearch) {
+    esSearch.setFrom(options.getOffset()).setSize(options.getLimit());
+  }
+
+  private static void addComponentRelatedFilters(IssueQuery query, Map<String, QueryBuilder> filters) {
+    QueryBuilder viewFilter = createViewFilter(query.viewUuids());
+    QueryBuilder componentFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.componentUuids());
+    QueryBuilder projectFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, query.projectUuids());
+    QueryBuilder moduleRootFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_PATH, query.moduleRootUuids());
+    QueryBuilder moduleFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, query.moduleUuids());
+    QueryBuilder directoryFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, query.directories());
+    QueryBuilder fileFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.fileUuids());
+
+    if (BooleanUtils.isTrue(query.onComponentOnly())) {
+      filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentFilter);
+    } else {
+      filters.put("__view", viewFilter);
+      filters.put(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, projectFilter);
+      filters.put("__module", moduleRootFilter);
+      filters.put(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, moduleFilter);
+      filters.put(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, directoryFilter);
+      if (fileFilter != null) {
+        filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, fileFilter);
+      } else {
+        filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentFilter);
+      }
+    }
+  }
+
+  @CheckForNull
+  private static QueryBuilder createViewFilter(Collection<String> viewUuids) {
+    if (viewUuids.isEmpty()) {
+      return null;
+    }
+
+    BoolQueryBuilder viewsFilter = boolQuery();
+    for (String viewUuid : viewUuids) {
+      viewsFilter.should(QueryBuilders.termsLookupQuery(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID)
+        .lookupIndex(ViewIndexDefinition.INDEX_TYPE_VIEW.getIndex())
+        .lookupType(ViewIndexDefinition.INDEX_TYPE_VIEW.getType())
+        .lookupId(viewUuid)
+        .lookupPath(ViewIndexDefinition.FIELD_PROJECTS));
+    }
+    return viewsFilter;
+  }
+
+  private static StickyFacetBuilder newStickyFacetBuilder(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder esQuery) {
+    if (hasQueryEffortFacet(query)) {
+      return new StickyFacetBuilder(esQuery, filters, EFFORT_AGGREGATION, EFFORT_AGGREGATION_ORDER);
+    }
+    return new StickyFacetBuilder(esQuery, filters);
+  }
+
+  private static void addSimpleStickyFacetIfNeeded(SearchOptions options, StickyFacetBuilder stickyFacetBuilder, SearchRequestBuilder esSearch,
+    String facetName, String fieldName, Object... selectedValues) {
+    if (options.getFacets().contains(facetName)) {
+      esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(fieldName, facetName, DEFAULT_FACET_SIZE, selectedValues));
+    }
+  }
+
+  private static AggregationBuilder addEffortAggregationIfNeeded(IssueQuery query, AggregationBuilder aggregation) {
+    if (hasQueryEffortFacet(query)) {
+      aggregation.subAggregation(EFFORT_AGGREGATION);
+    }
+    return aggregation;
+  }
+
+  private static boolean hasQueryEffortFacet(IssueQuery query) {
+    return FACET_MODE_EFFORT.equals(query.facetMode()) || DEPRECATED_FACET_MODE_DEBT.equals(query.facetMode());
+  }
+
+  private static AggregationBuilder createAssigneesFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder queryBuilder) {
+    String fieldName = IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE;
+    String facetName = PARAM_ASSIGNEES;
+
+    // Same as in super.stickyFacetBuilder
+    Map<String, QueryBuilder> assigneeFilters = Maps.newHashMap(filters);
+    assigneeFilters.remove(IS_ASSIGNED_FILTER);
+    assigneeFilters.remove(fieldName);
+    StickyFacetBuilder assigneeFacetBuilder = newStickyFacetBuilder(query, assigneeFilters, queryBuilder);
+    BoolQueryBuilder facetFilter = assigneeFacetBuilder.getStickyFacetFilter(fieldName);
+    FilterAggregationBuilder facetTopAggregation = assigneeFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, DEFAULT_FACET_SIZE);
+
+    Collection<String> assigneesEscaped = escapeValuesForFacetInclusion(query.assignees());
+    if (!assigneesEscaped.isEmpty()) {
+      facetTopAggregation = assigneeFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, t -> t, assigneesEscaped.toArray());
+    }
+
+    // Add missing facet for unassigned issues
+    facetTopAggregation.subAggregation(
+      addEffortAggregationIfNeeded(query, AggregationBuilders
+        .missing(facetName + FACET_SUFFIX_MISSING)
+        .field(fieldName)));
+
+    return AggregationBuilders
+      .global(facetName)
+      .subAggregation(facetTopAggregation);
+  }
+
+  private static Collection<String> escapeValuesForFacetInclusion(@Nullable Collection<String> values) {
+    if (values == null) {
+      return Collections.emptyList();
+    }
+    return values.stream().map(Pattern::quote).collect(MoreCollectors.toArrayList(values.size()));
+  }
+
+  private static AggregationBuilder createResolutionFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder esQuery) {
+    String fieldName = IssueIndexDefinition.FIELD_ISSUE_RESOLUTION;
+    String facetName = PARAM_RESOLUTIONS;
+
+    // Same as in super.stickyFacetBuilder
+    Map<String, QueryBuilder> resolutionFilters = Maps.newHashMap(filters);
+    resolutionFilters.remove("__isResolved");
+    resolutionFilters.remove(fieldName);
+    StickyFacetBuilder assigneeFacetBuilder = newStickyFacetBuilder(query, resolutionFilters, esQuery);
+    BoolQueryBuilder facetFilter = assigneeFacetBuilder.getStickyFacetFilter(fieldName);
+    FilterAggregationBuilder facetTopAggregation = assigneeFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, DEFAULT_FACET_SIZE);
+    facetTopAggregation = assigneeFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, t -> t);
+
+    // Add missing facet for unresolved issues
+    facetTopAggregation.subAggregation(
+      addEffortAggregationIfNeeded(query, AggregationBuilders
+        .missing(facetName + FACET_SUFFIX_MISSING)
+        .field(fieldName)));
+
+    return AggregationBuilders
+      .global(facetName)
+      .subAggregation(facetTopAggregation);
+  }
+
+  @CheckForNull
+  private static QueryBuilder createTermsFilter(String field, Collection<?> values) {
+    return values.isEmpty() ? null : termsQuery(field, values);
+  }
+
+  @CheckForNull
+  private static QueryBuilder createTermFilter(String field, @Nullable String value) {
+    return value == null ? null : termQuery(field, value);
+  }
+
   public SearchResponse search(IssueQuery query, SearchOptions options) {
     SearchRequestBuilder requestBuilder = client.prepareSearch(INDEX_TYPE_ISSUE);
 
@@ -202,20 +351,6 @@ public class IssueIndex {
     return requestBuilder.get();
   }
 
-  /**
-   * Optimization - do not send ES request to all shards when scope is restricted
-   * to a set of projects. Because project UUID is used for routing, the request
-   * can be sent to only the shards containing the specified projects.
-   * Note that sticky facets may involve all projects, so this optimization must be
-   * disabled when facets are enabled.
-   */
-  private static void configureRouting(IssueQuery query, SearchOptions options, SearchRequestBuilder requestBuilder) {
-    Collection<String> uuids = query.projectUuids();
-    if (!uuids.isEmpty() && options.getFacets().isEmpty()) {
-      requestBuilder.setRouting(uuids.toArray(new String[uuids.size()]));
-    }
-  }
-
   private void configureSorting(IssueQuery query, SearchRequestBuilder esRequest) {
     String sortField = query.sort();
     if (sortField != null) {
@@ -226,10 +361,6 @@ public class IssueIndex {
     }
   }
 
-  private static void configurePagination(SearchOptions options, SearchRequestBuilder esSearch) {
-    esSearch.setFrom(options.getOffset()).setSize(options.getLimit());
-  }
-
   private Map<String, QueryBuilder> createFilters(IssueQuery query) {
     Map<String, QueryBuilder> filters = new HashMap<>();
     filters.put("__authorization", createAuthorizationFilter(query.checkAuthorization()));
@@ -270,48 +401,6 @@ public class IssueIndex {
     return filters;
   }
 
-  private static void addComponentRelatedFilters(IssueQuery query, Map<String, QueryBuilder> filters) {
-    QueryBuilder viewFilter = createViewFilter(query.viewUuids());
-    QueryBuilder componentFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.componentUuids());
-    QueryBuilder projectFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, query.projectUuids());
-    QueryBuilder moduleRootFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_PATH, query.moduleRootUuids());
-    QueryBuilder moduleFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, query.moduleUuids());
-    QueryBuilder directoryFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, query.directories());
-    QueryBuilder fileFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.fileUuids());
-
-    if (BooleanUtils.isTrue(query.onComponentOnly())) {
-      filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentFilter);
-    } else {
-      filters.put("__view", viewFilter);
-      filters.put(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, projectFilter);
-      filters.put("__module", moduleRootFilter);
-      filters.put(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, moduleFilter);
-      filters.put(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, directoryFilter);
-      if (fileFilter != null) {
-        filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, fileFilter);
-      } else {
-        filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentFilter);
-      }
-    }
-  }
-
-  @CheckForNull
-  private static QueryBuilder createViewFilter(Collection<String> viewUuids) {
-    if (viewUuids.isEmpty()) {
-      return null;
-    }
-
-    BoolQueryBuilder viewsFilter = boolQuery();
-    for (String viewUuid : viewUuids) {
-      viewsFilter.should(QueryBuilders.termsLookupQuery(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID)
-        .lookupIndex(ViewIndexDefinition.INDEX_TYPE_VIEW.getIndex())
-        .lookupType(ViewIndexDefinition.INDEX_TYPE_VIEW.getType())
-        .lookupId(viewUuid)
-        .lookupPath(ViewIndexDefinition.FIELD_PROJECTS));
-    }
-    return viewsFilter;
-  }
-
   private QueryBuilder createAuthorizationFilter(boolean checkAuthorization) {
     if (checkAuthorization) {
       return authorizationTypeSupport.createQueryFilter();
@@ -395,31 +484,6 @@ public class IssueIndex {
     }
   }
 
-  private static StickyFacetBuilder newStickyFacetBuilder(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder esQuery) {
-    if (hasQueryEffortFacet(query)) {
-      return new StickyFacetBuilder(esQuery, filters, EFFORT_AGGREGATION, EFFORT_AGGREGATION_ORDER);
-    }
-    return new StickyFacetBuilder(esQuery, filters);
-  }
-
-  private static void addSimpleStickyFacetIfNeeded(SearchOptions options, StickyFacetBuilder stickyFacetBuilder, SearchRequestBuilder esSearch,
-    String facetName, String fieldName, Object... selectedValues) {
-    if (options.getFacets().contains(facetName)) {
-      esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(fieldName, facetName, DEFAULT_FACET_SIZE, selectedValues));
-    }
-  }
-
-  private static AggregationBuilder addEffortAggregationIfNeeded(IssueQuery query, AggregationBuilder aggregation) {
-    if (hasQueryEffortFacet(query)) {
-      aggregation.subAggregation(EFFORT_AGGREGATION);
-    }
-    return aggregation;
-  }
-
-  private static boolean hasQueryEffortFacet(IssueQuery query) {
-    return FACET_MODE_EFFORT.equals(query.facetMode()) || DEPRECATED_FACET_MODE_DEBT.equals(query.facetMode());
-  }
-
   private Optional<AggregationBuilder> getCreatedAtFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder esQuery) {
     long startTime;
     Date createdAfter = query.createdAfter();
@@ -483,41 +547,6 @@ public class IssueIndex {
     return Optional.of(actualValue.longValue());
   }
 
-  private static AggregationBuilder createAssigneesFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder queryBuilder) {
-    String fieldName = IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE;
-    String facetName = PARAM_ASSIGNEES;
-
-    // Same as in super.stickyFacetBuilder
-    Map<String, QueryBuilder> assigneeFilters = Maps.newHashMap(filters);
-    assigneeFilters.remove(IS_ASSIGNED_FILTER);
-    assigneeFilters.remove(fieldName);
-    StickyFacetBuilder assigneeFacetBuilder = newStickyFacetBuilder(query, assigneeFilters, queryBuilder);
-    BoolQueryBuilder facetFilter = assigneeFacetBuilder.getStickyFacetFilter(fieldName);
-    FilterAggregationBuilder facetTopAggregation = assigneeFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, DEFAULT_FACET_SIZE);
-
-    Collection<String> assigneesEscaped = escapeValuesForFacetInclusion(query.assignees());
-    if (!assigneesEscaped.isEmpty()) {
-      facetTopAggregation = assigneeFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, t -> t, assigneesEscaped.toArray());
-    }
-
-    // Add missing facet for unassigned issues
-    facetTopAggregation.subAggregation(
-      addEffortAggregationIfNeeded(query, AggregationBuilders
-        .missing(facetName + FACET_SUFFIX_MISSING)
-        .field(fieldName)));
-
-    return AggregationBuilders
-      .global(facetName)
-      .subAggregation(facetTopAggregation);
-  }
-
-  private static Collection<String> escapeValuesForFacetInclusion(@Nullable Collection<String> values) {
-    if (values == null) {
-      return Collections.emptyList();
-    }
-    return values.stream().map(Pattern::quote).collect(MoreCollectors.toArrayList(values.size()));
-  }
-
   private void addAssignedToMeFacetIfNeeded(SearchRequestBuilder builder, SearchOptions options, IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder queryBuilder) {
     String login = userSession.getLogin();
 
@@ -544,41 +573,13 @@ public class IssueIndex {
         .subAggregation(facetTopAggregation));
   }
 
-  private static AggregationBuilder createResolutionFacet(IssueQuery query, Map<String, QueryBuilder> filters, QueryBuilder esQuery) {
-    String fieldName = IssueIndexDefinition.FIELD_ISSUE_RESOLUTION;
-    String facetName = PARAM_RESOLUTIONS;
-
-    // Same as in super.stickyFacetBuilder
-    Map<String, QueryBuilder> resolutionFilters = Maps.newHashMap(filters);
-    resolutionFilters.remove("__isResolved");
-    resolutionFilters.remove(fieldName);
-    StickyFacetBuilder assigneeFacetBuilder = newStickyFacetBuilder(query, resolutionFilters, esQuery);
-    BoolQueryBuilder facetFilter = assigneeFacetBuilder.getStickyFacetFilter(fieldName);
-    FilterAggregationBuilder facetTopAggregation = assigneeFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, DEFAULT_FACET_SIZE);
-    facetTopAggregation = assigneeFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, t -> t);
-
-    // Add missing facet for unresolved issues
-    facetTopAggregation.subAggregation(
-      addEffortAggregationIfNeeded(query, AggregationBuilders
-        .missing(facetName + FACET_SUFFIX_MISSING)
-        .field(fieldName)));
-
-    return AggregationBuilders
-      .global(facetName)
-      .subAggregation(facetTopAggregation);
-  }
-
-  @CheckForNull
-  private static QueryBuilder createTermsFilter(String field, Collection<?> values) {
-    return values.isEmpty() ? null : termsQuery(field, values);
-  }
-
-  @CheckForNull
-  private static QueryBuilder createTermFilter(String field, @Nullable String value) {
-    return value == null ? null : termQuery(field, value);
-  }
+  public List<String> listTags(OrganizationDto organization, @Nullable String textQuery, int size) {
+    int maxPageSize = 500;
+    checkArgument(size <= maxPageSize, "Page size must be lower than or equals to " + maxPageSize);
+    if (size <= 0) {
+      return emptyList();
+    }
 
-  public List<String> listTags(OrganizationDto organization, @Nullable String textQuery, int maxNumberOfTags) {
     SearchRequestBuilder requestBuilder = client
       .prepareSearch(INDEX_TYPE_ISSUE)
       .setQuery(boolQuery()
@@ -588,7 +589,7 @@ public class IssueIndex {
 
     TermsBuilder termsAggregation = AggregationBuilders.terms(AGGREGATION_NAME_FOR_TAGS)
       .field(IssueIndexDefinition.FIELD_ISSUE_TAGS)
-      .size(maxNumberOfTags)
+      .size(size)
       .order(Terms.Order.term(true))
       .minDocCount(1L);
     if (textQuery != null) {
index fd97bac9dd81224fcdfcace59a5f6ee21da10752..0359656f0ed0476332a95c3376a58da633e4bff5 100644 (file)
@@ -41,7 +41,6 @@ import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.rule.index.RuleIndex;
 import org.sonar.server.ws.WsUtils;
 
-import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ORGANIZATION;
 
 /**
@@ -62,6 +61,14 @@ public class TagsAction implements IssuesWsAction {
     this.defaultOrganizationProvider = defaultOrganizationProvider;
   }
 
+  private static void writeResponse(Response response, List<String> tags) {
+    try (JsonWriter json = response.newJsonWriter()) {
+      json.beginObject().name("tags").beginArray();
+      tags.forEach(json::value);
+      json.endArray().endObject();
+    }
+  }
+
   @Override
   public void define(WebService.NewController controller) {
     NewAction action = controller.createAction("tags")
@@ -69,13 +76,8 @@ public class TagsAction implements IssuesWsAction {
       .setSince("5.1")
       .setDescription("List tags matching a given query")
       .setResponseExample(Resources.getResource(getClass(), "tags-example.json"));
-    action.createParam(Param.TEXT_QUERY)
-      .setDescription("A pattern to match tags against")
-      .setExampleValue("misra");
-    action.createParam(PAGE_SIZE)
-      .setDescription("The size of the list to return")
-      .setExampleValue("25")
-      .setDefaultValue("10");
+    action.createSearchQuery("misra", "tags");
+    action.createPageSize(10, 100);
     action.createParam(PARAM_ORGANIZATION)
       .setDescription("Organization key")
       .setRequired(false)
@@ -114,12 +116,4 @@ public class TagsAction implements IssuesWsAction {
     }
   }
 
-  private static void writeResponse(Response response, List<String> tags) {
-    try (JsonWriter json = response.newJsonWriter()) {
-      json.beginObject().name("tags").beginArray();
-      tags.forEach(json::value);
-      json.endArray().endObject();
-    }
-  }
-
 }
index 416f253ef7e463c77fb99a90f1fd8a6ea85f9bbc..7e68dac492b61a4e08ba95670d021b35f827c0af 100644 (file)
@@ -77,20 +77,20 @@ import static org.sonar.server.issue.IssueDocTesting.newDoc;
 
 public class IssueIndexTest {
 
-  private System2 system2 = mock(System2.class);
   private MapSettings settings = new MapSettings();
+
   @Rule
   public EsTester es = new EsTester(
     new IssueIndexDefinition(settings.asConfig()),
     new ViewIndexDefinition(settings.asConfig()),
     new RuleIndexDefinition(settings.asConfig()));
   @Rule
-  public DbTester db = DbTester.create(system2);
-  @Rule
   public UserSessionRule userSessionRule = UserSessionRule.standalone();
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
-
+  private System2 system2 = mock(System2.class);
+  @Rule
+  public DbTester db = DbTester.create(system2);
   private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()));
   private ViewIndexer viewIndexer = new ViewIndexer(db.getDbClient(), es.client());
   private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient());
@@ -1083,12 +1083,22 @@ public class IssueIndexTest {
       newDoc("I3", file).setOrganizationUuid(org.getUuid()).setRuleKey(r2.getKey().toString()),
       newDoc("I4", file).setOrganizationUuid(org.getUuid()).setRuleKey(r1.getKey().toString()).setTags(of("convention")));
 
-    assertThat(underTest.listTags(org, null, Integer.MAX_VALUE)).containsOnly("convention", "java8", "bug");
+    assertThat(underTest.listTags(org, null, 100)).containsOnly("convention", "java8", "bug");
     assertThat(underTest.listTags(org, null, 2)).containsOnly("bug", "convention");
-    assertThat(underTest.listTags(org, "vent", Integer.MAX_VALUE)).containsOnly("convention");
+    assertThat(underTest.listTags(org, "vent", 100)).containsOnly("convention");
     assertThat(underTest.listTags(org, null, 1)).containsOnly("bug");
-    assertThat(underTest.listTags(org, null, Integer.MAX_VALUE)).containsOnly("convention", "java8", "bug");
-    assertThat(underTest.listTags(org, "invalidRegexp[", Integer.MAX_VALUE)).isEmpty();
+    assertThat(underTest.listTags(org, null, 100)).containsOnly("convention", "java8", "bug");
+    assertThat(underTest.listTags(org, "invalidRegexp[", 100)).isEmpty();
+  }
+
+  @Test
+  public void fail_to_list_tags_when_size_greater_than_500() {
+    OrganizationDto organization = db.organizations().insert();
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Page size must be lower than or equals to 500");
+
+    underTest.listTags(organization, null, 501);
   }
 
   @Test
index 0535e58ed6bce7b04471a1af61d56522fb01657a..20cf8e1ea691e7961a9802a075582fbd73d09ee9 100644 (file)
@@ -45,11 +45,11 @@ public class SearchActionTest {
   private SearchResponseLoader searchResponseLoader = mock(SearchResponseLoader.class);
   private SearchResponseFormat searchResponseFormat = mock(SearchResponseFormat.class);
   private SearchAction underTest = new SearchAction(userSession, index, issueQueryFactory, searchResponseLoader, searchResponseFormat);
-  private WsActionTester wsTester = new WsActionTester(underTest);
+  private WsActionTester ws = new WsActionTester(underTest);
 
   @Test
   public void test_definition() {
-    WebService.Action def = wsTester.getDef();
+    WebService.Action def = ws.getDef();
     assertThat(def.key()).isEqualTo("search");
     assertThat(def.isInternal()).isFalse();
     assertThat(def.isPost()).isFalse();
index b861d7840a0541cb1631baa506b064274a236165..69d49c14489049cf40f63e1f61b0cbca010db084 100644 (file)
@@ -70,7 +70,7 @@ public class TagsActionTest {
   private IssueIndex issueIndex = new IssueIndex(esTester.client(), System2.INSTANCE, userSession, new AuthorizationTypeSupport(userSession));
   private RuleIndex ruleIndex = new RuleIndex(esTester.client());
 
-  private WsActionTester tester = new WsActionTester(new TagsAction(issueIndex, ruleIndex, dbTester.getDbClient(), TestDefaultOrganizationProvider.from(dbTester)));
+  private WsActionTester ws = new WsActionTester(new TagsAction(issueIndex, ruleIndex, dbTester.getDbClient(), TestDefaultOrganizationProvider.from(dbTester)));
   private OrganizationDto organization;
 
   @Before
@@ -84,7 +84,7 @@ public class TagsActionTest {
     insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag1", "tag2");
     insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag3", "tag4", "tag5");
 
-    String result = tester.newRequest()
+    String result = ws.newRequest()
       .setParam("organization", organization.getKey())
       .execute().getInput();
     assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag2\", \"tag3\", \"tag4\", \"tag5\"]}");
@@ -103,7 +103,7 @@ public class TagsActionTest {
     dbTester.rules().insertOrUpdateMetadata(r2, organization, setTags("tag4", "tag5"));
     ruleIndexer.commitAndIndex(dbTester.getSession(), r2.getKey(), organization);
 
-    String result = tester.newRequest()
+    String result = ws.newRequest()
       .setParam("organization", organization.getKey())
       .execute().getInput();
     assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag2\", \"tag3\", \"tag4\", \"tag5\"]}");
@@ -120,7 +120,7 @@ public class TagsActionTest {
     dbTester.rules().insertOrUpdateMetadata(r, organization, setTags("tag7"));
     ruleIndexer.commitAndIndex(dbTester.getSession(), r.getKey(), organization);
 
-    String result = tester.newRequest()
+    String result = ws.newRequest()
       .setParam("organization", organization.getKey())
       .execute().getInput();
     assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag2\", \"tag3\", \"tag4\", \"tag5\", \"tag6\", \"tag7\"]}");
@@ -132,7 +132,7 @@ public class TagsActionTest {
     insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag1", "tag2");
     insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag3", "tag4", "tag5");
 
-    String result = tester.newRequest()
+    String result = ws.newRequest()
       .setParam("ps", "2")
       .setParam("organization", organization.getKey())
       .execute().getInput();
@@ -145,7 +145,7 @@ public class TagsActionTest {
     insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag1", "tag2");
     insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag12", "tag4", "tag5");
 
-    String result = tester.newRequest()
+    String result = ws.newRequest()
       .setParam("q", "ag1")
       .setParam("organization", organization.getKey())
       .execute().getInput();
@@ -158,7 +158,7 @@ public class TagsActionTest {
     insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag1", "tag2");
     insertIssueWithoutBrowsePermission(insertRuleWithoutTags(), "tag3", "tag4");
 
-    String result = tester.newRequest()
+    String result = ws.newRequest()
       .setParam("organization", organization.getKey())
       .execute().getInput();
     assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag2\"]}");
@@ -166,14 +166,14 @@ public class TagsActionTest {
   }
 
   @Test
-  public void return_empty_list() throws Exception {
+  public void empty_list() throws Exception {
     userSession.logIn();
-    String result = tester.newRequest().execute().getInput();
+    String result = ws.newRequest().execute().getInput();
     assertJson(result).isSimilarTo("{\"tags\":[]}");
   }
 
   @Test
-  public void test_example() throws Exception {
+  public void json_example() throws Exception {
     userSession.logIn();
     insertIssueWithBrowsePermission(insertRuleWithoutTags(), "convention");
 
@@ -182,16 +182,17 @@ public class TagsActionTest {
     dbTester.rules().insertOrUpdateMetadata(r, organization, setTags("security"));
     ruleIndexer.commitAndIndex(dbTester.getSession(), r.getKey(), organization);
 
-    String result = tester.newRequest()
+    String result = ws.newRequest()
       .setParam("organization", organization.getKey())
       .execute().getInput();
-    assertJson(result).isSimilarTo(tester.getDef().responseExampleAsString());
+
+    assertJson(result).isSimilarTo(ws.getDef().responseExampleAsString());
   }
 
   @Test
-  public void test_definition() {
+  public void definition() {
     userSession.logIn();
-    Action action = tester.getDef();
+    Action action = ws.getDef();
     assertThat(action.description()).isNotEmpty();
     assertThat(action.responseExampleAsString()).isNotEmpty();
     assertThat(action.isPost()).isFalse();
@@ -199,13 +200,11 @@ public class TagsActionTest {
     assertThat(action.params()).extracting(Param::key).containsExactlyInAnyOrder("q", "ps", "organization");
 
     Param query = action.param("q");
-    assertThat(query).isNotNull();
     assertThat(query.isRequired()).isFalse();
     assertThat(query.description()).isNotEmpty();
     assertThat(query.exampleValue()).isNotEmpty();
 
     Param pageSize = action.param("ps");
-    assertThat(pageSize).isNotNull();
     assertThat(pageSize.isRequired()).isFalse();
     assertThat(pageSize.defaultValue()).isEqualTo("10");
     assertThat(pageSize.description()).isNotEmpty();