import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
-import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.ObjectUtils;
import org.sonarqube.ws.client.issue.SearchWsRequest;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Predicates.notNull;
+import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
import static org.sonar.api.utils.DateUtils.longToDate;
import static org.sonar.db.component.ComponentDtoFunctions.toUuid;
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
import static org.sonar.server.ws.WsUtils.checkRequest;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENTS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENT_KEYS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENT_ROOTS;
+import static org.sonarqube.ws.client.issue.IssueFilterParameters.COMPONENT_UUIDS;
import static org.sonarqube.ws.client.issue.IssueFilterParameters.CREATED_AFTER;
import static org.sonarqube.ws.client.issue.IssueFilterParameters.CREATED_IN_LAST;
import static org.sonarqube.ws.client.issue.IssueFilterParameters.SINCE_LEAK_PERIOD;
request.getFileUuids(),
request.getAuthors());
- builder.createdAfter(buildCreatedAfterFromRequest(session, request, allComponentUuids, effectiveOnComponentOnly));
+ builder.createdAfter(buildCreatedAfterFromRequest(session, request, allComponentUuids));
String sort = request.getSort();
if (!Strings.isNullOrEmpty(sort)) {
}
}
- private Date buildCreatedAfterFromRequest(DbSession dbSession, SearchWsRequest request, Set<String> componentUuids, boolean effectiveOnComponentOnly) {
+ private Date buildCreatedAfterFromRequest(DbSession dbSession, SearchWsRequest request, Set<String> componentUuids) {
Date createdAfter = parseAsDateTime(request.getCreatedAfter());
String createdInLast = request.getCreatedInLast();
}
checkRequest(createdAfter == null, "'%s' and '%s' cannot be set simultaneously", CREATED_AFTER, SINCE_LEAK_PERIOD);
- Set<String> allComponentUuids = new HashSet<>(componentUuids);
- if (!effectiveOnComponentOnly) {
- if (request.getProjectKeys() != null) {
- allComponentUuids.addAll(componentUuids(dbSession, request.getProjectKeys()));
- }
- if (request.getProjectUuids() != null) {
- allComponentUuids.addAll(request.getProjectUuids());
- }
- if (request.getModuleUuids() != null) {
- allComponentUuids.addAll(request.getModuleUuids());
- }
- if (request.getFileUuids() != null) {
- allComponentUuids.addAll(request.getFileUuids());
- }
- }
- checkArgument(allComponentUuids.size() == 1, "One and only one component must be provided when searching since leak period");
- String uuid = allComponentUuids.iterator().next();
+ checkArgument(componentUuids.size() == 1, "One and only one component must be provided when searching since leak period");
+ String uuid = componentUuids.iterator().next();
// TODO use ComponentFinder instead
Date createdAfterFromSnapshot = findCreatedAfterFromComponentUuid(dbSession, uuid);
return buildCreatedAfterFromDates(createdAfterFromSnapshot, createdInLast);
}
+ @CheckForNull
private Date findCreatedAfterFromComponentUuid(DbSession dbSession, String uuid) {
ComponentDto component = checkFoundWithOptional(componentService.getByUuid(uuid), "Component with id '%s' not found", uuid);
SnapshotDto snapshot = dbClient.snapshotDao().selectLastSnapshotByComponentId(dbSession, component.getId());
Set<String> allComponentUuids) {
boolean effectiveOnComponentOnly = false;
- failIfBothParametersSet(componentRootUuids, componentRoots, "componentRoots and componentRootUuids cannot be set simultaneously");
- failIfBothParametersSet(componentUuids, components, "components and componentUuids cannot be set simultaneously");
- failIfBothParametersSet(componentKeys, componentUuids, "componentKeys and componentUuids cannot be set simultaneously");
+ checkArgument(atMostOneNonNullElement(components, componentUuids, componentKeys, componentRootUuids, componentRoots),
+ "At most one of the following parameters can be provided: %s, %s, %s, %s, %s",
+ COMPONENT_KEYS, COMPONENT_UUIDS, COMPONENTS, COMPONENT_ROOTS, COMPONENT_UUIDS);
if (componentRootUuids != null) {
allComponentUuids.addAll(componentRootUuids);
return effectiveOnComponentOnly;
}
- private void failIfBothParametersSet(@Nullable Collection<String> uuids, @Nullable Collection<String> keys, String message) {
- if (uuids != null && keys != null) {
- throw new IllegalArgumentException(message);
- }
+ private static boolean atMostOneNonNullElement(Object... objects) {
+ return !from(Arrays.asList(objects))
+ .filter(notNull())
+ .anyMatch(new HasTwoOrMoreElements());
}
private void addComponentParameters(IssueQuery.Builder builder, DbSession session,
}
builder.authors(authors);
- failIfBothParametersSet(projectUuids, projects, "projects and projectUuids cannot be set simultaneously");
+ checkArgument(projectUuids == null || projects == null, "projects and projectUuids cannot be set simultaneously");
if (projectUuids != null) {
builder.projectUuids(projectUuids);
} else {
}
}
}
+
+ private static class HasTwoOrMoreElements implements Predicate<Object> {
+ private AtomicInteger counter;
+
+ private HasTwoOrMoreElements() {
+ this.counter = new AtomicInteger();
+ }
+
+ @Override
+ public boolean apply(@Nonnull Object input) {
+ Objects.requireNonNull(input);
+ return counter.incrementAndGet() >= 2;
+ }
+ }
}
import com.google.common.base.Function;
import com.google.common.collect.Lists;
-import com.google.common.io.Resources;
import java.util.Collection;
import java.util.EnumSet;
import java.util.LinkedHashMap;
.createAction(SEARCH_ACTION)
.setHandler(this)
.setDescription(
- "Search for issues. Requires Browse permission on project(s).<br/>" +
- "Since 5.5, response field 'debt' has been renamed to 'effort'.<br/>" +
- "Since 5.5, response field 'actionPlan' has been removed.<br/>" +
- "Since 5.5, response field 'reporter' has been removed, as manual issue feature has been dropped")
+ "Search for issues. Requires Browse permission on project(s).<br>" +
+ "At most one of the following parameters can be provided at the same time: %s, %s, %s, %s, %s<br>" +
+ "Since 5.5, response field 'debt' has been renamed to 'effort'.<br>" +
+ "Since 5.5, response field 'actionPlan' has been removed.<br>" +
+ "Since 5.5, response field 'reporter' has been removed, as manual issue feature has been dropped.",
+ COMPONENT_KEYS, COMPONENT_UUIDS, COMPONENTS, COMPONENT_ROOT_UUIDS, COMPONENT_ROOTS)
.setSince("3.6")
- .setResponseExample(Resources.getResource(this.getClass(), "example-search.json"));
+ .setResponseExample(getClass().getResource("example-search.json"));
action.addPagingParams(100, MAX_LIMIT);
action.createParam(Param.FACETS)
action.createParam(COMPONENT_KEYS)
.setDescription("To retrieve issues associated to a specific list of components sub-components (comma-separated list of component keys). " +
- "A component can be a view, developer, project, module, directory or file. " +
- "If this parameter is set, componentUuids must not be set.")
+ "A component can be a view, developer, project, module, directory or file.")
.setExampleValue(KEY_PROJECT_EXAMPLE_001);
action.createParam(COMPONENTS)
.setDeprecatedSince("5.1")
action.createParam(COMPONENT_UUIDS)
.setDescription("To retrieve issues associated to a specific list of components their sub-components (comma-separated list of component UUIDs). " +
INTERNAL_PARAMETER_DISCLAIMER +
- "A component can be a project, module, directory or file. " +
- "If this parameter is set, componentKeys must not be set.")
+ "A component can be a project, module, directory or file.")
.setExampleValue("584a89f2-8037-4f7b-b82c-8b45d2d63fb2");
-
action.createParam(COMPONENT_ROOTS)
.setDeprecatedSince("5.1")
.setDescription("If used, will have the same meaning as componentKeys AND onComponentOnly=false.");
.setExampleValue("7d8749e8-3070-4903-9188-bdd82933bb92");
action.createParam(DIRECTORIES)
- .setDescription("Since 5.1. To retrieve issues associated to a specific list of directories (comma-separated list of directory paths). " +
+ .setDescription("To retrieve issues associated to a specific list of directories (comma-separated list of directory paths). " +
"This parameter is only meaningful when a module is selected. " +
INTERNAL_PARAMETER_DISCLAIMER)
+ .setSince("5.1")
.setExampleValue("src/main/java/org/sonar/server/");
action.createParam(FILE_UUIDS)
addMandatoryValuesToFacet(facets, LANGUAGES, request.getLanguages());
addMandatoryValuesToFacet(facets, TAGS, request.getTags());
addMandatoryValuesToFacet(facets, TYPES, RuleType.names());
- List<String> actionPlans = Lists.newArrayList("");
- List<String> actionPlansFromRequest = request.getActionPlans();
- if (actionPlansFromRequest != null) {
- actionPlans.addAll(actionPlansFromRequest);
- }
- addMandatoryValuesToFacet(facets, DEPRECATED_ACTION_PLANS, actionPlans);
addMandatoryValuesToFacet(facets, COMPONENT_UUIDS, request.getComponentUuids());
for (String facetName : request.getFacets()) {
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.Date;
import java.util.Map;
import org.assertj.core.api.Fail;
map.put("components", componentKeys);
map.put("componentUuids", newArrayList("ABCD"));
- try {
- underTest.createFromMap(map);
- fail();
- } catch (Exception e) {
- assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("components and componentUuids cannot be set simultaneously");
- }
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("At most one of the following parameters can be provided: componentKeys, componentUuids, components, componentRoots, componentUuids");
+
+ underTest.createFromMap(map);
}
@Test
map.put("componentRoots", newArrayList("org.apache"));
map.put("componentRootUuids", newArrayList("ABCD"));
- try {
- underTest.createFromMap(map);
- fail();
- } catch (Exception e) {
- assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("componentRoots and componentRootUuids cannot be set simultaneously");
- }
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("At most one of the following parameters can be provided: componentKeys, componentUuids, components, componentRoots, componentUuids");
+
+ underTest.createFromMap(map);
}
@Test
underTest.createFromRequest(new SearchWsRequest()
.setSinceLeakPeriod(true)
- .setComponentUuids(Collections.singletonList("component-uuid"))
- .setProjectUuids(Collections.singletonList("project-uuid")));
+ .setComponentUuids(newArrayList("component-uuid", "project-uuid")));
}
@Test
tester.get(IssueIndexer.class).indexAll();
wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
+ .setParam(IssueFilterParameters.COMPONENT_UUIDS, project.uuid())
.setParam(IssueFilterParameters.FILE_UUIDS, file.uuid())
.setParam(IssueFilterParameters.SINCE_LEAK_PERIOD, "true")
.execute()
return this;
}
- public NewAction setDescription(@Nullable String s) {
- this.description = s;
+ /**
+ * Used in Orchestrator
+ */
+ public NewAction setDescription(@Nullable String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * @since 5.6
+ */
+ public NewAction setDescription(@Nullable String description, Object... descriptionArgument) {
+ this.description = description == null ? null : String.format(description, descriptionArgument);
return this;
}
return this;
}
+ /**
+ * @since 5.3
+ */
public NewAction setDeprecatedSince(@Nullable String deprecatedSince) {
this.deprecatedSince = deprecatedSince;
return this;
this.key = key;
}
+ /**
+ * @since 5.3
+ */
public NewParam setSince(@Nullable String since) {
this.since = since;
return this;
}
+ /**
+ * @since 5.3
+ */
public NewParam setDeprecatedSince(@Nullable String deprecatedSince) {
this.deprecatedSince = deprecatedSince;
return this;
return this;
}
- public NewParam setDescription(@Nullable String s) {
- this.description = s;
+ public NewParam setDescription(@Nullable String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * @since 5.6
+ */
+ public NewParam setDescription(@Nullable String description, Object... descriptionArgument) {
+ this.description = description == null ? null : String.format(description, descriptionArgument);
return this;
}