In api/qualityprofiles/search, the following actions are now availables : - delete : Available when not built-it, not default, and have either QP admin permission or have rights to edit - associateProjects : Available when not default and have either QP admin permission or have rights to edittags/7.0-RC1
@@ -96,11 +96,14 @@ public class SearchAction implements QProfileWsAction { | |||
.setSince("5.2") | |||
.setDescription("Search quality profiles") | |||
.setHandler(this) | |||
.setChangelog(new Change("6.5", format("The parameters '%s', '%s' and '%s' can be combined without any constraint", PARAM_DEFAULTS, PARAM_PROJECT, PARAM_LANGUAGE))) | |||
.setChangelog( | |||
new Change("6.5", format("The parameters '%s', '%s' and '%s' can be combined without any constraint", PARAM_DEFAULTS, PARAM_PROJECT, PARAM_LANGUAGE)), | |||
new Change("6.6", "Add available actions 'edit', 'copy' and 'setAsDefault' and global action 'create'"), | |||
new Change("7.0", "Add available actions 'delete' and 'associateProjects'") | |||
) | |||
.setResponseExample(getClass().getResource("search-example.json")); | |||
QProfileWsSupport.createOrganizationParam(action | |||
) | |||
QProfileWsSupport.createOrganizationParam(action) | |||
.setSince("6.4"); | |||
action | |||
@@ -165,8 +168,7 @@ public class SearchAction implements QProfileWsAction { | |||
dbClient.activeRuleDao().countActiveRulesByQuery(dbSession, builder.setProfiles(profiles).setRuleStatus(DEPRECATED).build())) | |||
.setProjectCountByProfileKey(dbClient.qualityProfileDao().countProjectsByOrganizationAndProfiles(dbSession, organization, profiles)) | |||
.setDefaultProfileKeys(defaultProfiles) | |||
.setEditableProfileKeys(editableProfiles) | |||
.setGlobalQProfileAdmin(userSession.hasPermission(ADMINISTER_QUALITY_PROFILES, organization)); | |||
.setEditableProfileKeys(editableProfiles); | |||
} | |||
} | |||
@@ -197,15 +199,14 @@ public class SearchAction implements QProfileWsAction { | |||
UserDto user = dbClient.userDao().selectActiveUserByLogin(dbSession, login); | |||
checkState(user != null, "User with login '%s' is not found'", login); | |||
return | |||
Stream.concat( | |||
dbClient.qProfileEditUsersDao().selectQProfileUuidsByOrganizationAndUser(dbSession, organization, user).stream(), | |||
dbClient.qProfileEditGroupsDao().selectQProfileUuidsByOrganizationAndGroups(dbSession, organization, userSession.getGroups()).stream()) | |||
.collect(toList()); | |||
return Stream.concat( | |||
dbClient.qProfileEditUsersDao().selectQProfileUuidsByOrganizationAndUser(dbSession, organization, user).stream(), | |||
dbClient.qProfileEditGroupsDao().selectQProfileUuidsByOrganizationAndGroups(dbSession, organization, userSession.getGroups()).stream()) | |||
.collect(toList()); | |||
} | |||
private List<QProfileDto> searchProfiles(DbSession dbSession, SearchRequest request, OrganizationDto organization, List<QProfileDto> defaultProfiles, | |||
@Nullable ComponentDto project) { | |||
@Nullable ComponentDto project) { | |||
Collection<QProfileDto> profiles = selectAllProfiles(dbSession, organization); | |||
return profiles.stream() | |||
@@ -256,10 +257,10 @@ public class SearchAction implements QProfileWsAction { | |||
private SearchWsResponse buildResponse(SearchData data) { | |||
List<QProfileDto> profiles = data.getProfiles(); | |||
Map<String, QProfileDto> profilesByKey = profiles.stream().collect(Collectors.toMap(QProfileDto::getKee, identity())); | |||
boolean isGlobalQProfileAdmin = userSession.hasPermission(ADMINISTER_QUALITY_PROFILES, data.getOrganization()); | |||
SearchWsResponse.Builder response = SearchWsResponse.newBuilder(); | |||
response.setActions(SearchWsResponse.Actions.newBuilder().setCreate(data.isGlobalQProfileAdmin())); | |||
response.setActions(SearchWsResponse.Actions.newBuilder().setCreate(isGlobalQProfileAdmin)); | |||
for (QProfileDto profile : profiles) { | |||
QualityProfile.Builder profileBuilder = response.addProfilesBuilder(); | |||
@@ -284,11 +285,12 @@ public class SearchAction implements QProfileWsAction { | |||
profileBuilder.setIsBuiltIn(profile.isBuiltIn()); | |||
profileBuilder.setActions(SearchWsResponse.QualityProfile.Actions.newBuilder() | |||
.setEdit(data.isEditable(profile)) | |||
.setSetAsDefault(data.isGlobalQProfileAdmin()) | |||
.setCopy(data.isGlobalQProfileAdmin())); | |||
.setEdit(!profile.isBuiltIn() && (isGlobalQProfileAdmin || data.isEditable(profile))) | |||
.setSetAsDefault(!isDefault && isGlobalQProfileAdmin) | |||
.setCopy(isGlobalQProfileAdmin) | |||
.setDelete(!isDefault && !profile.isBuiltIn() && (isGlobalQProfileAdmin || data.isEditable(profile))) | |||
.setAssociateProjects(!isDefault && (isGlobalQProfileAdmin || data.isEditable(profile)))); | |||
} | |||
return response.build(); | |||
} | |||
@@ -39,7 +39,6 @@ class SearchData { | |||
private Map<String, Long> projectCountByProfileKey; | |||
private Set<String> defaultProfileKeys; | |||
private Set<String> editableProfileKeys; | |||
private boolean isGlobalQProfileAdmin; | |||
SearchData setOrganization(OrganizationDto organization) { | |||
this.organization = organization; | |||
@@ -96,7 +95,7 @@ class SearchData { | |||
} | |||
boolean isEditable(QProfileDto profile) { | |||
return !profile.isBuiltIn() && (isGlobalQProfileAdmin || editableProfileKeys.contains(profile.getKee())); | |||
return editableProfileKeys.contains(profile.getKee()); | |||
} | |||
SearchData setEditableProfileKeys(List<String> editableProfileKeys) { | |||
@@ -104,12 +103,4 @@ class SearchData { | |||
return this; | |||
} | |||
boolean isGlobalQProfileAdmin() { | |||
return isGlobalQProfileAdmin; | |||
} | |||
SearchData setGlobalQProfileAdmin(boolean globalQProfileAdmin) { | |||
isGlobalQProfileAdmin = globalQProfileAdmin; | |||
return this; | |||
} | |||
} |
@@ -15,7 +15,9 @@ | |||
"actions": { | |||
"edit": false, | |||
"setAsDefault": false, | |||
"copy": false | |||
"copy": false, | |||
"delete": false, | |||
"associateProjects": false | |||
} | |||
}, | |||
{ | |||
@@ -37,7 +39,9 @@ | |||
"actions": { | |||
"edit": true, | |||
"setAsDefault": false, | |||
"copy": false | |||
"copy": false, | |||
"delete": true, | |||
"associateProjects": true | |||
} | |||
}, | |||
{ | |||
@@ -55,7 +59,9 @@ | |||
"actions": { | |||
"edit": true, | |||
"setAsDefault": false, | |||
"copy": false | |||
"copy": false, | |||
"delete": false, | |||
"associateProjects": false | |||
} | |||
}, | |||
{ | |||
@@ -72,7 +78,9 @@ | |||
"actions": { | |||
"edit": false, | |||
"setAsDefault": false, | |||
"copy": false | |||
"copy": false, | |||
"delete": false, | |||
"associateProjects": false | |||
} | |||
} | |||
], |
@@ -97,7 +97,10 @@ public class SearchActionTest { | |||
assertThat(definition.changelog()) | |||
.extracting(Change::getVersion, Change::getDescription) | |||
.containsExactlyInAnyOrder(tuple("6.5", "The parameters 'defaults', 'project' and 'language' can be combined without any constraint")); | |||
.containsExactlyInAnyOrder( | |||
tuple("6.5", "The parameters 'defaults', 'project' and 'language' can be combined without any constraint"), | |||
tuple("6.6", "Add available actions 'edit', 'copy' and 'setAsDefault' and global action 'create'"), | |||
tuple("7.0", "Add available actions 'delete' and 'associateProjects'")); | |||
WebService.Param organization = definition.param("organization"); | |||
assertThat(organization).isNotNull(); | |||
@@ -328,16 +331,21 @@ public class SearchActionTest { | |||
OrganizationDto organization = db.organizations().insert(); | |||
QProfileDto customProfile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO1.getKey())); | |||
QProfileDto builtInProfile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO1.getKey()).setIsBuiltIn(true)); | |||
QProfileDto defaultProfile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO1.getKey())); | |||
db.qualityProfiles().setAsDefault(defaultProfile); | |||
UserDto user = db.users().insertUser(); | |||
userSession.logIn(user).addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization); | |||
SearchWsResponse result = call(ws.newRequest() | |||
.setParam(PARAM_ORGANIZATION, organization.getKey())); | |||
assertThat(result.getProfilesList()).extracting(QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault()) | |||
assertThat(result.getProfilesList()) | |||
.extracting(QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault(), | |||
qp -> qp.getActions().getDelete(), qp -> qp.getActions().getAssociateProjects()) | |||
.containsExactlyInAnyOrder( | |||
tuple(customProfile.getKee(), true, true, true), | |||
tuple(builtInProfile.getKee(), false, true, true)); | |||
tuple(customProfile.getKee(), true, true, true, true, true), | |||
tuple(builtInProfile.getKee(), false, true, true, false, true), | |||
tuple(defaultProfile.getKee(), true, true, false, false, false)); | |||
assertThat(result.getActions().getCreate()).isTrue(); | |||
} | |||
@@ -357,12 +365,14 @@ public class SearchActionTest { | |||
SearchWsResponse result = call(ws.newRequest() | |||
.setParam(PARAM_ORGANIZATION, organization.getKey())); | |||
assertThat(result.getProfilesList()).extracting(QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault()) | |||
assertThat(result.getProfilesList()) | |||
.extracting(QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault(), | |||
qp -> qp.getActions().getDelete(), qp -> qp.getActions().getAssociateProjects()) | |||
.containsExactlyInAnyOrder( | |||
tuple(profile1.getKee(), true, false, false), | |||
tuple(profile2.getKee(), false, false, false), | |||
tuple(profile3.getKee(), true, false, false), | |||
tuple(builtInProfile.getKee(), false, false, false)); | |||
tuple(profile1.getKee(), true, false, false, true, true), | |||
tuple(profile2.getKee(), false, false, false, false, false), | |||
tuple(profile3.getKee(), true, false, false, true, true), | |||
tuple(builtInProfile.getKee(), false, false, false, false, false)); | |||
assertThat(result.getActions().getCreate()).isFalse(); | |||
} | |||
@@ -375,8 +385,10 @@ public class SearchActionTest { | |||
SearchWsResponse result = call(ws.newRequest() | |||
.setParam(PARAM_ORGANIZATION, organization.getKey())); | |||
assertThat(result.getProfilesList()).extracting(QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault()) | |||
.containsExactlyInAnyOrder(tuple(profile.getKee(), false, false, false)); | |||
assertThat(result.getProfilesList()) | |||
.extracting(QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault(), | |||
qp -> qp.getActions().getDelete(), qp -> qp.getActions().getAssociateProjects()) | |||
.containsExactlyInAnyOrder(tuple(profile.getKee(), false, false, false, false, false)); | |||
assertThat(result.getActions().getCreate()).isFalse(); | |||
} | |||
@@ -54,7 +54,9 @@ message SearchWsResponse { | |||
optional bool edit = 1; | |||
optional bool setAsDefault = 2; | |||
optional bool copy = 3; | |||
} | |||
optional bool associateProjects = 4; | |||
optional bool delete = 5; | |||
} | |||
} | |||
message Actions { |
@@ -24,8 +24,8 @@ import java.util.function.Predicate; | |||
import org.junit.ClassRule; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonarqube.tests.Category6Suite; | |||
import org.sonarqube.qa.util.Tester; | |||
import org.sonarqube.tests.Category6Suite; | |||
import org.sonarqube.ws.Common; | |||
import org.sonarqube.ws.Organizations.Organization; | |||
import org.sonarqube.ws.Qualityprofiles.CreateWsResponse; | |||
@@ -213,11 +213,12 @@ public class QualityProfilesEditTest { | |||
.qProfiles().service().search(new SearchRequest().setOrganizationKey(organization.getKey())); | |||
assertThat(result.getActions().getCreate()).isFalse(); | |||
assertThat(result.getProfilesList()) | |||
.extracting(SearchWsResponse.QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault()) | |||
.extracting(SearchWsResponse.QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault(), | |||
qp -> qp.getActions().getDelete(), qp -> qp.getActions().getAssociateProjects()) | |||
.contains( | |||
tuple(xooProfile1.getKey(), true, false, false), | |||
tuple(xooProfile2.getKey(), true, false, false), | |||
tuple(xooProfile3.getKey(), false, false, false)); | |||
tuple(xooProfile1.getKey(), true, false, false, true, true), | |||
tuple(xooProfile2.getKey(), true, false, false, true, true), | |||
tuple(xooProfile3.getKey(), false, false, false, false, false)); | |||
} | |||
@Test | |||
@@ -231,9 +232,10 @@ public class QualityProfilesEditTest { | |||
.qProfiles().service().search(new SearchRequest().setOrganizationKey(organization.getKey())); | |||
assertThat(result.getActions().getCreate()).isTrue(); | |||
assertThat(result.getProfilesList()) | |||
.extracting(SearchWsResponse.QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault()) | |||
.extracting(SearchWsResponse.QualityProfile::getKey, qp -> qp.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault(), | |||
qp -> qp.getActions().getDelete(), qp -> qp.getActions().getAssociateProjects()) | |||
.contains( | |||
tuple(xooProfile.getKey(), true, true, true)); | |||
tuple(xooProfile.getKey(), true, true, true, true, true)); | |||
} | |||
@Test |