Skip to content

Commit

Permalink
SONAR-9181 WS bulk_apply_template accepts the parameters as projects/…
Browse files Browse the repository at this point in the history
…search
  • Loading branch information
teryk authored and Stas Vilchik committed Sep 11, 2017
1 parent 0ec5f41 commit f8176cf
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 36 deletions.
Expand Up @@ -19,6 +19,8 @@
*/ */
package org.sonar.server.permission.ws.template; package org.sonar.server.permission.ws.template;


import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import org.sonar.api.i18n.I18n; import org.sonar.api.i18n.I18n;
import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Qualifiers;
Expand All @@ -35,20 +37,28 @@
import org.sonar.server.permission.PermissionTemplateService; import org.sonar.server.permission.PermissionTemplateService;
import org.sonar.server.permission.ws.PermissionWsSupport; import org.sonar.server.permission.ws.PermissionWsSupport;
import org.sonar.server.permission.ws.PermissionsWsAction; import org.sonar.server.permission.ws.PermissionsWsAction;
import org.sonar.server.project.Visibility;
import org.sonar.server.user.UserSession; import org.sonar.server.user.UserSession;
import org.sonarqube.ws.client.permission.BulkApplyTemplateWsRequest; import org.sonarqube.ws.client.permission.BulkApplyTemplateWsRequest;


import static org.sonar.api.utils.DateUtils.parseDateOrDateTime;
import static org.sonar.core.util.Protobuf.setNullable; import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobalAdmin; import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobalAdmin;
import static org.sonar.server.permission.ws.PermissionsWsParametersBuilder.createTemplateParameters; import static org.sonar.server.permission.ws.PermissionsWsParametersBuilder.createTemplateParameters;
import static org.sonar.server.permission.ws.template.WsTemplateRef.newTemplateRef; import static org.sonar.server.permission.ws.template.WsTemplateRef.newTemplateRef;
import static org.sonar.server.ws.WsParameterBuilder.createRootQualifiersParameter; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_002;
import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext; import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
import static org.sonar.server.ws.WsParameterBuilder.createRootQualifiersParameter;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_ORGANIZATION; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_ORGANIZATION;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_QUALIFIER; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_QUALIFIER;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_ID; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_ID;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_NAME; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_NAME;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ANALYZED_BEFORE;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ON_PROVISIONED_ONLY;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECTS;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS; import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_VISIBILITY;


public class BulkApplyTemplateAction implements PermissionsWsAction { public class BulkApplyTemplateAction implements PermissionsWsAction {


Expand Down Expand Up @@ -91,6 +101,32 @@ public void define(WebService.NewController context) {
.setDeprecatedKey(PARAM_QUALIFIER, "6.6"); .setDeprecatedKey(PARAM_QUALIFIER, "6.6");


createTemplateParameters(action); createTemplateParameters(action);

action
.createParam(PARAM_PROJECTS)
.setDescription("Comma-separated list of project keys")
.setSince("6.6")
.setExampleValue(String.join(",", KEY_PROJECT_EXAMPLE_001, KEY_PROJECT_EXAMPLE_002));

action.createParam(PARAM_VISIBILITY)
.setDescription("Filter the projects that should be visible to everyone (%s), or only specific user/groups (%s).<br/>" +
"If no visibility is specified, the default project visibility of the organization will be used.",
Visibility.PUBLIC.getLabel(), Visibility.PRIVATE.getLabel())
.setRequired(false)
.setInternal(true)
.setSince("6.6")
.setPossibleValues(Visibility.getLabels());

action.createParam(PARAM_ANALYZED_BEFORE)
.setDescription("Filter the projects for which last analysis is older than the given date (exclusive).<br> " +
"Format: date or datetime ISO formats.")
.setSince("6.6");

action.createParam(PARAM_ON_PROVISIONED_ONLY)
.setDescription("Filter the projects that are provisioned")
.setBooleanPossibleValues()
.setDefaultValue("false")
.setSince("6.6");
} }


@Override @Override
Expand Down Expand Up @@ -118,15 +154,29 @@ private static BulkApplyTemplateWsRequest toBulkApplyTemplateWsRequest(Request r
.setTemplateId(request.param(PARAM_TEMPLATE_ID)) .setTemplateId(request.param(PARAM_TEMPLATE_ID))
.setTemplateName(request.param(PARAM_TEMPLATE_NAME)) .setTemplateName(request.param(PARAM_TEMPLATE_NAME))
.setQualifiers(request.mandatoryParamAsStrings(PARAM_QUALIFIERS)) .setQualifiers(request.mandatoryParamAsStrings(PARAM_QUALIFIERS))
.setQuery(request.param(Param.TEXT_QUERY)); .setQuery(request.param(Param.TEXT_QUERY))
.setVisibility(request.param(PARAM_VISIBILITY))
.setOnProvisionedOnly(request.mandatoryParamAsBoolean(PARAM_ON_PROVISIONED_ONLY))
.setAnalyzedBefore(request.param(PARAM_ANALYZED_BEFORE))
.setProjects(request.paramAsStrings(PARAM_PROJECTS));
} }


private static ComponentQuery buildDbQuery(BulkApplyTemplateWsRequest request) { private static ComponentQuery buildDbQuery(BulkApplyTemplateWsRequest request) {
ComponentQuery.Builder dbQuery = ComponentQuery.builder() Collection<String> qualifiers = request.getQualifiers();
.setNameOrKeyQuery(request.getQuery()); ComponentQuery.Builder query = ComponentQuery.builder()
setNullable(request.getQualifiers(), l -> dbQuery.setQualifiers(l.toArray(new String[0]))); .setQualifiers(qualifiers.toArray(new String[qualifiers.size()]));


return dbQuery.build(); 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()));
setNullable(request.isOnProvisionedOnly(), query::setOnProvisionedOnly);
setNullable(request.getProjects(), keys -> query.setComponentKeys(new HashSet<>(keys)));

return query.build();
} }


} }
Expand Up @@ -41,12 +41,18 @@
import org.sonar.server.permission.ws.BasePermissionWsTest; import org.sonar.server.permission.ws.BasePermissionWsTest;


import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.api.utils.DateUtils.parseDate;
import static org.sonar.db.component.ComponentTesting.newApplication; import static org.sonar.db.component.ComponentTesting.newApplication;
import static org.sonar.db.component.ComponentTesting.newView; import static org.sonar.db.component.ComponentTesting.newView;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_ORGANIZATION; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_ORGANIZATION;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_ID; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_ID;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_NAME; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_NAME;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ANALYZED_BEFORE;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ON_PROVISIONED_ONLY;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECTS;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS; import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_VISIBILITY;


public class BulkApplyTemplateActionTest extends BasePermissionWsTest<BulkApplyTemplateAction> { public class BulkApplyTemplateActionTest extends BasePermissionWsTest<BulkApplyTemplateAction> {


Expand Down Expand Up @@ -103,17 +109,14 @@ public void bulk_apply_template_by_template_uuid() throws Exception {


ComponentDto privateProject = db.components().insertPrivateProject(organization); ComponentDto privateProject = db.components().insertPrivateProject(organization);
ComponentDto publicProject = db.components().insertPublicProject(organization); ComponentDto publicProject = db.components().insertPublicProject(organization);
ComponentDto view = db.components().insertView(organization);
loginAsAdmin(organization); loginAsAdmin(organization);


newRequest() newRequest()
.setParam(PARAM_TEMPLATE_ID, template1.getUuid()) .setParam(PARAM_TEMPLATE_ID, template1.getUuid())
.setParam(PARAM_QUALIFIERS, String.join(",", Qualifiers.PROJECT, Qualifiers.VIEW))
.execute(); .execute();


assertTemplate1AppliedToPrivateProject(privateProject); assertTemplate1AppliedToPrivateProject(privateProject);
assertTemplate1AppliedToPublicProject(publicProject); assertTemplate1AppliedToPublicProject(publicProject);
assertTemplate1AppliedToPublicProject(view);
} }


@Test @Test
Expand Down Expand Up @@ -171,14 +174,13 @@ public void apply_template_by_query_on_name_and_key_public_project() throws Exce
db.components().insertProjectAndSnapshot(publicProjectFoundByKey); db.components().insertProjectAndSnapshot(publicProjectFoundByKey);
ComponentDto publicProjectFoundByName = ComponentTesting.newPublicProjectDto(organization).setName("name-sonar-name"); ComponentDto publicProjectFoundByName = ComponentTesting.newPublicProjectDto(organization).setName("name-sonar-name");
db.components().insertProjectAndSnapshot(publicProjectFoundByName); db.components().insertProjectAndSnapshot(publicProjectFoundByName);
// match must be exact on key ComponentDto projectUntouched = ComponentTesting.newPublicProjectDto(organization).setDbKey("new-sona").setName("project-name");
ComponentDto projectUntouched = ComponentTesting.newPublicProjectDto(organization).setDbKey("new-sonar").setName("project-name");
db.components().insertProjectAndSnapshot(projectUntouched); db.components().insertProjectAndSnapshot(projectUntouched);
loginAsAdmin(organization); loginAsAdmin(organization);


newRequest() newRequest()
.setParam(PARAM_TEMPLATE_ID, template1.getUuid()) .setParam(PARAM_TEMPLATE_ID, template1.getUuid())
.setParam(Param.TEXT_QUERY, "sonar") .setParam(Param.TEXT_QUERY, "SONAR")
.execute(); .execute();


assertTemplate1AppliedToPublicProject(publicProjectFoundByKey); assertTemplate1AppliedToPublicProject(publicProjectFoundByKey);
Expand All @@ -188,25 +190,97 @@ public void apply_template_by_query_on_name_and_key_public_project() throws Exce


@Test @Test
public void apply_template_by_query_on_name_and_key() throws Exception { public void apply_template_by_query_on_name_and_key() throws Exception {
ComponentDto privateProjectFoundByKey = ComponentTesting.newPrivateProjectDto(organization).setDbKey("sonar"); // partial match on key
ComponentDto privateProjectFoundByKey = ComponentTesting.newPrivateProjectDto(organization).setDbKey("sonarqube");
db.components().insertProjectAndSnapshot(privateProjectFoundByKey); db.components().insertProjectAndSnapshot(privateProjectFoundByKey);
ComponentDto privateProjectFoundByName = ComponentTesting.newPrivateProjectDto(organization).setName("name-sonar-name"); ComponentDto privateProjectFoundByName = ComponentTesting.newPrivateProjectDto(organization).setName("name-sonar-name");
db.components().insertProjectAndSnapshot(privateProjectFoundByName); db.components().insertProjectAndSnapshot(privateProjectFoundByName);
// match must be exact on key ComponentDto projectUntouched = ComponentTesting.newPublicProjectDto(organization).setDbKey("new-sona").setName("project-name");
ComponentDto projectUntouched = ComponentTesting.newPublicProjectDto(organization).setDbKey("new-sonar").setName("project-name");
db.components().insertProjectAndSnapshot(projectUntouched); db.components().insertProjectAndSnapshot(projectUntouched);
loginAsAdmin(organization); loginAsAdmin(organization);


newRequest() newRequest()
.setParam(PARAM_TEMPLATE_ID, template1.getUuid()) .setParam(PARAM_TEMPLATE_ID, template1.getUuid())
.setParam(Param.TEXT_QUERY, "sonar") .setParam(Param.TEXT_QUERY, "SONAR")
.execute(); .execute();


assertTemplate1AppliedToPrivateProject(privateProjectFoundByKey); assertTemplate1AppliedToPrivateProject(privateProjectFoundByKey);
assertTemplate1AppliedToPrivateProject(privateProjectFoundByName); assertTemplate1AppliedToPrivateProject(privateProjectFoundByName);
assertNoPermissionOnProject(projectUntouched); assertNoPermissionOnProject(projectUntouched);
} }


@Test
public void apply_template_by_project_keys() throws Exception {
ComponentDto project1 = db.components().insertPrivateProject(organization);
ComponentDto project2 = db.components().insertPrivateProject(organization);
ComponentDto untouchedProject = db.components().insertPrivateProject(organization);
loginAsAdmin(organization);

newRequest()
.setParam(PARAM_TEMPLATE_ID, template1.getUuid())
.setParam(PARAM_PROJECTS, String.join(",", project1.getKey(), project2.getKey()))
.execute();

assertTemplate1AppliedToPrivateProject(project1);
assertTemplate1AppliedToPrivateProject(project2);
assertNoPermissionOnProject(untouchedProject);
}

@Test
public void apply_template_by_provisioned_only() throws Exception {
ComponentDto provisionedProject1 = db.components().insertPrivateProject(organization);
ComponentDto provisionedProject2 = db.components().insertPrivateProject(organization);
ComponentDto analyzedProject = db.components().insertPrivateProject(organization);
db.components().insertSnapshot(newAnalysis(analyzedProject));
loginAsAdmin(organization);

newRequest()
.setParam(PARAM_TEMPLATE_ID, template1.getUuid())
.setParam(PARAM_ON_PROVISIONED_ONLY, "true")
.execute();

assertTemplate1AppliedToPrivateProject(provisionedProject1);
assertTemplate1AppliedToPrivateProject(provisionedProject2);
assertNoPermissionOnProject(analyzedProject);
}

@Test
public void apply_template_by_analyzed_before() throws Exception {
ComponentDto oldProject1 = db.components().insertPrivateProject(organization);
ComponentDto oldProject2 = db.components().insertPrivateProject(organization);
ComponentDto recentProject = db.components().insertPrivateProject(organization);
db.components().insertSnapshot(oldProject1, a -> a.setCreatedAt(parseDate("2015-02-03").getTime()));
db.components().insertSnapshot(oldProject2, a -> a.setCreatedAt(parseDate("2016-12-11").getTime()));
db.components().insertSnapshot(recentProject, a -> a.setCreatedAt(System.currentTimeMillis()));
loginAsAdmin(organization);

newRequest()
.setParam(PARAM_TEMPLATE_ID, template1.getUuid())
.setParam(PARAM_ANALYZED_BEFORE, "2017-09-07")
.execute();

assertTemplate1AppliedToPrivateProject(oldProject1);
assertTemplate1AppliedToPrivateProject(oldProject2);
assertNoPermissionOnProject(recentProject);
}

@Test
public void apply_template_by_visibility() throws Exception {
ComponentDto privateProject1 = db.components().insertPrivateProject(organization);
ComponentDto privateProject2 = db.components().insertPrivateProject(organization);
ComponentDto publicProject = db.components().insertPublicProject(organization);
loginAsAdmin(organization);

newRequest()
.setParam(PARAM_TEMPLATE_ID, template1.getUuid())
.setParam(PARAM_VISIBILITY, "private")
.execute();

assertTemplate1AppliedToPrivateProject(privateProject1);
assertTemplate1AppliedToPrivateProject(privateProject2);
assertNoPermissionOnProject(publicProject);
}

@Test @Test
public void fail_if_no_template_parameter() throws Exception { public void fail_if_no_template_parameter() throws Exception {
loginAsAdmin(db.getDefaultOrganization()); loginAsAdmin(db.getDefaultOrganization());
Expand Down
Expand Up @@ -22,15 +22,21 @@
import java.util.Collection; import java.util.Collection;
import javax.annotation.CheckForNull; import javax.annotation.CheckForNull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.sonar.api.resources.Qualifiers;


import static java.util.Collections.singleton;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;


public class BulkApplyTemplateWsRequest { public class BulkApplyTemplateWsRequest {
private String templateId; private String templateId;
private String organization; private String organization;
private String templateName; private String templateName;
private String query; private String query;
private Collection<String> qualifiers; private Collection<String> qualifiers = singleton(Qualifiers.PROJECT);
private String visibility;
private String analyzedBefore;
private boolean onProvisionedOnly = false;
private Collection<String> projects;


@CheckForNull @CheckForNull
public String getTemplateId() { public String getTemplateId() {
Expand Down Expand Up @@ -72,7 +78,6 @@ public BulkApplyTemplateWsRequest setQuery(@Nullable String query) {
return this; return this;
} }


@CheckForNull
public Collection<String> getQualifiers() { public Collection<String> getQualifiers() {
return qualifiers; return qualifiers;
} }
Expand All @@ -81,4 +86,43 @@ public BulkApplyTemplateWsRequest setQualifiers(Collection<String> qualifiers) {
this.qualifiers = requireNonNull(qualifiers); this.qualifiers = requireNonNull(qualifiers);
return this; return this;
} }

@CheckForNull
public String getVisibility() {
return visibility;
}

public BulkApplyTemplateWsRequest setVisibility(@Nullable String visibility) {
this.visibility = visibility;
return this;
}

@CheckForNull
public String getAnalyzedBefore() {
return analyzedBefore;
}

public BulkApplyTemplateWsRequest setAnalyzedBefore(@Nullable String analyzedBefore) {
this.analyzedBefore = analyzedBefore;
return this;
}

public boolean isOnProvisionedOnly() {
return onProvisionedOnly;
}

public BulkApplyTemplateWsRequest setOnProvisionedOnly(boolean onProvisionedOnly) {
this.onProvisionedOnly = onProvisionedOnly;
return this;
}

@CheckForNull
public Collection<String> getProjects() {
return projects;
}

public BulkApplyTemplateWsRequest setProjects(@Nullable Collection<String> projects) {
this.projects = projects;
return this;
}
} }
Expand Up @@ -30,6 +30,7 @@
import org.sonarqube.ws.client.GetRequest; import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.PostRequest; import org.sonarqube.ws.client.PostRequest;
import org.sonarqube.ws.client.WsConnector; import org.sonarqube.ws.client.WsConnector;
import org.sonarqube.ws.client.project.ProjectsWsParameters;


import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_DESCRIPTION; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_DESCRIPTION;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID;
Expand All @@ -45,7 +46,6 @@
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_ID; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_ID;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_NAME; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_NAME;
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_USER_LOGIN; import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_USER_LOGIN;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS;


public class PermissionsService extends BaseService { public class PermissionsService extends BaseService {


Expand Down Expand Up @@ -124,7 +124,11 @@ public void bulkApplyTemplate(BulkApplyTemplateWsRequest request) {
.setParam(PARAM_TEMPLATE_ID, request.getTemplateId()) .setParam(PARAM_TEMPLATE_ID, request.getTemplateId())
.setParam(PARAM_TEMPLATE_NAME, request.getTemplateName()) .setParam(PARAM_TEMPLATE_NAME, request.getTemplateName())
.setParam("q", request.getQuery()) .setParam("q", request.getQuery())
.setParam(PARAM_QUALIFIERS, inlineMultipleParamValue(request.getQualifiers()))); .setParam(ProjectsWsParameters.PARAM_QUALIFIERS, inlineMultipleParamValue(request.getQualifiers()))
.setParam(ProjectsWsParameters.PARAM_VISIBILITY, request.getVisibility())
.setParam(ProjectsWsParameters.PARAM_ANALYZED_BEFORE, request.getAnalyzedBefore())
.setParam(ProjectsWsParameters.PARAM_ON_PROVISIONED_ONLY, request.isOnProvisionedOnly())
.setParam(ProjectsWsParameters.PARAM_PROJECTS, inlineMultipleParamValue(request.getProjects())));
} }


public CreateTemplateWsResponse createTemplate(CreateTemplateWsRequest request) { public CreateTemplateWsResponse createTemplate(CreateTemplateWsRequest request) {
Expand Down

0 comments on commit f8176cf

Please sign in to comment.