Skip to content

Commit

Permalink
SONAR-8649 add organization parameter to api/projects/provisioned
Browse files Browse the repository at this point in the history
  • Loading branch information
sns-seb committed Jan 30, 2017
1 parent 2072b8c commit 7a1e3dd
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 96 deletions.
Expand Up @@ -34,29 +34,35 @@
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.user.UserSession;

import static com.google.common.collect.Sets.newHashSet;
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;

public class ProvisionedAction implements ProjectsWsAction {

private static final String PARAM_ORGANIZATION = "organization";
private static final Set<String> QUALIFIERS_FILTER = newHashSet(Qualifiers.PROJECT);
private static final Set<String> POSSIBLE_FIELDS = newHashSet("uuid", "key", "name", "creationDate");

private final DbClient dbClient;
private final UserSession userSession;
private final DefaultOrganizationProvider defaultOrganizationProvider;

public ProvisionedAction(DbClient dbClient, UserSession userSession) {
public ProvisionedAction(DbClient dbClient, UserSession userSession, DefaultOrganizationProvider defaultOrganizationProvider) {
this.dbClient = dbClient;
this.userSession = userSession;
this.defaultOrganizationProvider = defaultOrganizationProvider;
}

@Override
public void define(WebService.NewController controller) {
controller
.createAction("provisioned")
WebService.NewAction action = controller.createAction("provisioned");
action
.setDescription(
"Get the list of provisioned projects.<br /> " +
"Require 'Create Projects' permission.")
Expand All @@ -66,29 +72,46 @@ public void define(WebService.NewController controller) {
.addPagingParams(100, MAX_LIMIT)
.addSearchQuery("sonar", "names", "keys")
.addFieldsParam(POSSIBLE_FIELDS);

action.createParam(PARAM_ORGANIZATION)
.setDescription("The key of the organization")
.setRequired(false)
.setInternal(true)
.setSince("6.3");
}

@Override
public void handle(Request request, Response response) throws Exception {
userSession.checkPermission(GlobalPermissions.PROVISIONING);
userSession.checkLoggedIn();

SearchOptions options = new SearchOptions().setPage(
request.mandatoryParamAsInt(Param.PAGE),
request.mandatoryParamAsInt(Param.PAGE_SIZE)
);
request.mandatoryParamAsInt(Param.PAGE_SIZE));
Set<String> desiredFields = desiredFields(request);
String query = request.param(Param.TEXT_QUERY);

try (DbSession dbSession = dbClient.openSession(false)) {
OrganizationDto organization = getOrganization(dbSession, request);
userSession.checkOrganizationPermission(organization.getUuid(), GlobalPermissions.PROVISIONING);

RowBounds rowBounds = new RowBounds(options.getOffset(), options.getLimit());
List<ComponentDto> projects = dbClient.componentDao().selectProvisioned(dbSession, query, QUALIFIERS_FILTER, rowBounds);
int nbOfProjects = dbClient.componentDao().countProvisioned(dbSession, query, QUALIFIERS_FILTER);
List<ComponentDto> projects = dbClient.componentDao().selectProvisioned(dbSession, organization.getUuid(), query, QUALIFIERS_FILTER, rowBounds);
int nbOfProjects = dbClient.componentDao().countProvisioned(dbSession, organization.getUuid(), query, QUALIFIERS_FILTER);
JsonWriter json = response.newJsonWriter().beginObject();
writeProjects(projects, json, desiredFields);
options.writeJson(json, nbOfProjects);
json.endObject().close();
}
}

private OrganizationDto getOrganization(DbSession dbSession, Request request) {
String organizationKey = request.getParam(PARAM_ORGANIZATION)
.or(defaultOrganizationProvider.get()::getKey);
return checkFoundWithOptional(
dbClient.organizationDao().selectByKey(dbSession, organizationKey),
"No organization for key '%s'", organizationKey);
}

private static void writeProjects(List<ComponentDto> projects, JsonWriter json, Set<String> desiredFields) {
json.name("projects");
json.beginArray();
Expand Down
Expand Up @@ -24,83 +24,118 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.component.SnapshotTesting;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.WsTester;
import org.sonar.test.JsonAssert;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.test.JsonAssert.assertJson;

public class ProvisionedActionTest {

private static final String PARAM_ORGANIZATION = "organization";

@Rule
public DbTester db = DbTester.create(System2.INSTANCE);

@Rule
public UserSessionRule userSessionRule = UserSessionRule.standalone();

@Rule
public ExpectedException expectedException = ExpectedException.none();

private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private DbClient dbClient = db.getDbClient();
private ComponentDao componentDao = dbClient.componentDao();
private WsTester ws = new WsTester(new ProjectsWs(new ProvisionedAction(dbClient, userSessionRule)));
private WsActionTester underTest = new WsActionTester(new ProvisionedAction(dbClient, userSessionRule, defaultOrganizationProvider));

@Test
public void verify_definition() {
WebService.Action action = underTest.getDef();

assertThat(action.description()).isEqualTo("Get the list of provisioned projects.<br /> " +
"Require 'Create Projects' permission.");
assertThat(action.since()).isEqualTo("5.2");

assertThat(action.params()).hasSize(5);

Param organization = action.param(PARAM_ORGANIZATION);
assertThat(organization.description()).isEqualTo("The key of the organization");
assertThat(organization.isInternal()).isTrue();
assertThat(organization.isRequired()).isFalse();
assertThat(organization.since()).isEqualTo("6.3");
}

@Test
public void all_provisioned_projects_without_analyzed_projects() throws Exception {
userSessionRule.setGlobalPermissions(GlobalPermissions.PROVISIONING);
OrganizationDto organizationDto = db.organizations().insert();
ComponentDto analyzedProject = ComponentTesting.newProjectDto(organizationDto, "analyzed-uuid-1");
componentDao.insert(db.getSession(), newProvisionedProject(organizationDto, "1"), newProvisionedProject(organizationDto, "2"), analyzedProject);
SnapshotDto snapshot = SnapshotTesting.newAnalysis(analyzedProject);
dbClient.snapshotDao().insert(db.getSession(), snapshot);
db.getSession().commit();

WsTester.Result result = ws.newGetRequest("api/projects", "provisioned").execute();

result.assertJson(getClass(), "all-projects.json");
assertThat(result.outputAsString()).doesNotContain("analyzed-uuid-1");
db.components().insertComponents(newProvisionedProject(organizationDto, "1"), newProvisionedProject(organizationDto, "2"), analyzedProject);
db.components().insertSnapshot(SnapshotTesting.newAnalysis(analyzedProject));
userSessionRule.login().addOrganizationPermission(organizationDto, GlobalPermissions.PROVISIONING);

TestResponse result = underTest.newRequest()
.setParam(PARAM_ORGANIZATION, organizationDto.getKey())
.execute();

String json = result.getInput();
assertJson(json)
.isSimilarTo("{" +
" \"projects\":[" +
" {" +
" \"uuid\":\"provisioned-uuid-1\"," +
" \"key\":\"provisioned-key-1\"," +
" \"name\":\"provisioned-name-1\"" +
" }," +
" {" +
" \"uuid\":\"provisioned-uuid-2\"," +
" \"key\":\"provisioned-key-2\"," +
" \"name\":\"provisioned-name-2\"" +
" }" +
" ]" +
"}");
assertThat(json).doesNotContain("analyzed-uuid-1");
}

@Test
public void provisioned_projects_with_correct_pagination() throws Exception {
userSessionRule.setGlobalPermissions(GlobalPermissions.PROVISIONING);
OrganizationDto organizationDto = db.organizations().insert();
for (int i = 1; i <= 10; i++) {
componentDao.insert(db.getSession(), newProvisionedProject(organizationDto, String.valueOf(i)));
db.components().insertComponent(newProvisionedProject(organizationDto, String.valueOf(i)));
}
db.getSession().commit();
userSessionRule.login().addOrganizationPermission(organizationDto, GlobalPermissions.PROVISIONING);

WsTester.TestRequest request = ws.newGetRequest("api/projects", "provisioned")
TestRequest request = underTest.newRequest()
.setParam(PARAM_ORGANIZATION, organizationDto.getKey())
.setParam(Param.PAGE, "3")
.setParam(Param.PAGE_SIZE, "4");

String jsonOutput = request.execute().outputAsString();
String jsonOutput = request.execute().getInput();

assertThat(StringUtils.countMatches(jsonOutput, "provisioned-uuid-")).isEqualTo(2);
}

@Test
public void provisioned_projects_with_desired_fields() throws Exception {
userSessionRule.setGlobalPermissions(GlobalPermissions.PROVISIONING);
componentDao.insert(db.getSession(), newProvisionedProject(db.organizations().insert(), "1"));
db.getSession().commit();
OrganizationDto organization = db.organizations().insert();
db.components().insertComponent(newProvisionedProject(organization, "1"));
userSessionRule.login().addOrganizationPermission(organization, GlobalPermissions.PROVISIONING);

String jsonOutput = ws.newGetRequest("api/projects", "provisioned")
String jsonOutput = underTest.newRequest()
.setParam(PARAM_ORGANIZATION, organization.getKey())
.setParam(Param.FIELDS, "key")
.execute().outputAsString();
.execute().getInput();

assertThat(jsonOutput).contains("uuid", "key")
.doesNotContain("name")
Expand All @@ -109,14 +144,14 @@ public void provisioned_projects_with_desired_fields() throws Exception {

@Test
public void provisioned_projects_with_query() throws Exception {
userSessionRule.setGlobalPermissions(GlobalPermissions.PROVISIONING);
OrganizationDto organizationDto = db.organizations().insert();
componentDao.insert(db.getSession(), newProvisionedProject(organizationDto, "1"), newProvisionedProject(organizationDto, "2"));
db.getSession().commit();
OrganizationDto organization = db.organizations().insert();
db.components().insertComponents(newProvisionedProject(organization, "1"), newProvisionedProject(organization, "2"));
userSessionRule.login().addOrganizationPermission(organization, GlobalPermissions.PROVISIONING);

String jsonOutput = ws.newGetRequest("api/projects", "provisioned")
String jsonOutput = underTest.newRequest()
.setParam(PARAM_ORGANIZATION, organization.getKey())
.setParam(Param.TEXT_QUERY, "PROVISIONED-name-2")
.execute().outputAsString();
.execute().getInput();

assertThat(jsonOutput)
.contains("provisioned-name-2", "provisioned-uuid-2")
Expand All @@ -125,7 +160,6 @@ public void provisioned_projects_with_query() throws Exception {

@Test
public void provisioned_projects_as_defined_in_the_example() throws Exception {
userSessionRule.setGlobalPermissions(GlobalPermissions.PROVISIONING);
OrganizationDto organizationDto = db.organizations().insert();
ComponentDto hBaseProject = ComponentTesting.newProjectDto(organizationDto, "ce4c03d6-430f-40a9-b777-ad877c00aa4d")
.setKey("org.apache.hbas:hbase")
Expand All @@ -135,21 +169,26 @@ public void provisioned_projects_as_defined_in_the_example() throws Exception {
.setKey("com.microsoft.roslyn:roslyn")
.setName("Roslyn")
.setCreatedAt(DateUtils.parseDateTime("2013-03-04T23:03:44+0100"));
componentDao.insert(db.getSession(), hBaseProject, roslynProject);
db.getSession().commit();
db.components().insertComponents(hBaseProject, roslynProject);
userSessionRule.login().addOrganizationPermission(organizationDto.getUuid(), GlobalPermissions.PROVISIONING);

WsTester.Result result = ws.newGetRequest("api/projects", "provisioned").execute();
TestResponse result = underTest.newRequest()
.setParam(PARAM_ORGANIZATION, organizationDto.getKey())
.execute();

JsonAssert.assertJson(result.outputAsString()).isSimilarTo(Resources.getResource(getClass(), "projects-example-provisioned.json"));
assertJson(result.getInput())
.isSimilarTo(Resources.getResource(getClass(), "projects-example-provisioned.json"));
}

@Test
public void fail_when_not_enough_privileges() throws Exception {
OrganizationDto organizationDto = db.organizations().insert();
db.components().insertComponent(newProvisionedProject(organizationDto, "1"));
userSessionRule.login().addOrganizationPermission(organizationDto.getUuid(), GlobalPermissions.SCAN_EXECUTION);

expectedException.expect(ForbiddenException.class);
userSessionRule.setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
componentDao.insert(db.getSession(), newProvisionedProject(db.organizations().insert(), "1"));

ws.newGetRequest("api/projects", "provisioned").execute();
underTest.newRequest().execute();
}

private static ComponentDto newProvisionedProject(OrganizationDto organizationDto, String uuid) {
Expand Down
Expand Up @@ -28,6 +28,7 @@
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.user.ThreadLocalUserSession;
import org.sonar.server.user.UserSession;
Expand Down Expand Up @@ -202,6 +203,11 @@ public UserSessionRule addOrganizationPermission(String organizationUuid, String
return this;
}

public UserSessionRule addOrganizationPermission(OrganizationDto organizationDto, String permission) {
ensureAbstractMockUserSession().addOrganizationPermission(organizationDto.getUuid(), permission);
return this;
}

public UserSessionRule setUserId(@Nullable Integer userId) {
ensureMockUserSession().setUserId(userId);
return this;
Expand Down

This file was deleted.

12 changes: 6 additions & 6 deletions sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java
Expand Up @@ -212,27 +212,27 @@ public List<ComponentDto> selectAllRootsByOrganization(DbSession dbSession, Stri

/**
* Select a page of provisioned (root) components. Results are ordered by ascending name.
*
* @param dbSession
* @param organizationUuid uuid of the organization
* @param textQuery optional text query to match component name or key
* @param qualifiers filter on qualifiers. Must not be null nor empty
* @param rowBounds pagination
*/
public List<ComponentDto> selectProvisioned(DbSession dbSession, @Nullable String textQuery, Set<String> qualifiers, RowBounds rowBounds) {
public List<ComponentDto> selectProvisioned(DbSession dbSession, String organizationUuid, @Nullable String textQuery, Set<String> qualifiers, RowBounds rowBounds) {
checkArgument(!qualifiers.isEmpty(), "qualifiers must not be empty");
return mapper(dbSession).selectProvisioned(buildUpperLikeSql(textQuery), qualifiers, rowBounds);
return mapper(dbSession).selectProvisioned(organizationUuid, buildUpperLikeSql(textQuery), qualifiers, rowBounds);
}

/**
* Count number of provisioned (root) components.
*
* @param dbSession
* @param organizationUuid uuid of the organization
* @param textQuery optional text query to match component name or key
* @param qualifiers filter on qualifiers. Must not be null nor empty
*/
public int countProvisioned(DbSession dbSession, @Nullable String textQuery, Set<String> qualifiers) {
public int countProvisioned(DbSession dbSession, String organizationUuid, @Nullable String textQuery, Set<String> qualifiers) {
checkArgument(!qualifiers.isEmpty(), "qualifiers must not be empty");
return mapper(dbSession).countProvisioned(buildUpperLikeSql(textQuery), qualifiers);
return mapper(dbSession).countProvisioned(organizationUuid, buildUpperLikeSql(textQuery), qualifiers);
}

@CheckForNull
Expand Down
Expand Up @@ -117,9 +117,10 @@ List<ComponentDto> selectComponentsFromProjectKeyAndScope(@Param("projectKey") S
*/
List<String> selectProjectsFromView(@Param("viewUuidLikeQuery") String viewUuidLikeQuery, @Param("projectViewUuid") String projectViewUuid);

List<ComponentDto> selectProvisioned(@Nullable @Param("keyOrNameLike") String keyOrNameLike, @Param("qualifiers") Set<String> qualifiers, RowBounds rowBounds);
List<ComponentDto> selectProvisioned(@Param("organizationUuid") String organizationUuid, @Nullable @Param("keyOrNameLike") String keyOrNameLike,
@Param("qualifiers") Set<String> qualifiers, RowBounds rowBounds);

int countProvisioned(@Nullable @Param("keyOrNameLike") String keyOrNameLike, @Param("qualifiers") Set<String> qualifiers);
int countProvisioned(@Param("organizationUuid") String organizationUuid, @Nullable @Param("keyOrNameLike") String keyOrNameLike, @Param("qualifiers") Set<String> qualifiers);

List<ComponentDto> selectGhostProjects(Map<String, Object> parameters, RowBounds rowBounds);

Expand Down
Expand Up @@ -363,6 +363,7 @@
left join snapshots s on s.component_uuid=p.uuid
where
s.id is null
and p.organization_uuid = #{organizationUuid,jdbcType=VARCHAR}
and p.enabled = ${_true}
and p.qualifier in <foreach collection="qualifiers" open="(" close=")" item="qualifier" separator=",">#{qualifier}</foreach>
and p.copy_component_uuid is null
Expand Down

0 comments on commit 7a1e3dd

Please sign in to comment.