public class ComponentQuery {
private final String nameOrKeyQuery;
+ private final boolean partialMatchOnKey;
private final String[] qualifiers;
private final String language;
private final Boolean isPrivate;
private final Set<Long> componentIds;
private final Long analyzedBefore;
- /**
- * Used by Dev Cockpit 1.9.
- * Could be removed when Developer Cockpit doesn't use it anymore.
- *
- * @deprecated since 5.4, use {@link Builder} instead
- */
- @Deprecated
- public ComponentQuery(@Nullable String nameOrKeyQuery, String... qualifiers) {
- this.nameOrKeyQuery = nameOrKeyQuery;
- this.qualifiers = Builder.validateQualifiers(qualifiers);
- this.language = null;
- this.componentIds = null;
- this.isPrivate = null;
- this.analyzedBefore = null;
- }
-
private ComponentQuery(Builder builder) {
this.nameOrKeyQuery = builder.nameOrKeyQuery;
+ this.partialMatchOnKey = builder.partialMatchOnKey == null ? false : builder.partialMatchOnKey;
this.qualifiers = builder.qualifiers;
this.language = builder.language;
this.componentIds = builder.componentIds;
return buildLikeValue(nameOrKeyQuery, WildcardPosition.BEFORE_AND_AFTER).toUpperCase(Locale.ENGLISH);
}
+ /**
+ * Used by MyBatis mapper
+ */
+ public boolean isPartialMatchOnKey() {
+ return partialMatchOnKey;
+ }
+
@CheckForNull
public String getLanguage() {
return language;
public static class Builder {
private String nameOrKeyQuery;
+ private Boolean partialMatchOnKey;
private String[] qualifiers;
private String language;
private Boolean isPrivate;
return this;
}
+ /**
+ * Beware, can be resource intensive! Should be used with precautions.
+ */
+ public Builder setPartialMatchOnKey(@Nullable Boolean partialMatchOnKey) {
+ this.partialMatchOnKey = partialMatchOnKey;
+ return this;
+ }
+
public Builder setQualifiers(String... qualifiers) {
this.qualifiers = qualifiers;
return this;
return this;
}
- protected static String[] validateQualifiers(@Nullable String... qualifiers) {
+ public ComponentQuery build() {
checkArgument(qualifiers != null && qualifiers.length > 0, "At least one qualifier must be provided");
- return qualifiers;
- }
+ checkArgument(nameOrKeyQuery != null || partialMatchOnKey == null, "A query must be provided if a partial match on key is specified.");
- public ComponentQuery build() {
- validateQualifiers(this.qualifiers);
return new ComponentQuery(this);
}
}
</if>
<if test="query.nameOrKeyQuery!=null">
and (
- p.kee = #{query.nameOrKeyQuery,jdbcType=VARCHAR}
- or
upper(p.name) like #{query.nameOrKeyUpperLikeQuery,jdbcType=VARCHAR} escape '/'
+ or
+ <choose>
+ <when test="query.isPartialMatchOnKey()">
+ upper(p.kee) like #{query.nameOrKeyUpperLikeQuery,jdbcType=VARCHAR} escape '/'
+ </when>
+ <otherwise>
+ p.kee = #{query.nameOrKeyQuery,jdbcType=VARCHAR}
+ </otherwise>
+ </choose>
)
</if>
<if test="query.private!=null">
ComponentDto provisionedProject = db.components().insertPrivateProject();
ComponentDto provisionedView = db.components().insertView(organization, (dto) -> {
});
- String projectUuid = db.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organization)).getComponentUuid();
- String disabledProjectUuid = db.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organization).setEnabled(false)).getComponentUuid();
+ String projectUuid = db.components().insertProjectAndSnapshot(newPrivateProjectDto(organization)).getComponentUuid();
+ String disabledProjectUuid = db.components().insertProjectAndSnapshot(newPrivateProjectDto(organization).setEnabled(false)).getComponentUuid();
String viewUuid = db.components().insertProjectAndSnapshot(ComponentTesting.newView(organization)).getComponentUuid();
assertThat(underTest.selectProjects(dbSession))
public void select_provisioned() {
OrganizationDto organization = db.organizations().insert();
ComponentDto provisionedProject = db.components()
- .insertComponent(ComponentTesting.newPrivateProjectDto(organization).setDbKey("provisioned.project").setName("Provisioned Project"));
+ .insertComponent(newPrivateProjectDto(organization).setDbKey("provisioned.project").setName("Provisioned Project"));
ComponentDto provisionedView = db.components().insertView(organization);
- String projectUuid = db.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organization)).getComponentUuid();
- String disabledProjectUuid = db.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organization).setEnabled(false)).getComponentUuid();
+ String projectUuid = db.components().insertProjectAndSnapshot(newPrivateProjectDto(organization)).getComponentUuid();
+ String disabledProjectUuid = db.components().insertProjectAndSnapshot(newPrivateProjectDto(organization).setEnabled(false)).getComponentUuid();
String viewUuid = db.components().insertProjectAndSnapshot(ComponentTesting.newView(organization)).getComponentUuid();
Set<String> projectQualifiers = newHashSet(Qualifiers.PROJECT);
public void count_provisioned() {
OrganizationDto organization = db.organizations().insert();
db.components().insertPrivateProject(organization);
- db.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organization));
+ db.components().insertProjectAndSnapshot(newPrivateProjectDto(organization));
db.components().insertProjectAndSnapshot(ComponentTesting.newView(organization));
assertThat(underTest.countProvisioned(dbSession, organization.getUuid(), null, newHashSet(Qualifiers.PROJECT))).isEqualTo(1);
@Test
public void selectByQuery_with_paging_query_and_qualifiers() {
OrganizationDto organizationDto = db.organizations().insert();
- db.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organizationDto).setName("aaaa-name"));
+ db.components().insertProjectAndSnapshot(newPrivateProjectDto(organizationDto).setName("aaaa-name"));
db.components().insertProjectAndSnapshot(newView(organizationDto));
for (int i = 9; i >= 1; i--) {
- db.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(organizationDto).setName("project-" + i));
+ db.components().insertProjectAndSnapshot(newPrivateProjectDto(organizationDto).setName("project-" + i));
}
ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("oJect").setQualifiers(Qualifiers.PROJECT).build();
@Test
public void selectByQuery_name_with_special_characters() {
- db.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setName("project-\\_%/-name"));
+ db.components().insertProjectAndSnapshot(newPrivateProjectDto(db.getDefaultOrganization()).setName("project-\\_%/-name"));
ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("-\\_%/-").setQualifiers(Qualifiers.PROJECT).build();
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
@Test
public void selectByQuery_key_with_special_characters() {
- db.components().insertProjectAndSnapshot(ComponentTesting.newPrivateProjectDto(db.organizations().insert()).setDbKey("project-_%-key"));
+ db.components().insertProjectAndSnapshot(newPrivateProjectDto(db.organizations().insert()).setDbKey("project-_%-key"));
+ db.components().insertProjectAndSnapshot(newPrivateProjectDto(db.organizations().insert()).setDbKey("project-key-that-does-not-match"));
ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("project-_%-key").setQualifiers(Qualifiers.PROJECT).build();
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
assertThat(result.get(0).getDbKey()).isEqualTo("project-_%-key");
}
+ @Test
+ public void selectByQuery_on_key_partial_match_case_insensitive() {
+ db.components().insertProjectAndSnapshot(newPrivateProjectDto(db.organizations().insert()).setDbKey("project-key"));
+
+ ComponentQuery query = ComponentQuery.builder()
+ .setNameOrKeyQuery("JECT-K")
+ .setPartialMatchOnKey(true)
+ .setQualifiers(Qualifiers.PROJECT).build();
+ List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
+
+ assertThat(result).hasSize(1);
+ assertThat(result.get(0).getDbKey()).isEqualTo("project-key");
+ }
+
@Test
public void selectByQuery_filter_on_language() {
- db.components().insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("java-project-key").setLanguage("java"));
- db.components().insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("cpp-project-key").setLanguage("cpp"));
+ db.components().insertComponent(newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("java-project-key").setLanguage("java"));
+ db.components().insertComponent(newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("cpp-project-key").setLanguage("cpp"));
ComponentQuery query = ComponentQuery.builder().setLanguage("java").setQualifiers(Qualifiers.PROJECT).build();
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
@Test
public void selectByQuery_filter_on_visibility() {
- db.components().insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("private-key"));
+ db.components().insertComponent(newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("private-key"));
db.components().insertComponent(ComponentTesting.newPublicProjectDto(db.getDefaultOrganization()).setDbKey("public-key"));
ComponentQuery privateProjectsQuery = ComponentQuery.builder().setPrivate(true).setQualifiers(Qualifiers.PROJECT).build();
@Test
public void selectByQuery_on_component_ids() {
OrganizationDto organizationDto = db.organizations().insert();
- ComponentDto sonarqube = db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto));
- ComponentDto jdk8 = db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto));
- ComponentDto cLang = db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto));
+ ComponentDto sonarqube = db.components().insertComponent(newPrivateProjectDto(organizationDto));
+ ComponentDto jdk8 = db.components().insertComponent(newPrivateProjectDto(organizationDto));
+ ComponentDto cLang = db.components().insertComponent(newPrivateProjectDto(organizationDto));
ComponentQuery query = ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT)
.setComponentIds(newHashSet(sonarqube.getId(), jdk8.getId())).build();
@Test
public void select_projects_by_name_query() {
OrganizationDto organizationDto = db.organizations().insert();
- ComponentDto project1 = db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto).setName("project1"));
+ ComponentDto project1 = db.components().insertComponent(newPrivateProjectDto(organizationDto).setName("project1"));
ComponentDto module1 = db.components().insertComponent(newModuleDto(project1).setName("module1"));
ComponentDto subModule1 = db.components().insertComponent(newModuleDto(module1).setName("subModule1"));
db.components().insertComponent(newFileDto(subModule1).setName("file"));
- ComponentDto project2 = db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto).setName("project2"));
- ComponentDto project3 = db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto).setName("project3"));
+ ComponentDto project2 = db.components().insertComponent(newPrivateProjectDto(organizationDto).setName("project2"));
+ ComponentDto project3 = db.components().insertComponent(newPrivateProjectDto(organizationDto).setName("project3"));
assertThat(underTest.selectProjectsByNameQuery(dbSession, null, false)).extracting(ComponentDto::uuid)
.containsOnly(project1.uuid(), project2.uuid(), project3.uuid());
OrganizationDto organizationDto = db.organizations().insert();
String[] uuids = {
- db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto).setProjectUuid(uuid1).setPrivate(true)).uuid(),
- db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto).setProjectUuid(uuid1).setPrivate(false)).uuid(),
- db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto).setProjectUuid(uuid2).setPrivate(true)).uuid(),
- db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto).setProjectUuid(uuid2).setPrivate(false)).uuid(),
- db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto).setRootUuid(uuid1).setProjectUuid("foo").setPrivate(false)).uuid(),
+ db.components().insertComponent(newPrivateProjectDto(organizationDto).setProjectUuid(uuid1).setPrivate(true)).uuid(),
+ db.components().insertComponent(newPrivateProjectDto(organizationDto).setProjectUuid(uuid1).setPrivate(false)).uuid(),
+ db.components().insertComponent(newPrivateProjectDto(organizationDto).setProjectUuid(uuid2).setPrivate(true)).uuid(),
+ db.components().insertComponent(newPrivateProjectDto(organizationDto).setProjectUuid(uuid2).setPrivate(false)).uuid(),
+ db.components().insertComponent(newPrivateProjectDto(organizationDto).setRootUuid(uuid1).setProjectUuid("foo").setPrivate(false)).uuid(),
};
underTest.setPrivateForRootComponentUuid(db.getSession(), uuid1, true);
assertThat(underTest.getQualifiers()).containsOnly(PROJECT);
}
+ @Test
+ public void test_getNameOrKeyUpperLikeQuery() throws Exception {
+ ComponentQuery underTest = ComponentQuery.builder()
+ .setNameOrKeyQuery("NAME/key")
+ .setQualifiers(PROJECT)
+ .build();
+
+ assertThat(underTest.getNameOrKeyUpperLikeQuery()).isEqualTo("%NAME//KEY%");
+ }
+
@Test
public void fail_if_no_qualifier_provided() {
expectedException.expect(IllegalArgumentException.class);
}
@Test
- public void test_getNameOrKeyUpperLikeQuery() throws Exception {
- ComponentQuery underTest = ComponentQuery.builder()
- .setNameOrKeyQuery("NAME/key")
- .setQualifiers(PROJECT)
- .build();
+ public void fail_if_partial_match_on_key_without_a_query() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("A query must be provided if a partial match on key is specified.");
- assertThat(underTest.getNameOrKeyUpperLikeQuery()).isEqualTo("%NAME//KEY%");
+ ComponentQuery.builder().setQualifiers(PROJECT).setPartialMatchOnKey(false).build();
}
}
action.createParam(Param.TEXT_QUERY)
.setDescription("Limit search to: <ul>" +
"<li>component names that contain the supplied string</li>" +
- "<li>component keys that are exactly the same as the supplied string</li>" +
+ "<li>component keys that contain the supplied string</li>" +
"</ul>")
.setExampleValue("sonar");
private static ComponentQuery buildQuery(SearchWsRequest request) {
List<String> qualifiers = request.getQualifiers();
ComponentQuery.Builder query = ComponentQuery.builder()
- .setNameOrKeyQuery(request.getQuery())
.setQualifiers(qualifiers.toArray(new String[qualifiers.size()]));
+ setNullable(request.getQuery(), q -> {
+ query.setNameOrKeyQuery(q);
+ query.setPartialMatchOnKey(true);
+ return query;
+ });
setNullable(request.getVisibility(), v -> query.setPrivate(Visibility.isPrivate(v)));
setNullable(request.getAnalyzedBefore(), d -> query.setAnalyzedBefore(parseDateOrDateTime(d).getTime()));
new SearchAction(db.getDbClient(), userSession, defaultOrganizationProvider, new ProjectsWsSupport(db.getDbClient(), mock(BillingValidationsProxy.class))));
@Test
- public void search_by_key_query() throws IOException {
+ public void search_by_key_query_with_partial_match_case_insensitive() throws IOException {
userSession.addPermission(ADMINISTER, db.getDefaultOrganization());
db.components().insertComponents(
ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("project-_%-key"),
+ ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("PROJECT-_%-KEY"),
ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("project-key-without-escaped-characters"));
- SearchWsResponse response = call(SearchWsRequest.builder().setQuery("project-_%-key").build());
+ SearchWsResponse response = call(SearchWsRequest.builder().setQuery("JeCt-_%-k").build());
- assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly("project-_%-key");
+ assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly("project-_%-key", "PROJECT-_%-KEY");
}
@Test
assertThat(qParam.description()).isEqualTo("Limit search to: " +
"<ul>" +
"<li>component names that contain the supplied string</li>" +
- "<li>component keys that are exactly the same as the supplied string</li>" +
+ "<li>component keys that contain the supplied string</li>" +
"</ul>");
WebService.Param qualifierParam = action.param("qualifiers");
*/
package org.sonar.api.server.ws;
-import com.google.common.base.Joiner;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
* </p>
*/
public NewParam createSearchQuery(String exampleValue, String... pluralFields) {
- String actionDescription = format("Limit search to %s that contain the supplied string.", Joiner.on(" or ").join(pluralFields));
+ String actionDescription = format("Limit search to %s that contain the supplied string.", String.join(" or ", pluralFields));
return createParam(Param.TEXT_QUERY)
.setDescription(actionDescription)
assertThat(result.getComponentsList()).extracting(SearchWsResponse.Component::getKey).containsExactlyInAnyOrder(oldProject.getKey());
}
+ @Test
+ public void search_on_key_partial_match_case_insensitive() {
+ Organizations.Organization organization = tester.organizations().generate();
+ CreateWsResponse.Project lowerCaseProject = tester.projects().generate(organization, p -> p.setKey("project-key"));
+ CreateWsResponse.Project upperCaseProject = tester.projects().generate(organization, p -> p.setKey("PROJECT-KEY"));
+ CreateWsResponse.Project anotherProject = tester.projects().generate(organization, p -> p.setKey("another-project"));
+
+ analyzeProject(lowerCaseProject.getKey(), organization.getKey());
+ analyzeProject(upperCaseProject.getKey(), organization.getKey());
+ analyzeProject(anotherProject.getKey(), organization.getKey());
+
+ SearchWsResponse result = tester.wsClient().projects().search(SearchWsRequest.builder()
+ .setOrganization(organization.getKey())
+ .setQualifiers(singletonList("TRK"))
+ .setQuery("JeCt-K")
+ .build());
+
+ assertThat(result.getComponentsList()).extracting(SearchWsResponse.Component::getKey)
+ .containsExactlyInAnyOrder(lowerCaseProject.getKey(), upperCaseProject.getKey())
+ .doesNotContain(anotherProject.getKey());
+ }
+
private void analyzeProject(String projectKey, Date analysisDate, String organizationKey) {
runProjectAnalysis(orchestrator, "shared/xoo-sample",
"sonar.organization", organizationKey,
"sonar.login", "admin",
"sonar.password", "admin");
}
+
+ private void analyzeProject(String projectKey, String organizationKey) {
+ analyzeProject(projectKey, new Date(), organizationKey);
+ }
}