@@ -18,17 +18,17 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.permission.ws; | |||
package org.sonar.server.component; | |||
import com.google.common.base.Function; | |||
import javax.annotation.Nonnull; | |||
import org.sonar.api.resources.ResourceType; | |||
public class ResourceTypeToQualifier { | |||
public class ResourceTypeFunctions { | |||
public static final Function<ResourceType, String> RESOURCE_TYPE_TO_QUALIFIER = Singleton.INSTANCE; | |||
public static final Function<ResourceType, String> RESOURCE_TYPE_TO_QUALIFIER = ResourceTypeToQualifier.INSTANCE; | |||
private enum Singleton implements Function<ResourceType, String> { | |||
private enum ResourceTypeToQualifier implements Function<ResourceType, String> { | |||
INSTANCE; | |||
@Override |
@@ -0,0 +1,37 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.component.ws; | |||
import org.sonar.core.platform.Module; | |||
public class ComponentsWsModule extends Module { | |||
@Override | |||
protected void configureModule() { | |||
add( | |||
ResourcesWs.class, | |||
ComponentsWs.class, | |||
EventsWs.class, | |||
// actions | |||
AppAction.class, | |||
SearchAction.class, | |||
SearchViewComponentsAction.class); | |||
} | |||
} |
@@ -20,24 +20,164 @@ | |||
package org.sonar.server.component.ws; | |||
import com.google.common.base.Function; | |||
import java.util.List; | |||
import java.util.Set; | |||
import javax.annotation.Nonnull; | |||
import org.sonar.api.i18n.I18n; | |||
import org.sonar.api.resources.Qualifiers; | |||
import org.sonar.api.resources.ResourceTypes; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.api.server.ws.WebService.Param; | |||
import org.sonar.api.utils.Paging; | |||
import org.sonar.core.permission.GlobalPermissions; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.component.ComponentQuery; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonarqube.ws.WsComponents.WsSearchResponse; | |||
import static com.google.common.collect.FluentIterable.from; | |||
import static com.google.common.collect.Ordering.natural; | |||
import static java.lang.String.format; | |||
import static org.sonar.server.component.ResourceTypeFunctions.RESOURCE_TYPE_TO_QUALIFIER; | |||
import static org.sonar.server.component.ws.WsComponentsParameters.PARAM_QUALIFIERS; | |||
import static org.sonar.server.permission.ws.WsPermissionParameters.PARAM_QUALIFIER; | |||
import static org.sonar.server.ws.WsUtils.checkRequest; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
public class SearchAction implements ComponentsWsAction { | |||
private static final String QUALIFIER_PROPERTY_PREFIX = "qualifiers."; | |||
private final DbClient dbClient; | |||
private final ResourceTypes resourceTypes; | |||
private final I18n i18n; | |||
private final UserSession userSession; | |||
public SearchAction(DbClient dbClient, ResourceTypes resourceTypes, I18n i18n, UserSession userSession) { | |||
this.dbClient = dbClient; | |||
this.resourceTypes = resourceTypes; | |||
this.i18n = i18n; | |||
this.userSession = userSession; | |||
} | |||
@Override | |||
public void define(WebService.NewController context) { | |||
WebService.NewAction action = context.createAction("search") | |||
.setSince("5.2") | |||
.setInternal(true) | |||
.setDescription("Search for components") | |||
.addPagingParams(100) | |||
.addSearchQuery("sona", "project names") | |||
.setResponseExample(getClass().getResource("search-example.json")) | |||
.addSearchQuery("sona", "component names", "component keys") | |||
.setResponseExample(getClass().getResource("search-components-example.json")) | |||
.setHandler(this); | |||
action.createParam(PARAM_QUALIFIERS) | |||
.setRequired(true) | |||
.setExampleValue(format("%s,%s", Qualifiers.PROJECT, Qualifiers.MODULE)) | |||
.setDescription("Comma-separated list of component qualifiers. Possible values are " + buildQualifiersDescription()); | |||
} | |||
@Override | |||
public void handle(Request wsRequest, Response wsResponse) throws Exception { | |||
userSession.checkLoggedIn().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); | |||
List<String> qualifiers = wsRequest.mandatoryParamAsStrings(PARAM_QUALIFIERS); | |||
validateQualifiers(qualifiers); | |||
DbSession dbSession = dbClient.openSession(false); | |||
try { | |||
ComponentQuery query = buildQuery(wsRequest, qualifiers); | |||
Paging paging = buildPaging(dbSession, wsRequest, query); | |||
List<ComponentDto> components = searchComponents(dbSession, query, paging); | |||
WsSearchResponse response = buildResponse(components, paging); | |||
writeProtobuf(response, wsRequest, wsResponse); | |||
} finally { | |||
dbClient.closeSession(dbSession); | |||
} | |||
} | |||
private List<ComponentDto> searchComponents(DbSession dbSession, ComponentQuery query, Paging paging) { | |||
return dbClient.componentDao().selectByQuery( | |||
dbSession, | |||
query, | |||
paging.offset(), | |||
paging.pageSize()); | |||
} | |||
private WsSearchResponse buildResponse(List<ComponentDto> components, Paging paging) { | |||
WsSearchResponse.Builder responseBuilder = WsSearchResponse.newBuilder(); | |||
responseBuilder.getPagingBuilder() | |||
.setPageIndex(paging.pageIndex()) | |||
.setPageSize(paging.pageSize()) | |||
.setTotal(paging.total()) | |||
.build(); | |||
responseBuilder.addAllComponents( | |||
from(components) | |||
.transform(ComponentDToComponentResponseFunction.INSTANCE)); | |||
return responseBuilder.build(); | |||
} | |||
private Paging buildPaging(DbSession dbSession, Request wsRequest, ComponentQuery query) { | |||
int total = dbClient.componentDao().countByQuery(dbSession, query); | |||
return Paging.forPageIndex(wsRequest.mandatoryParamAsInt(Param.PAGE)) | |||
.withPageSize(wsRequest.mandatoryParamAsInt(Param.PAGE_SIZE)) | |||
.andTotal(total); | |||
} | |||
private ComponentQuery buildQuery(Request wsRequest, List<String> qualifiers) { | |||
return new ComponentQuery( | |||
dbClient.getDatabase(), | |||
wsRequest.param(Param.TEXT_QUERY), | |||
qualifiers.toArray(new String[qualifiers.size()])); | |||
} | |||
private void validateQualifiers(List<String> qualifiers) { | |||
Set<String> possibleQualifiers = allQualifiers(); | |||
for (String qualifier : qualifiers) { | |||
checkRequest(possibleQualifiers.contains(qualifier), | |||
format("The '%s' parameter must be one of %s. '%s' was passed.", PARAM_QUALIFIER, possibleQualifiers, qualifier)); | |||
} | |||
} | |||
private Set<String> allQualifiers() { | |||
return from(resourceTypes.getAll()) | |||
.transform(RESOURCE_TYPE_TO_QUALIFIER) | |||
.toSortedSet(natural()); | |||
} | |||
private String buildQualifiersDescription() { | |||
StringBuilder description = new StringBuilder(); | |||
description.append("<ul>"); | |||
String qualifierPattern = "<li>%s - %s</li>"; | |||
for (String qualifier : allQualifiers()) { | |||
description.append(format(qualifierPattern, qualifier, i18n(qualifier))); | |||
} | |||
description.append("</ul>"); | |||
return description.toString(); | |||
} | |||
private String i18n(String qualifier) { | |||
return i18n.message(userSession.locale(), QUALIFIER_PROPERTY_PREFIX + qualifier, ""); | |||
} | |||
private enum ComponentDToComponentResponseFunction implements Function<ComponentDto, WsSearchResponse.Component> { | |||
INSTANCE; | |||
@Override | |||
public WsSearchResponse.Component apply(@Nonnull ComponentDto dto) { | |||
return WsSearchResponse.Component.newBuilder() | |||
.setId(dto.uuid()) | |||
.setKey(dto.key()) | |||
.setName(dto.name()) | |||
.setQualifier(dto.qualifier()) | |||
.build(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.component.ws; | |||
class WsComponentsParameters { | |||
private WsComponentsParameters() { | |||
// static utility class | |||
} | |||
static final String PARAM_QUALIFIERS = "qualifiers"; | |||
} |
@@ -44,7 +44,7 @@ import static org.sonar.api.server.ws.WebService.Param.PAGE; | |||
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE; | |||
import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY; | |||
import static org.sonar.api.utils.Paging.forPageIndex; | |||
import static org.sonar.server.permission.ws.ResourceTypeToQualifier.RESOURCE_TYPE_TO_QUALIFIER; | |||
import static org.sonar.server.component.ResourceTypeFunctions.RESOURCE_TYPE_TO_QUALIFIER; | |||
import static org.sonar.server.permission.ws.SearchProjectPermissionsData.newBuilder; | |||
public class SearchProjectPermissionsDataLoader { |
@@ -32,7 +32,7 @@ import static com.google.common.collect.FluentIterable.from; | |||
import static com.google.common.collect.Ordering.natural; | |||
import static org.sonar.server.permission.DefaultPermissionTemplates.DEFAULT_TEMPLATE_PROPERTY; | |||
import static org.sonar.server.permission.DefaultPermissionTemplates.defaultRootQualifierTemplateProperty; | |||
import static org.sonar.server.permission.ws.ResourceTypeToQualifier.RESOURCE_TYPE_TO_QUALIFIER; | |||
import static org.sonar.server.component.ResourceTypeFunctions.RESOURCE_TYPE_TO_QUALIFIER; | |||
public class DefaultPermissionTemplateFinder { | |||
private final Settings settings; |
@@ -44,7 +44,7 @@ import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobal | |||
import static org.sonar.server.permission.ws.WsPermissionParameters.PARAM_QUALIFIER; | |||
import static org.sonar.server.permission.ws.WsPermissionParameters.createTemplateParameters; | |||
import static org.sonar.server.permission.ws.PermissionRequestValidator.validateQualifier; | |||
import static org.sonar.server.permission.ws.ResourceTypeToQualifier.RESOURCE_TYPE_TO_QUALIFIER; | |||
import static org.sonar.server.component.ResourceTypeFunctions.RESOURCE_TYPE_TO_QUALIFIER; | |||
public class SetDefaultTemplateAction implements PermissionsWsAction { | |||
private final DbClient dbClient; |
@@ -58,10 +58,7 @@ import org.sonar.server.component.ComponentFinder; | |||
import org.sonar.server.component.ComponentService; | |||
import org.sonar.server.component.DefaultComponentFinder; | |||
import org.sonar.server.component.DefaultRubyComponentService; | |||
import org.sonar.server.component.ws.ComponentsWs; | |||
import org.sonar.server.component.ws.EventsWs; | |||
import org.sonar.server.component.ws.ResourcesWs; | |||
import org.sonar.server.component.ws.SearchViewComponentsAction; | |||
import org.sonar.server.component.ws.ComponentsWsModule; | |||
import org.sonar.server.computation.CeModule; | |||
import org.sonar.server.computation.container.ReportProcessingModule; | |||
import org.sonar.server.computation.queue.CeQueueModule; | |||
@@ -338,16 +335,16 @@ public class PlatformLevel4 extends PlatformLevel { | |||
IndexDefinitions.class, | |||
IndexCreator.class, | |||
// Activity | |||
// Activity | |||
ActivityService.class, | |||
ActivityIndexDefinition.class, | |||
ActivityIndexer.class, | |||
ActivityIndex.class, | |||
// batch | |||
// batch | |||
BatchWsModule.class, | |||
// Dashboard | |||
// Dashboard | |||
DashboardsWs.class, | |||
org.sonar.server.dashboard.ws.ShowAction.class, | |||
ProjectDefaultDashboard.class, | |||
@@ -385,12 +382,12 @@ public class PlatformLevel4 extends PlatformLevel { | |||
ProjectIssueFilterWidget.class, | |||
IssueTagCloudWidget.class, | |||
// update center | |||
// update center | |||
UpdateCenterClient.class, | |||
UpdateCenterMatrixFactory.class, | |||
UpdateCenterWs.class, | |||
// quality profile | |||
// quality profile | |||
XMLProfileParser.class, | |||
XMLProfileSerializer.class, | |||
AnnotationProfileParser.class, | |||
@@ -434,7 +431,7 @@ public class PlatformLevel4 extends PlatformLevel { | |||
QProfileReset.class, | |||
RubyQProfileActivityService.class, | |||
// rule | |||
// rule | |||
AnnotationRuleParser.class, | |||
XMLRuleParser.class, | |||
DefaultRuleFinder.class, | |||
@@ -462,17 +459,17 @@ public class PlatformLevel4 extends PlatformLevel { | |||
RepositoriesAction.class, | |||
org.sonar.server.rule.ws.AppAction.class, | |||
// languages | |||
// languages | |||
Languages.class, | |||
LanguageWs.class, | |||
org.sonar.server.language.ws.ListAction.class, | |||
// activity | |||
// activity | |||
ActivitiesWs.class, | |||
org.sonar.server.activity.ws.SearchAction.class, | |||
ActivityMapping.class, | |||
// measure | |||
// measure | |||
MeasureFilterFactory.class, | |||
MeasureFilterExecutor.class, | |||
MeasureFilterEngine.class, | |||
@@ -484,14 +481,14 @@ public class PlatformLevel4 extends PlatformLevel { | |||
DefaultMetricFinder.class, | |||
TimeMachineWs.class, | |||
// quality gates | |||
// quality gates | |||
QualityGateDao.class, | |||
QualityGateConditionDao.class, | |||
QualityGates.class, | |||
ProjectQgateAssociationDao.class, | |||
QgateProjectFinder.class, | |||
org.sonar.server.qualitygate.ws.ListAction.class, | |||
org.sonar.server.qualitygate.ws.ListAction.class, | |||
org.sonar.server.qualitygate.ws.SearchAction.class, | |||
org.sonar.server.qualitygate.ws.ShowAction.class, | |||
org.sonar.server.qualitygate.ws.CreateAction.class, | |||
@@ -508,17 +505,17 @@ public class PlatformLevel4 extends PlatformLevel { | |||
org.sonar.server.qualitygate.ws.AppAction.class, | |||
QGatesWs.class, | |||
// web services | |||
// web services | |||
WebServiceEngine.class, | |||
ListingWs.class, | |||
// localization | |||
// localization | |||
L10nWs.class, | |||
// authentication | |||
// authentication | |||
AuthenticationWs.class, | |||
// users | |||
// users | |||
SecurityRealmFactory.class, | |||
DeprecatedUserFinder.class, | |||
NewUserNotifier.class, | |||
@@ -540,39 +537,35 @@ public class PlatformLevel4 extends PlatformLevel { | |||
UserIndex.class, | |||
UserUpdater.class, | |||
// groups | |||
// groups | |||
GroupMembershipService.class, | |||
GroupMembershipFinder.class, | |||
UserGroupsModule.class, | |||
// permissions | |||
// permissions | |||
PermissionRepository.class, | |||
PermissionService.class, | |||
PermissionUpdater.class, | |||
PermissionFinder.class, | |||
PermissionsWsModule.class, | |||
// components | |||
// components | |||
ProjectsWsModule.class, | |||
ComponentsWsModule.class, | |||
DefaultComponentFinder.class, | |||
DefaultRubyComponentService.class, | |||
ComponentService.class, | |||
ComponentFinder.class, | |||
ResourcesWs.class, | |||
ComponentsWs.class, | |||
org.sonar.server.component.ws.AppAction.class, | |||
SearchViewComponentsAction.class, | |||
EventsWs.class, | |||
NewAlerts.class, | |||
NewAlerts.newMetadata(), | |||
ComponentCleanerService.class, | |||
// views | |||
// views | |||
ViewIndexDefinition.class, | |||
ViewIndexer.class, | |||
ViewIndex.class, | |||
// issues | |||
// issues | |||
IssueIndexDefinition.class, | |||
IssueIndexer.class, | |||
IssueAuthorizationIndexer.class, | |||
@@ -606,13 +599,13 @@ public class PlatformLevel4 extends PlatformLevel { | |||
EmailNotificationChannel.class, | |||
AlertsEmailTemplate.class, | |||
IssueFilterWsModule.class, | |||
IssueFilterWsModule.class, | |||
// action plan | |||
// action plan | |||
ActionPlanWs.class, | |||
ActionPlanService.class, | |||
// issues actions | |||
// issues actions | |||
AssignAction.class, | |||
PlanAction.class, | |||
SetSeverityAction.class, | |||
@@ -621,7 +614,7 @@ public class PlatformLevel4 extends PlatformLevel { | |||
AddTagsAction.class, | |||
RemoveTagsAction.class, | |||
// technical debt | |||
// technical debt | |||
DebtModelService.class, | |||
DebtModelOperations.class, | |||
DebtModelLookup.class, | |||
@@ -631,7 +624,7 @@ public class PlatformLevel4 extends PlatformLevel { | |||
DebtRulesXMLImporter.class, | |||
DebtCharacteristicsXMLImporter.class, | |||
// source | |||
// source | |||
HtmlSourceDecorator.class, | |||
SourceService.class, | |||
SourcesWs.class, | |||
@@ -642,23 +635,23 @@ public class PlatformLevel4 extends PlatformLevel { | |||
IndexAction.class, | |||
ScmAction.class, | |||
// Duplications | |||
// Duplications | |||
DuplicationsParser.class, | |||
DuplicationsWs.class, | |||
DuplicationsJsonWriter.class, | |||
org.sonar.server.duplication.ws.ShowAction.class, | |||
// text | |||
// text | |||
MacroInterpreter.class, | |||
RubyTextService.class, | |||
// Notifications | |||
// Notifications | |||
EmailSettings.class, | |||
NotificationService.class, | |||
NotificationCenter.class, | |||
DefaultNotificationManager.class, | |||
// Tests | |||
// Tests | |||
CoverageService.class, | |||
TestsWs.class, | |||
CoveredFilesAction.class, | |||
@@ -667,12 +660,12 @@ public class PlatformLevel4 extends PlatformLevel { | |||
TestIndex.class, | |||
TestIndexer.class, | |||
// Properties | |||
// Properties | |||
PropertiesWs.class, | |||
TypeValidationModule.class, | |||
TypeValidationModule.class, | |||
// System | |||
// System | |||
RestartAction.class, | |||
InfoAction.class, | |||
UpgradesAction.class, | |||
@@ -687,7 +680,7 @@ public class PlatformLevel4 extends PlatformLevel { | |||
MigrateDbAction.class, | |||
DbMigrationStatusAction.class, | |||
// Plugins WS | |||
// Plugins WS | |||
PluginWSCommons.class, | |||
PluginUpdateAggregator.class, | |||
InstalledAction.class, | |||
@@ -700,18 +693,18 @@ public class PlatformLevel4 extends PlatformLevel { | |||
CancelAllAction.class, | |||
PluginsWs.class, | |||
// Compute engine | |||
// Compute engine | |||
CeModule.class, | |||
CeQueueModule.class, | |||
CeTaskProcessorModule.class, | |||
CeWsModule.class, | |||
ReportProcessingModule.class, | |||
// Views plugin | |||
// Views plugin | |||
ViewsBootstrap.class, | |||
ViewsStopper.class, | |||
// UI | |||
// UI | |||
GlobalNavigationAction.class, | |||
SettingsNavigationAction.class, | |||
ComponentNavigationAction.class, |
@@ -0,0 +1,33 @@ | |||
{ | |||
"paging": { | |||
"pageIndex": 1, | |||
"pageSize": 100, | |||
"total": 4 | |||
}, | |||
"components": [ | |||
{ | |||
"id": "directory-uuid", | |||
"key": "directory-key", | |||
"qualifier": "DIR", | |||
"name": "Directory Name" | |||
}, | |||
{ | |||
"id": "file-uuid", | |||
"key": "file-key", | |||
"qualifier": "FIL", | |||
"name": "File Name" | |||
}, | |||
{ | |||
"id": "module-uuid", | |||
"key": "module-key", | |||
"qualifier": "BRC", | |||
"name": "Module Name" | |||
}, | |||
{ | |||
"id": "project-uuid", | |||
"key": "project-key", | |||
"qualifier": "TRK", | |||
"name": "Project Name" | |||
} | |||
] | |||
} |
@@ -0,0 +1,35 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.component.ws; | |||
import org.junit.Test; | |||
import org.sonar.core.platform.ComponentContainer; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class ComponentsWsModuleTest { | |||
@Test | |||
public void verify_count_of_added_components() { | |||
ComponentContainer container = new ComponentContainer(); | |||
new ComponentsWsModule().configure(container); | |||
assertThat(container.size()).isEqualTo(8); | |||
} | |||
} |
@@ -24,6 +24,7 @@ import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.api.i18n.I18n; | |||
import org.sonar.api.resources.ResourceTypes; | |||
import org.sonar.api.server.ws.RailsHandler; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.api.utils.Durations; | |||
@@ -45,8 +46,9 @@ public class ComponentsWsTest { | |||
public void setUp() { | |||
WsTester tester = new WsTester(new ComponentsWs( | |||
new AppAction(mock(DbClient.class), mock(Durations.class), mock(I18n.class), userSessionRule, mock(ComponentFinder.class)), | |||
new SearchViewComponentsAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class))) | |||
); | |||
new SearchViewComponentsAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class)), | |||
new SearchAction(mock(org.sonar.db.DbClient.class), mock(ResourceTypes.class), mock(I18n.class), userSessionRule) | |||
)); | |||
controller = tester.controller("api/components"); | |||
} | |||
@@ -55,7 +57,7 @@ public class ComponentsWsTest { | |||
assertThat(controller).isNotNull(); | |||
assertThat(controller.description()).isNotEmpty(); | |||
assertThat(controller.since()).isEqualTo("4.2"); | |||
assertThat(controller.actions()).hasSize(3); | |||
assertThat(controller.actions()).hasSize(4); | |||
} | |||
@Test | |||
@@ -80,8 +82,9 @@ public class ComponentsWsTest { | |||
} | |||
@Test | |||
public void define_search_action() { | |||
public void define_search_view_components_action() { | |||
WebService.Action action = controller.action("search_view_components"); | |||
assertThat(action).isNotNull(); | |||
assertThat(action.isInternal()).isTrue(); | |||
assertThat(action.isPost()).isFalse(); | |||
@@ -89,4 +92,16 @@ public class ComponentsWsTest { | |||
assertThat(action.params()).hasSize(4); | |||
} | |||
@Test | |||
public void define_search_action() { | |||
WebService.Action action = controller.action("search"); | |||
assertThat(action).isNotNull(); | |||
assertThat(action.param("qualifiers").isRequired()).isTrue(); | |||
assertThat(action.responseExampleAsString()).isNotEmpty(); | |||
assertThat(action.description()).isNotEmpty(); | |||
assertThat(action.isInternal()).isTrue(); | |||
assertThat(action.isPost()).isFalse(); | |||
assertThat(action.since()).isEqualTo("5.2"); | |||
} | |||
} |
@@ -0,0 +1,191 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.component.ws; | |||
import com.google.common.base.Joiner; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.List; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.resources.Qualifiers; | |||
import org.sonar.api.resources.ResourceType; | |||
import org.sonar.api.resources.ResourceTypes; | |||
import org.sonar.api.server.ws.WebService.Param; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.core.permission.GlobalPermissions; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.component.ComponentDbTester; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.server.exceptions.BadRequestException; | |||
import org.sonar.server.exceptions.ForbiddenException; | |||
import org.sonar.server.exceptions.UnauthorizedException; | |||
import org.sonar.server.i18n.I18nRule; | |||
import org.sonar.server.plugins.MimeTypes; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import org.sonar.server.ws.TestRequest; | |||
import org.sonar.server.ws.WsActionTester; | |||
import org.sonarqube.ws.WsComponents.WsSearchResponse; | |||
import static java.util.Arrays.asList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.db.component.ComponentTesting.newDirectory; | |||
import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
import static org.sonar.db.component.ComponentTesting.newModuleDto; | |||
import static org.sonar.db.component.ComponentTesting.newProjectDto; | |||
import static org.sonar.db.component.ComponentTesting.newView; | |||
import static org.sonar.server.component.ws.WsComponentsParameters.PARAM_QUALIFIERS; | |||
import static org.sonar.test.JsonAssert.assertJson; | |||
public class SearchActionTest { | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@Rule | |||
public UserSessionRule userSession = UserSessionRule.standalone(); | |||
@Rule | |||
public DbTester db = DbTester.create(System2.INSTANCE); | |||
ComponentDbTester componentDb = new ComponentDbTester(db); | |||
I18nRule i18n = new I18nRule(); | |||
WsActionTester ws; | |||
ResourceTypes resourceTypes; | |||
@Before | |||
public void setUp() { | |||
userSession.login().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); | |||
resourceTypes = mock(ResourceTypes.class); | |||
when(resourceTypes.getAll()).thenReturn(resourceTypes()); | |||
ws = new WsActionTester(new SearchAction(db.getDbClient(), resourceTypes, i18n, userSession)); | |||
} | |||
@Test | |||
public void search_json_example() { | |||
componentDb.insertComponent(newView()); | |||
ComponentDto project = componentDb.insertComponent( | |||
newProjectDto("project-uuid") | |||
.setName("Project Name") | |||
.setKey("project-key") | |||
); | |||
ComponentDto module = componentDb.insertComponent( | |||
newModuleDto("module-uuid", project) | |||
.setName("Module Name") | |||
.setKey("module-key") | |||
); | |||
componentDb.insertComponent( | |||
newDirectory(module, "path/to/directoy") | |||
.setUuid("directory-uuid") | |||
.setKey("directory-key") | |||
.setName("Directory Name") | |||
); | |||
componentDb.insertComponent( | |||
newFileDto(module, "file-uuid") | |||
.setKey("file-key") | |||
.setName("File Name") | |||
); | |||
db.commit(); | |||
String response = newRequest(Qualifiers.PROJECT, Qualifiers.MODULE, Qualifiers.DIRECTORY, Qualifiers.FILE) | |||
.setMediaType(MimeTypes.JSON) | |||
.execute() | |||
.getInput(); | |||
assertJson(response).isSimilarTo(getClass().getResource("search-components-example.json")); | |||
} | |||
@Test | |||
public void search_with_pagination() throws IOException { | |||
for (int i = 1; i <= 9; i++) { | |||
componentDb.insertComponent( | |||
newProjectDto("project-uuid-" + i) | |||
.setName("Project Name " + i)); | |||
} | |||
db.commit(); | |||
InputStream responseStream = newRequest(Qualifiers.PROJECT) | |||
.setParam(Param.PAGE, "2") | |||
.setParam(Param.PAGE_SIZE, "3") | |||
.execute() | |||
.getInputStream(); | |||
WsSearchResponse response = WsSearchResponse.parseFrom(responseStream); | |||
assertThat(response.getComponentsCount()).isEqualTo(3); | |||
assertThat(response.getComponentsList()).extracting("id").containsExactly("project-uuid-4", "project-uuid-5", "project-uuid-6"); | |||
} | |||
@Test | |||
public void search_with_key_query() throws IOException { | |||
componentDb.insertComponent(newProjectDto().setKey("project-_%-key")); | |||
componentDb.insertComponent(newProjectDto().setKey("project-key-without-escaped-characters")); | |||
db.commit(); | |||
InputStream responseStream = newRequest(Qualifiers.PROJECT) | |||
.setParam(Param.TEXT_QUERY, "project-_%") | |||
.execute().getInputStream(); | |||
WsSearchResponse response = WsSearchResponse.parseFrom(responseStream); | |||
assertThat(response.getComponentsCount()).isEqualTo(1); | |||
assertThat(response.getComponentsList()).extracting("key").containsExactly("project-_%-key"); | |||
} | |||
@Test | |||
public void fail_if_unknown_qualifier_provided() { | |||
expectedException.expect(BadRequestException.class); | |||
expectedException.expectMessage("The 'qualifier' parameter must be one of [BRC, DIR, FIL, TRK]. 'Unknown-Qualifier' was passed."); | |||
newRequest("Unknown-Qualifier").execute(); | |||
} | |||
@Test | |||
public void fail_if_not_logged_in() { | |||
expectedException.expect(UnauthorizedException.class); | |||
userSession.anonymous(); | |||
newRequest(Qualifiers.PROJECT).execute(); | |||
} | |||
@Test | |||
public void fail_if_insufficient_privileges() { | |||
expectedException.expect(ForbiddenException.class); | |||
userSession.login().setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); | |||
newRequest(Qualifiers.PROJECT).execute(); | |||
} | |||
private TestRequest newRequest(String... qualifiers) { | |||
return ws.newRequest() | |||
.setMediaType(MimeTypes.PROTOBUF) | |||
.setParam(PARAM_QUALIFIERS, Joiner.on(",").join(qualifiers)); | |||
} | |||
private static List<ResourceType> resourceTypes() { | |||
return asList( | |||
ResourceType.builder(Qualifiers.PROJECT).build(), | |||
ResourceType.builder(Qualifiers.MODULE).build(), | |||
ResourceType.builder(Qualifiers.DIRECTORY).build(), | |||
ResourceType.builder(Qualifiers.FILE).build()); | |||
} | |||
} |
@@ -0,0 +1,40 @@ | |||
// SonarQube, open source software quality management tool. | |||
// Copyright (C) 2008-2015 SonarSource | |||
// mailto:contact AT sonarsource DOT com | |||
// | |||
// SonarQube is free software; you can redistribute it and/or | |||
// modify it under the terms of the GNU Lesser General Public | |||
// License as published by the Free Software Foundation; either | |||
// version 3 of the License, or (at your option) any later version. | |||
// | |||
// SonarQube is distributed in the hope that it will be useful, | |||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
// Lesser General Public License for more details. | |||
// | |||
// You should have received a copy of the GNU Lesser General Public License | |||
// along with this program; if not, write to the Free Software Foundation, | |||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
syntax = "proto2"; | |||
package sonarqube.ws.component; | |||
import "ws-commons.proto"; | |||
option java_package = "org.sonarqube.ws"; | |||
option java_outer_classname = "WsComponents"; | |||
option optimize_for = SPEED; | |||
// WS api/components/search | |||
message WsSearchResponse { | |||
message Component { | |||
optional string id = 1; | |||
optional string key = 2; | |||
optional string qualifier = 3; | |||
optional string name = 4; | |||
} | |||
optional sonarqube.ws.commons.Paging paging = 1; | |||
repeated Component components = 2; | |||
} |