@@ -137,7 +137,7 @@ public class ActionPlanTest { | |||
try { | |||
// Create a user having admin permission on the project | |||
adminClient.userClient().create(UserParameters.create().login(projectAdminUser).name(projectAdminUser).password("password").passwordConfirmation("password")); | |||
adminWsClient.permissionsClient().addUser( | |||
adminWsClient.permissions().addUser( | |||
new AddUserWsRequest() | |||
.setLogin(projectAdminUser) | |||
.setProjectKey(PROJECT_KEY) | |||
@@ -145,7 +145,7 @@ public class ActionPlanTest { | |||
// Create a user having browse permission on the project | |||
adminClient.userClient().create(UserParameters.create().login(projectUser).name(projectUser).password("password").passwordConfirmation("password")); | |||
adminWsClient.permissionsClient().addUser( | |||
adminWsClient.permissions().addUser( | |||
new AddUserWsRequest() | |||
.setLogin(projectUser) | |||
.setProjectKey(PROJECT_KEY) |
@@ -32,18 +32,20 @@ import org.junit.ClassRule; | |||
import org.junit.Ignore; | |||
import org.junit.Test; | |||
import org.sonarqube.ws.WsUserTokens; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.client.HttpConnector; | |||
import org.sonarqube.ws.client.HttpWsClient; | |||
import org.sonarqube.ws.client.PostRequest; | |||
import org.sonarqube.ws.client.WsClient; | |||
import org.sonarqube.ws.client.WsResponse; | |||
import org.sonarqube.ws.client.permission.AddGroupWsRequest; | |||
import org.sonarqube.ws.client.permission.AddUserWsRequest; | |||
import org.sonarqube.ws.client.permission.RemoveGroupWsRequest; | |||
import org.sonarqube.ws.client.usertoken.GenerateWsRequest; | |||
import org.sonarqube.ws.client.usertoken.UserTokensWsClient; | |||
import org.sonarqube.ws.client.usertoken.UserTokensService; | |||
import static java.lang.String.format; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonarqube.ws.client.HttpConnector.newHttpConnector; | |||
import static org.sonarqube.ws.client.WsRequest.newGetRequest; | |||
import static org.sonarqube.ws.client.WsRequest.newPostRequest; | |||
import static util.ItUtils.newAdminWsClient; | |||
import static util.ItUtils.projectDir; | |||
@@ -51,7 +53,7 @@ public class AuthenticationTest { | |||
@ClassRule | |||
public static Orchestrator ORCHESTRATOR = Category1Suite.ORCHESTRATOR; | |||
private static WsClient adminWsClient; | |||
private static UserTokensWsClient userTokensWsClient; | |||
private static UserTokensService userTokensWsClient; | |||
private static final String PROJECT_KEY = "sample"; | |||
private static final String LOGIN = "george.orwell"; | |||
@@ -64,7 +66,7 @@ public class AuthenticationTest { | |||
ORCHESTRATOR.getServer().associateProjectToQualityProfile("sample", "xoo", "one-issue-per-line"); | |||
adminWsClient = newAdminWsClient(ORCHESTRATOR); | |||
userTokensWsClient = adminWsClient.userTokensWsClient(); | |||
userTokensWsClient = adminWsClient.userTokens(); | |||
removeGroupPermission("anyone", "dryRunScan"); | |||
removeGroupPermission("anyone", "scan"); | |||
@@ -89,9 +91,9 @@ public class AuthenticationTest { | |||
createUser(login, name, password); | |||
// authenticate | |||
WsClient wsClient = new WsClient(newHttpConnector().url(ORCHESTRATOR.getServer().getUrl()).login(login).password(password).build()); | |||
String response = wsClient.execute(newGetRequest("api/authentication/validate")); | |||
assertThat(response).isEqualTo("{\"valid\":true}"); | |||
WsClient wsClient = new HttpWsClient(new HttpConnector.Builder().url(ORCHESTRATOR.getServer().getUrl()).credentials(login, password).build()); | |||
WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate")); | |||
assertThat(response.getContent()).isEqualTo("{\"valid\":true}"); | |||
} | |||
@Test | |||
@@ -99,14 +101,13 @@ public class AuthenticationTest { | |||
WsUserTokens.GenerateWsResponse generateWsResponse = userTokensWsClient.generate(new GenerateWsRequest() | |||
.setLogin(LOGIN) | |||
.setName("Validate token based authentication")); | |||
WsClient wsClient = new WsClient(newHttpConnector() | |||
WsClient wsClient = new HttpWsClient(new HttpConnector.Builder() | |||
.url(ORCHESTRATOR.getServer().getUrl()) | |||
.login(generateWsResponse.getToken()) | |||
.password("").build()); | |||
.token(generateWsResponse.getToken()).build()); | |||
String response = wsClient.execute(newGetRequest("api/authentication/validate")); | |||
WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate")); | |||
assertThat(response).isEqualTo("{\"valid\":true}"); | |||
assertThat(response.getContent()).isEqualTo("{\"valid\":true}"); | |||
} | |||
/** | |||
@@ -123,9 +124,9 @@ public class AuthenticationTest { | |||
createUser(login, format("name-%s", userId), password); | |||
// authenticate | |||
WsClient wsClient = new WsClient(newHttpConnector().url(ORCHESTRATOR.getServer().getUrl()).login(login).password(password).build()); | |||
String response = wsClient.execute(newGetRequest("api/authentication/validate")); | |||
assertThat(response).isEqualTo("{\"valid\":false}"); | |||
WsClient wsClient = new HttpWsClient(new HttpConnector.Builder().url(ORCHESTRATOR.getServer().getUrl()).credentials(login, password).build()); | |||
WsResponse response = wsClient.wsConnector().call(new GetRequest("api/authentication/validate")); | |||
assertThat(response.getContent()).isEqualTo("{\"valid\":false}"); | |||
} | |||
@Test | |||
@@ -162,41 +163,41 @@ public class AuthenticationTest { | |||
} | |||
private static void createUser(String login, String password) { | |||
adminWsClient.execute( | |||
newPostRequest("api/users/create") | |||
adminWsClient.wsConnector().call( | |||
new PostRequest("api/users/create") | |||
.setParam("login", login) | |||
.setParam("name", login) | |||
.setParam("password", password)); | |||
} | |||
private static void createUser(String login, String name, String password) { | |||
adminWsClient.execute( | |||
newPostRequest("api/users/create") | |||
adminWsClient.wsConnector().call( | |||
new PostRequest("api/users/create") | |||
.setParam("login", login) | |||
.setParam("name", name) | |||
.setParam("password", password)); | |||
} | |||
private static void addUserPermission(String login, String permission) { | |||
adminWsClient.permissionsClient().addUser(new AddUserWsRequest() | |||
adminWsClient.permissions().addUser(new AddUserWsRequest() | |||
.setLogin(login) | |||
.setPermission(permission)); | |||
} | |||
private static void deactivateUser(String login) { | |||
adminWsClient.execute( | |||
newPostRequest("api/users/deactivate") | |||
adminWsClient.wsConnector().call( | |||
new PostRequest("api/users/deactivate") | |||
.setParam("login", login)); | |||
} | |||
private static void removeGroupPermission(String groupName, String permission) { | |||
adminWsClient.permissionsClient().removeGroup(new RemoveGroupWsRequest() | |||
adminWsClient.permissions().removeGroup(new RemoveGroupWsRequest() | |||
.setGroupName(groupName) | |||
.setPermission(permission)); | |||
} | |||
private static void addGroupPermission(String groupName, String permission) { | |||
adminWsClient.permissionsClient().addGroup(new AddGroupWsRequest() | |||
adminWsClient.permissions().addGroup(new AddGroupWsRequest() | |||
.setGroupName(groupName) | |||
.setPermission(permission)); | |||
} |
@@ -229,7 +229,7 @@ public class IssuePermissionTest { | |||
} | |||
private void addUserPermission(String login, String projectKey, String permission) { | |||
adminWsClient.permissionsClient().addUser( | |||
adminWsClient.permissions().addUser( | |||
new AddUserWsRequest() | |||
.setLogin(login) | |||
.setProjectKey(projectKey) | |||
@@ -237,7 +237,7 @@ public class IssuePermissionTest { | |||
} | |||
private void removeGroupPermission(String groupName, String projectKey, String permission) { | |||
adminWsClient.permissionsClient().removeGroup(new RemoveGroupWsRequest() | |||
adminWsClient.permissions().removeGroup(new RemoveGroupWsRequest() | |||
.setGroupName(groupName) | |||
.setProjectKey(projectKey) | |||
.setPermission(permission)); |
@@ -30,6 +30,7 @@ import org.junit.ClassRule; | |||
import org.junit.Test; | |||
import org.sonarqube.ws.WsPermissions; | |||
import org.sonarqube.ws.WsPermissions.SearchTemplatesWsResponse; | |||
import org.sonarqube.ws.client.PostRequest; | |||
import org.sonarqube.ws.client.WsClient; | |||
import org.sonarqube.ws.client.permission.AddGroupToTemplateWsRequest; | |||
import org.sonarqube.ws.client.permission.AddGroupWsRequest; | |||
@@ -37,14 +38,13 @@ import org.sonarqube.ws.client.permission.AddUserToTemplateWsRequest; | |||
import org.sonarqube.ws.client.permission.AddUserWsRequest; | |||
import org.sonarqube.ws.client.permission.CreateTemplateWsRequest; | |||
import org.sonarqube.ws.client.permission.GroupsWsRequest; | |||
import org.sonarqube.ws.client.permission.PermissionsWsClient; | |||
import org.sonarqube.ws.client.permission.PermissionsService; | |||
import org.sonarqube.ws.client.permission.RemoveGroupFromTemplateWsRequest; | |||
import org.sonarqube.ws.client.permission.RemoveUserFromTemplateWsRequest; | |||
import org.sonarqube.ws.client.permission.SearchTemplatesWsRequest; | |||
import org.sonarqube.ws.client.permission.UsersWsRequest; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonarqube.ws.client.WsRequest.newPostRequest; | |||
import static util.ItUtils.newAdminWsClient; | |||
import static util.ItUtils.projectDir; | |||
@@ -52,7 +52,7 @@ public class PermissionTest { | |||
@ClassRule | |||
public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR; | |||
private static WsClient adminWsClient; | |||
private static PermissionsWsClient permissionsWsClient; | |||
private static PermissionsService permissionsWsClient; | |||
private static final String PROJECT_KEY = "sample"; | |||
private static final String LOGIN = "george.orwell"; | |||
@@ -70,7 +70,7 @@ public class PermissionTest { | |||
orchestrator.executeBuild(sampleProject); | |||
adminWsClient = newAdminWsClient(orchestrator); | |||
permissionsWsClient = adminWsClient.permissionsClient(); | |||
permissionsWsClient = adminWsClient.permissions(); | |||
createUser(LOGIN, "George Orwell"); | |||
createGroup(GROUP_NAME); | |||
@@ -157,28 +157,28 @@ public class PermissionTest { | |||
} | |||
private static void createUser(String login, String name) { | |||
adminWsClient.execute( | |||
newPostRequest("api/users/create") | |||
adminWsClient.wsConnector().call( | |||
new PostRequest("api/users/create") | |||
.setParam("login", login) | |||
.setParam("name", name) | |||
.setParam("password", "123456")); | |||
} | |||
private static void deactivateUser(String login) { | |||
adminWsClient.execute( | |||
newPostRequest("api/users/deactivate") | |||
adminWsClient.wsConnector().call( | |||
new PostRequest("api/users/deactivate") | |||
.setParam("login", login)); | |||
} | |||
private static void createGroup(String groupName) { | |||
adminWsClient.execute( | |||
newPostRequest("api/user_groups/create") | |||
adminWsClient.wsConnector().call( | |||
new PostRequest("api/user_groups/create") | |||
.setParam("name", groupName)); | |||
} | |||
private static void deleteGroup(String groupName) { | |||
adminWsClient.execute( | |||
newPostRequest("api/user_groups/delete") | |||
adminWsClient.wsConnector().call( | |||
new PostRequest("api/user_groups/delete") | |||
.setParam("name", groupName)); | |||
} | |||
} |
@@ -86,7 +86,7 @@ public class CommonRulesTest extends AbstractIssueTest { | |||
} | |||
private List<Issue> findIssues(String componentKey, String ruleKey) { | |||
return adminWsClient.issuesWsClient().search( | |||
return adminWsClient.issues().search( | |||
new SearchWsRequest() | |||
.setComponents(singletonList(componentKey)) | |||
.setRules(singletonList(ruleKey))) |
@@ -147,7 +147,7 @@ public class MeasureFiltersTest { | |||
client.userClient().create(userCreationParameters); | |||
if (permission != null) { | |||
adminWsClient.permissionsClient().addUser(new AddUserWsRequest() | |||
adminWsClient.permissions().addUser(new AddUserWsRequest() | |||
.setLogin(login) | |||
.setPermission(permission)); | |||
} |
@@ -1,8 +1,8 @@ | |||
package util;/* | |||
* Copyright (C) 2009-2014 SonarSource SA | |||
* All rights reserved | |||
* mailto:contact AT sonarsource DOT com | |||
*/ | |||
* Copyright (C) 2009-2014 SonarSource SA | |||
* All rights reserved | |||
* mailto:contact AT sonarsource DOT com | |||
*/ | |||
import com.google.common.base.Supplier; | |||
import com.google.common.base.Suppliers; | |||
@@ -32,13 +32,14 @@ import org.sonar.wsclient.issue.IssueClient; | |||
import org.sonar.wsclient.issue.IssueQuery; | |||
import org.sonar.wsclient.services.PropertyDeleteQuery; | |||
import org.sonar.wsclient.services.PropertyUpdateQuery; | |||
import org.sonarqube.ws.client.HttpConnector; | |||
import org.sonarqube.ws.client.HttpWsClient; | |||
import org.sonarqube.ws.client.WsClient; | |||
import static com.google.common.collect.FluentIterable.from; | |||
import static java.util.Arrays.asList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.fail; | |||
import static org.sonarqube.ws.client.HttpConnector.newHttpConnector; | |||
public class ItUtils { | |||
@@ -77,10 +78,9 @@ public class ItUtils { | |||
public static WsClient newAdminWsClient(Orchestrator orchestrator) { | |||
Server server = orchestrator.getServer(); | |||
return new WsClient(newHttpConnector() | |||
return new HttpWsClient(new HttpConnector.Builder() | |||
.url(server.getUrl()) | |||
.login(server.ADMIN_LOGIN) | |||
.password(server.ADMIN_PASSWORD) | |||
.credentials(server.ADMIN_LOGIN, server.ADMIN_PASSWORD) | |||
.build()); | |||
} | |||
@@ -64,6 +64,7 @@ | |||
<slf4j.version>1.7.12</slf4j.version> | |||
<tomcat.version>8.0.18</tomcat.version> | |||
<elasticsearch.version>1.7.2</elasticsearch.version> | |||
<okhttp.version>2.6.0</okhttp.version> | |||
<protobuf.version>3.0.0-beta-1</protobuf.version> | |||
<protobuf.compiler>${settings.localRepository}/com/google/protobuf/protoc/${protobuf.version}/protoc-${protobuf.version}-${os.detected.classifier}.exe</protobuf.compiler> | |||
@@ -1342,9 +1343,6 @@ | |||
<exists>src/main/protobuf</exists> | |||
</file> | |||
</activation> | |||
<properties> | |||
<sonar.exclusions>target/generated-sources/protobuf/**/*</sonar.exclusions> | |||
</properties> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
@@ -1352,7 +1350,7 @@ | |||
<artifactId>maven-antrun-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<id>protobuf-compile</id> | |||
<id>compile-protobuf-sources</id> | |||
<phase>generate-sources</phase> | |||
<goals> | |||
<goal>run</goal> | |||
@@ -1389,7 +1387,7 @@ | |||
<artifactId>build-helper-maven-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<id>protobuf-compile</id> | |||
<id>add-protobuf-generated-sources</id> | |||
<phase>generate-sources</phase> | |||
<goals> | |||
<goal>add-source</goal> | |||
@@ -1412,9 +1410,6 @@ | |||
<exists>src/test/protobuf</exists> | |||
</file> | |||
</activation> | |||
<properties> | |||
<sonar.test.exclusions>target/generated-test-sources/protobuf/**/*</sonar.test.exclusions> | |||
</properties> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
@@ -1422,7 +1417,7 @@ | |||
<artifactId>maven-antrun-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<id>protobuf-test-compile</id> | |||
<id>compile-protobuf-tests</id> | |||
<phase>generate-sources</phase> | |||
<goals> | |||
<goal>run</goal> | |||
@@ -1459,7 +1454,7 @@ | |||
<artifactId>build-helper-maven-plugin</artifactId> | |||
<executions> | |||
<execution> | |||
<id>protobuf-test-compile</id> | |||
<id>add-protobuf-generated-tests</id> | |||
<phase>generate-test-sources</phase> | |||
<goals> | |||
<goal>add-test-source</goal> |
@@ -24,5 +24,6 @@ public class Messages { | |||
// constants | |||
} | |||
static final String NO_PERMISSION = "You're not authorized to execute any SonarQube analysis. Please contact your SonarQube administrator."; | |||
public static final String NO_PERMISSION = "You're not authorized to execute any SonarQube analysis. Please contact your SonarQube administrator."; | |||
} |
@@ -22,7 +22,7 @@ package org.sonar.server.permission.ws; | |||
import org.sonar.api.server.ws.WebService; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.ENDPOINT; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER; | |||
public class PermissionsWs implements WebService { | |||
private final PermissionsWsAction[] actions; | |||
@@ -33,7 +33,7 @@ public class PermissionsWs implements WebService { | |||
@Override | |||
public void define(Context context) { | |||
NewController controller = context.createController(ENDPOINT); | |||
NewController controller = context.createController(CONTROLLER); | |||
controller.setDescription("Permissions management"); | |||
controller.setSince("3.7"); | |||
@@ -22,7 +22,7 @@ package org.sonar.server.usertoken.ws; | |||
import org.sonar.api.server.ws.WebService; | |||
import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.USER_TOKENS_ENDPOINT; | |||
import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.CONTROLLER; | |||
public class UserTokensWs implements WebService { | |||
private final UserTokensWsAction[] actions; | |||
@@ -33,7 +33,7 @@ public class UserTokensWs implements WebService { | |||
@Override | |||
public void define(Context context) { | |||
NewController controller = context.createController(USER_TOKENS_ENDPOINT) | |||
NewController controller = context.createController(CONTROLLER) | |||
.setDescription("User token management. To enhance security, tokens can be used to take the place " + | |||
"of user credentials in analysis configuration. A token can be revoked at any time.") | |||
.setSince("5.3"); |
@@ -452,7 +452,7 @@ public class ProjectDataLoaderMediumTest { | |||
loader.load(ProjectDataQuery.create().setModuleKey(project.key())); | |||
fail(); | |||
} catch (Exception e) { | |||
assertThat(e).isInstanceOf(ForbiddenException.class).hasMessage("You're not authorized to execute any SonarQube analysis. Please contact your SonarQube administrator."); | |||
assertThat(e).isInstanceOf(ForbiddenException.class).hasMessage(Messages.NO_PERMISSION); | |||
} | |||
} | |||
@@ -53,7 +53,7 @@ import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN; | |||
import static org.sonar.db.component.ComponentTesting.newProjectDto; | |||
import static org.sonar.db.component.ComponentTesting.newView; | |||
import static org.sonar.server.permission.ws.AddGroupAction.ACTION; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.ENDPOINT; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION; | |||
@@ -223,7 +223,7 @@ public class AddGroupActionTest { | |||
public void fail_when_get_request() throws Exception { | |||
expectedException.expect(ServerException.class); | |||
ws.newGetRequest(ENDPOINT, ACTION) | |||
ws.newGetRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_GROUP_NAME, "sonar-administrators") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
.execute(); | |||
@@ -264,7 +264,7 @@ public class AddGroupActionTest { | |||
} | |||
private WsTester.TestRequest newRequest() { | |||
return ws.newPostRequest(ENDPOINT, ACTION); | |||
return ws.newPostRequest(CONTROLLER, ACTION); | |||
} | |||
private void commit() { |
@@ -53,7 +53,7 @@ import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
import static org.sonar.db.component.ComponentTesting.newProjectDto; | |||
import static org.sonar.db.component.ComponentTesting.newView; | |||
import static org.sonar.server.permission.ws.AddUserAction.ACTION; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.ENDPOINT; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY; | |||
@@ -89,7 +89,7 @@ public class AddUserActionTest { | |||
@Test | |||
public void call_permission_service_with_right_data() throws Exception { | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
.execute(); | |||
@@ -105,7 +105,7 @@ public class AddUserActionTest { | |||
dbClient.componentDao().insert(dbSession, newProjectDto("project-uuid").setKey("project-key")); | |||
commit(); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_ID, "project-uuid") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
@@ -121,7 +121,7 @@ public class AddUserActionTest { | |||
dbClient.componentDao().insert(dbSession, newProjectDto("project-uuid").setKey("project-key")); | |||
commit(); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_KEY, "project-key") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
@@ -137,7 +137,7 @@ public class AddUserActionTest { | |||
dbClient.componentDao().insert(dbSession, newView("view-uuid").setKey("view-key")); | |||
commit(); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_ID, "view-uuid") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
@@ -152,7 +152,7 @@ public class AddUserActionTest { | |||
public void fail_when_project_uuid_is_unknown() throws Exception { | |||
expectedException.expect(NotFoundException.class); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_ID, "unknown-project-uuid") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
@@ -163,7 +163,7 @@ public class AddUserActionTest { | |||
public void fail_when_project_permission_without_project() throws Exception { | |||
expectedException.expect(BadRequestException.class); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN) | |||
.execute(); | |||
@@ -175,7 +175,7 @@ public class AddUserActionTest { | |||
insertComponent(newFileDto(newProjectDto("project-uuid"), "file-uuid")); | |||
commit(); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_ID, "file-uuid") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
@@ -186,7 +186,7 @@ public class AddUserActionTest { | |||
public void fail_when_get_request() throws Exception { | |||
expectedException.expect(ServerException.class); | |||
ws.newGetRequest(ENDPOINT, ACTION) | |||
ws.newGetRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "george.orwell") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
.execute(); | |||
@@ -196,7 +196,7 @@ public class AddUserActionTest { | |||
public void fail_when_user_login_is_missing() throws Exception { | |||
expectedException.expect(IllegalArgumentException.class); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
.execute(); | |||
} | |||
@@ -205,7 +205,7 @@ public class AddUserActionTest { | |||
public void fail_when_permission_is_missing() throws Exception { | |||
expectedException.expect(IllegalArgumentException.class); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "jrr.tolkien") | |||
.execute(); | |||
} | |||
@@ -217,7 +217,7 @@ public class AddUserActionTest { | |||
insertComponent(newProjectDto("project-uuid").setKey("project-key")); | |||
commit(); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_ID, "project-uuid") |
@@ -54,7 +54,7 @@ import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
import static org.sonar.db.component.ComponentTesting.newProjectDto; | |||
import static org.sonar.db.component.ComponentTesting.newView; | |||
import static org.sonar.server.permission.ws.RemoveGroupAction.ACTION; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.ENDPOINT; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_ID; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_GROUP_NAME; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION; | |||
@@ -205,7 +205,7 @@ public class RemoveGroupActionTest { | |||
public void fail_when_get_request() throws Exception { | |||
expectedException.expect(ServerException.class); | |||
ws.newGetRequest(ENDPOINT, ACTION) | |||
ws.newGetRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_GROUP_NAME, "sonar-administrators") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
.execute(); | |||
@@ -256,7 +256,7 @@ public class RemoveGroupActionTest { | |||
} | |||
private WsTester.TestRequest newRequest() { | |||
return ws.newPostRequest(ENDPOINT, ACTION); | |||
return ws.newPostRequest(CONTROLLER, ACTION); | |||
} | |||
private GroupDto insertGroup(String groupName) { |
@@ -53,7 +53,7 @@ import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
import static org.sonar.db.component.ComponentTesting.newProjectDto; | |||
import static org.sonar.db.component.ComponentTesting.newView; | |||
import static org.sonar.server.permission.ws.RemoveUserAction.ACTION; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.ENDPOINT; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.CONTROLLER; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PERMISSION; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_ID; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_PROJECT_KEY; | |||
@@ -86,7 +86,7 @@ public class RemoveUserActionTest { | |||
@Test | |||
public void call_permission_service_with_right_data() throws Exception { | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
.execute(); | |||
@@ -101,7 +101,7 @@ public class RemoveUserActionTest { | |||
public void remove_with_project_uuid() throws Exception { | |||
insertComponent(newProjectDto("project-uuid").setKey("project-key")); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_ID, "project-uuid") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
@@ -116,7 +116,7 @@ public class RemoveUserActionTest { | |||
public void remove_with_project_key() throws Exception { | |||
insertComponent(newProjectDto("project-uuid").setKey("project-key")); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_KEY, "project-key") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
@@ -131,7 +131,7 @@ public class RemoveUserActionTest { | |||
public void remove_with_view_uuid() throws Exception { | |||
insertComponent(newView("view-uuid").setKey("view-key")); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_ID, "view-uuid") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
@@ -146,7 +146,7 @@ public class RemoveUserActionTest { | |||
public void fail_when_project_does_not_exist() throws Exception { | |||
expectedException.expect(NotFoundException.class); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_ID, "unknown-project-uuid") | |||
.setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN) | |||
@@ -157,7 +157,7 @@ public class RemoveUserActionTest { | |||
public void fail_when_project_permission_without_permission() throws Exception { | |||
expectedException.expect(BadRequestException.class); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PERMISSION, UserRole.ISSUE_ADMIN) | |||
.execute(); | |||
@@ -168,7 +168,7 @@ public class RemoveUserActionTest { | |||
expectedException.expect(BadRequestException.class); | |||
insertComponent(newFileDto(newProjectDto(), "file-uuid")); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_ID, "file-uuid") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
@@ -179,7 +179,7 @@ public class RemoveUserActionTest { | |||
public void fail_when_get_request() throws Exception { | |||
expectedException.expect(ServerException.class); | |||
ws.newGetRequest(ENDPOINT, ACTION) | |||
ws.newGetRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "george.orwell") | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
.execute(); | |||
@@ -189,7 +189,7 @@ public class RemoveUserActionTest { | |||
public void fail_when_user_login_is_missing() throws Exception { | |||
expectedException.expect(IllegalArgumentException.class); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
.execute(); | |||
} | |||
@@ -198,7 +198,7 @@ public class RemoveUserActionTest { | |||
public void fail_when_permission_is_missing() throws Exception { | |||
expectedException.expect(IllegalArgumentException.class); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_USER_LOGIN, "jrr.tolkien") | |||
.execute(); | |||
} | |||
@@ -209,7 +209,7 @@ public class RemoveUserActionTest { | |||
expectedException.expectMessage("Project id or project key can be provided, not both."); | |||
insertComponent(newProjectDto("project-uuid").setKey("project-key")); | |||
ws.newPostRequest(ENDPOINT, ACTION) | |||
ws.newPostRequest(CONTROLLER, ACTION) | |||
.setParam(PARAM_PERMISSION, SYSTEM_ADMIN) | |||
.setParam(PARAM_USER_LOGIN, "ray.bradbury") | |||
.setParam(PARAM_PROJECT_ID, "project-uuid") |
@@ -1,5 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>org.sonarsource.sonarqube</groupId> | |||
@@ -9,12 +10,13 @@ | |||
<artifactId>sonar-batch-protocol</artifactId> | |||
<name>SonarQube :: Batch :: Protocol</name> | |||
<description>Classes used for communication between batch and server</description> | |||
<properties> | |||
<!-- Viewer is for our internal use. This is not production code and mostly generated with Eclipse GUI builder --> | |||
<sonar.exclusions>src/main/java/org/sonar/batch/protocol/viewer/**</sonar.exclusions> | |||
<sonar.exclusions>target/generated-sources/**/*,src/main/java/org/sonar/batch/protocol/viewer/**</sonar.exclusions> | |||
<sonar.test.exclusions>target/generated-test-sources/**/*</sonar.test.exclusions> | |||
</properties> | |||
<dependencies> |
@@ -100,10 +100,6 @@ | |||
<groupId>com.google.code.gson</groupId> | |||
<artifactId>gson</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.github.kevinsawicki</groupId> | |||
<artifactId>http-request</artifactId> | |||
</dependency> | |||
<!-- For HTML Report --> | |||
<dependency> | |||
<groupId>org.freemarker</groupId> | |||
@@ -118,7 +114,6 @@ | |||
<scope>test</scope> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.code.bean-matchers</groupId> | |||
<artifactId>bean-matchers</artifactId> |
@@ -21,19 +21,19 @@ package org.sonar.batch.analysis; | |||
import org.picocontainer.injectors.ProviderAdapter; | |||
import org.sonar.api.batch.AnalysisMode; | |||
import org.sonar.batch.bootstrap.ServerClient; | |||
import org.sonar.batch.cache.WSLoader; | |||
import org.sonar.batch.cache.WSLoader.LoadStrategy; | |||
import org.sonar.home.cache.PersistentCache; | |||
import org.sonarqube.ws.client.WsClient; | |||
public class AnalysisWSLoaderProvider extends ProviderAdapter { | |||
private WSLoader wsLoader; | |||
public WSLoader provide(AnalysisProperties props, AnalysisMode mode, PersistentCache cache, ServerClient client) { | |||
public WSLoader provide(AnalysisMode mode, PersistentCache cache, WsClient client) { | |||
if (wsLoader == null) { | |||
// recreate cache directory if needed for this analysis | |||
cache.reconfigure(); | |||
wsLoader = new WSLoader(getStrategy(mode), cache, client, props); | |||
wsLoader = new WSLoader(getStrategy(mode), cache, client); | |||
} | |||
return wsLoader; | |||
} |
@@ -23,10 +23,12 @@ import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.collect.Lists; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.commons.lang.CharUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.SonarPlugin; | |||
@@ -39,6 +41,11 @@ import org.sonar.core.platform.PluginInfo; | |||
import org.sonar.core.platform.RemotePlugin; | |||
import org.sonar.core.platform.RemotePluginFile; | |||
import org.sonar.home.cache.FileCache; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.client.WsClient; | |||
import org.sonarqube.ws.client.WsResponse; | |||
import static java.lang.String.format; | |||
/** | |||
* Downloads the plugins installed on server and stores them in a local user cache | |||
@@ -52,13 +59,13 @@ public class BatchPluginInstaller implements PluginInstaller { | |||
private final WSLoader wsLoader; | |||
private final FileCache fileCache; | |||
private final BatchPluginPredicate pluginPredicate; | |||
private final ServerClient serverClient; | |||
private final WsClient wsClient; | |||
public BatchPluginInstaller(WSLoader wsLoader, ServerClient serverClient, FileCache fileCache, BatchPluginPredicate pluginPredicate) { | |||
public BatchPluginInstaller(WSLoader wsLoader, WsClient wsClient, FileCache fileCache, BatchPluginPredicate pluginPredicate) { | |||
this.wsLoader = wsLoader; | |||
this.fileCache = fileCache; | |||
this.pluginPredicate = pluginPredicate; | |||
this.serverClient = serverClient; | |||
this.wsClient = wsClient; | |||
} | |||
@Override | |||
@@ -137,14 +144,17 @@ public class BatchPluginInstaller implements PluginInstaller { | |||
@Override | |||
public void download(String filename, File toFile) throws IOException { | |||
String url = "/deploy/plugins/" + key + "/" + filename; | |||
String url = format("/deploy/plugins/%s/%s", key, filename); | |||
if (LOG.isDebugEnabled()) { | |||
LOG.debug("Download {} to {}", url, toFile.getAbsolutePath()); | |||
LOG.debug("Download plugin {} to {}", filename, toFile); | |||
} else { | |||
LOG.info("Download {}", filename); | |||
} | |||
serverClient.download(url, toFile); | |||
WsResponse response = wsClient.wsConnector().call(new GetRequest(url)); | |||
try (InputStream stream = response.getContentStream()) { | |||
FileUtils.copyInputStreamToFile(stream, toFile); | |||
} | |||
} | |||
} | |||
} |
@@ -91,7 +91,7 @@ public class GlobalContainer extends ComponentContainer { | |||
CachesManager.class, | |||
GlobalSettings.class, | |||
ServerClient.class, | |||
new WsClientProvider(), | |||
DefaultServer.class, | |||
new GlobalTempFolderProvider(), | |||
DefaultHttpDownloader.class, |
@@ -1,168 +0,0 @@ | |||
/* | |||
* 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.batch.bootstrap; | |||
import com.google.common.base.Joiner; | |||
import com.google.common.base.Preconditions; | |||
import com.google.common.base.Strings; | |||
import com.google.gson.JsonArray; | |||
import com.google.gson.JsonElement; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.URI; | |||
import java.nio.file.Files; | |||
import java.nio.file.StandardCopyOption; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.lang.StringEscapeUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.batch.BatchSide; | |||
import org.sonar.api.utils.HttpDownloader; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.batch.bootstrapper.EnvironmentInformation; | |||
import org.sonar.core.util.DefaultHttpDownloader; | |||
/** | |||
* Replace the deprecated org.sonar.batch.ServerMetadata | |||
* TODO extends Server when removing the deprecated org.sonar.batch.ServerMetadata | |||
* | |||
* @since 3.4 | |||
*/ | |||
@BatchSide | |||
public class ServerClient { | |||
private static final String GET = "GET"; | |||
private GlobalProperties props; | |||
private DefaultHttpDownloader.BaseHttpDownloader downloader; | |||
public ServerClient(GlobalProperties settings, EnvironmentInformation env) { | |||
this.props = settings; | |||
this.downloader = new DefaultHttpDownloader.BaseHttpDownloader(settings.properties(), env.toString()); | |||
} | |||
public String getURL() { | |||
return StringUtils.removeEnd(StringUtils.defaultIfBlank(props.property("sonar.host.url"), "http://localhost:9000"), "/"); | |||
} | |||
public URI getURI(String pathStartingWithSlash) { | |||
Preconditions.checkArgument(pathStartingWithSlash.startsWith("/"), "Path must start with slash /: " + pathStartingWithSlash); | |||
String path = StringEscapeUtils.escapeHtml(pathStartingWithSlash); | |||
return URI.create(getURL() + path); | |||
} | |||
public void download(String pathStartingWithSlash, File toFile) { | |||
download(pathStartingWithSlash, toFile, null, null); | |||
} | |||
public void download(String pathStartingWithSlash, File toFile, @Nullable Integer connectTimeoutMillis, @Nullable Integer readTimeoutMillis) { | |||
try { | |||
InputStream is = load(pathStartingWithSlash, GET, false, connectTimeoutMillis, readTimeoutMillis); | |||
Files.copy(is, toFile.toPath(), StandardCopyOption.REPLACE_EXISTING); | |||
} catch (HttpDownloader.HttpException he) { | |||
throw handleHttpException(he.getUri().toString(), he.getResponseCode(), he.getResponseContent(), he); | |||
} catch (IOException e) { | |||
throw new IllegalStateException(String.format("Unable to download '%s' to: %s", pathStartingWithSlash, toFile), e); | |||
} | |||
} | |||
public String downloadString(String pathStartingWithSlash) { | |||
return downloadString(pathStartingWithSlash, GET, true, null); | |||
} | |||
public String downloadString(String pathStartingWithSlash, String requestMethod, boolean wrapHttpException, @Nullable Integer timeoutMillis) { | |||
InputStream is = load(pathStartingWithSlash, requestMethod, wrapHttpException, null, timeoutMillis); | |||
try { | |||
return new String(IOUtils.toByteArray(is), "UTF-8"); | |||
} catch (IOException e) { | |||
throw new IllegalStateException(String.format("Unable to request: %s", pathStartingWithSlash), e); | |||
} | |||
} | |||
/** | |||
* @throws IllegalStateException on I/O error, not limited to the network connection and if HTTP response code > 400 and wrapHttpException is true | |||
* @throws HttpDownloader.HttpException if HTTP response code > 400 and wrapHttpException is false | |||
*/ | |||
public InputStream load(String pathStartingWithSlash, String requestMethod, boolean wrapHttpException, @Nullable Integer connectTimeoutMs, | |||
@Nullable Integer readTimeoutMs) { | |||
URI uri = getURI(pathStartingWithSlash); | |||
try { | |||
if (Strings.isNullOrEmpty(getLogin())) { | |||
return downloader.newInputSupplier(uri, requestMethod, connectTimeoutMs, readTimeoutMs).getInput(); | |||
} else { | |||
return downloader.newInputSupplier(uri, requestMethod, getLogin(), getPassword(), connectTimeoutMs, readTimeoutMs).getInput(); | |||
} | |||
} catch (HttpDownloader.HttpException he) { | |||
if (wrapHttpException) { | |||
throw handleHttpException(he.getUri().toString(), he.getResponseCode(), he.getResponseContent(), he); | |||
} else { | |||
throw he; | |||
} | |||
} catch (IOException e) { | |||
throw new IllegalStateException(String.format("Unable to request: %s", uri), e); | |||
} | |||
} | |||
public RuntimeException handleHttpException(String url, int responseCode, String responseContent, Exception he) { | |||
if (responseCode == 401) { | |||
return MessageException.of(String.format(getMessageWhenNotAuthorized(), CoreProperties.LOGIN, CoreProperties.PASSWORD), he); | |||
} | |||
if (responseCode == 403) { | |||
// SONAR-4397 Details are in response content | |||
return MessageException.of(tryParseAsJsonError(responseContent), he); | |||
} | |||
return MessageException.of(String.format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, responseContent), he); | |||
} | |||
private static String tryParseAsJsonError(String responseContent) { | |||
try { | |||
JsonParser parser = new JsonParser(); | |||
JsonObject obj = parser.parse(responseContent).getAsJsonObject(); | |||
JsonArray errors = obj.getAsJsonArray("errors"); | |||
List<String> errorMessages = new ArrayList<>(); | |||
for (JsonElement e : errors) { | |||
errorMessages.add(e.getAsJsonObject().get("msg").getAsString()); | |||
} | |||
return Joiner.on(", ").join(errorMessages); | |||
} catch (Exception e) { | |||
return responseContent; | |||
} | |||
} | |||
public String getMessageWhenNotAuthorized() { | |||
if (Strings.isNullOrEmpty(getLogin()) && Strings.isNullOrEmpty(getPassword())) { | |||
return "Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties %s and %s."; | |||
} | |||
return "Not authorized. Please check the properties %s and %s."; | |||
} | |||
public String getLogin() { | |||
return props.property(CoreProperties.LOGIN); | |||
} | |||
public String getPassword() { | |||
return props.property(CoreProperties.PASSWORD); | |||
} | |||
} |
@@ -0,0 +1,90 @@ | |||
/* | |||
* 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.batch.bootstrap; | |||
import com.google.common.base.Joiner; | |||
import com.google.gson.JsonArray; | |||
import com.google.gson.JsonElement; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParser; | |||
import com.squareup.okhttp.Interceptor; | |||
import com.squareup.okhttp.Request; | |||
import com.squareup.okhttp.Response; | |||
import java.io.IOException; | |||
import java.net.HttpURLConnection; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.api.utils.log.Profiler; | |||
import static java.lang.String.format; | |||
public class WsClientLoggingInterceptor implements Interceptor { | |||
private static final Logger LOG = Loggers.get(WsClientLoggingInterceptor.class); | |||
@Override | |||
public Response intercept(Chain chain) throws IOException { | |||
Response response = logAndSendRequest(chain); | |||
if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) { | |||
if (StringUtils.isBlank(response.request().header("Authorization"))) { | |||
// not authenticated - see https://jira.sonarsource.com/browse/SONAR-4048 | |||
throw MessageException.of(format("Not authorized. Analyzing this project requires to be authenticated. " + | |||
"Please provide the values of the properties %s and %s.", CoreProperties.LOGIN, CoreProperties.PASSWORD)); | |||
} | |||
// credentials are not valid | |||
throw MessageException.of(format("Not authorized. Please check the properties %s and %s.", | |||
CoreProperties.LOGIN, CoreProperties.PASSWORD)); | |||
} | |||
if (response.code() == HttpURLConnection.HTTP_FORBIDDEN) { | |||
// SONAR-4397 Details are in response content | |||
throw MessageException.of(tryParseAsJsonError(response.body().string())); | |||
} | |||
return response; | |||
} | |||
private Response logAndSendRequest(Chain chain) throws IOException { | |||
Request request = chain.request(); | |||
Response response; | |||
Profiler profiler = Profiler.createIfDebug(LOG).startTrace(format("%s %s", request.method(), request.url())); | |||
response = chain.proceed(request); | |||
profiler.stopDebug(format("%s %d %s", request.method(), response.code(), request.url())); | |||
return response; | |||
} | |||
private static String tryParseAsJsonError(String responseContent) { | |||
try { | |||
JsonParser parser = new JsonParser(); | |||
JsonObject obj = parser.parse(responseContent).getAsJsonObject(); | |||
JsonArray errors = obj.getAsJsonArray("errors"); | |||
List<String> errorMessages = new ArrayList<>(); | |||
for (JsonElement e : errors) { | |||
errorMessages.add(e.getAsJsonObject().get("msg").getAsString()); | |||
} | |||
return Joiner.on(", ").join(errorMessages); | |||
} catch (Exception e) { | |||
return responseContent; | |||
} | |||
} | |||
} |
@@ -0,0 +1,63 @@ | |||
/* | |||
* 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.batch.bootstrap; | |||
import org.picocontainer.injectors.ProviderAdapter; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.batch.BatchSide; | |||
import org.sonar.batch.bootstrapper.EnvironmentInformation; | |||
import org.sonarqube.ws.client.HttpConnector; | |||
import org.sonarqube.ws.client.HttpWsClient; | |||
import org.sonarqube.ws.client.WsClient; | |||
import static java.lang.Integer.parseInt; | |||
import static java.lang.String.valueOf; | |||
import static org.apache.commons.lang.StringUtils.defaultIfBlank; | |||
@BatchSide | |||
public class WsClientProvider extends ProviderAdapter { | |||
static final int CONNECT_TIMEOUT_MS = 5_000; | |||
static final String READ_TIMEOUT_SEC_PROPERTY = "sonar.ws.timeout"; | |||
static final int DEFAULT_READ_TIMEOUT_SEC = 60; | |||
private HttpWsClient wsClient; | |||
public synchronized WsClient provide(final GlobalProperties settings, final EnvironmentInformation env) { | |||
if (wsClient == null) { | |||
String url = defaultIfBlank(settings.property("sonar.host.url"), CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE); | |||
HttpConnector.Builder builder = new HttpConnector.Builder(); | |||
// TODO proxy | |||
String timeoutSec = defaultIfBlank(settings.property(READ_TIMEOUT_SEC_PROPERTY), valueOf(DEFAULT_READ_TIMEOUT_SEC)); | |||
builder | |||
.readTimeoutMilliseconds(parseInt(timeoutSec) * 1_000) | |||
.connectTimeoutMilliseconds(CONNECT_TIMEOUT_MS) | |||
.userAgent(env.toString()) | |||
.url(url) | |||
.credentials(settings.property(CoreProperties.LOGIN), settings.property(CoreProperties.PASSWORD)) | |||
.interceptor(new WsClientLoggingInterceptor()); | |||
wsClient = new HttpWsClient(builder.build()); | |||
} | |||
return wsClient; | |||
} | |||
} |
@@ -20,10 +20,9 @@ | |||
package org.sonar.batch.cache; | |||
import org.picocontainer.injectors.ProviderAdapter; | |||
import org.sonar.batch.bootstrap.GlobalProperties; | |||
import org.sonar.batch.bootstrap.ServerClient; | |||
import org.sonar.batch.cache.WSLoader.LoadStrategy; | |||
import org.sonar.home.cache.PersistentCache; | |||
import org.sonarqube.ws.client.WsClient; | |||
public class StrategyWSLoaderProvider extends ProviderAdapter { | |||
private final LoadStrategy strategy; | |||
@@ -33,9 +32,9 @@ public class StrategyWSLoaderProvider extends ProviderAdapter { | |||
this.strategy = strategy; | |||
} | |||
public WSLoader provide(PersistentCache cache, ServerClient client, GlobalProperties globalProps) { | |||
public WSLoader provide(PersistentCache cache, WsClient client) { | |||
if (wsLoader == null) { | |||
wsLoader = new WSLoader(strategy, cache, client, globalProps); | |||
wsLoader = new WSLoader(strategy, cache, client); | |||
} | |||
return wsLoader; | |||
} |
@@ -21,56 +21,53 @@ package org.sonar.batch.cache; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.Reader; | |||
import java.nio.charset.StandardCharsets; | |||
import javax.annotation.Nonnull; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.io.IOUtils; | |||
import org.sonar.api.utils.HttpDownloader.HttpException; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.batch.bootstrap.ServerClient; | |||
import org.sonar.batch.bootstrap.UserProperties; | |||
import org.sonar.home.cache.PersistentCache; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.client.HttpException; | |||
import org.sonarqube.ws.client.WsClient; | |||
import static org.sonar.batch.cache.WSLoader.ServerStatus.ACCESSIBLE; | |||
import static org.sonar.batch.cache.WSLoader.ServerStatus.NOT_ACCESSIBLE; | |||
import static org.sonar.batch.cache.WSLoader.ServerStatus.UNKNOWN; | |||
public class WSLoader { | |||
static final String SONAR_WS_TIMEOUT_PROPS = "sonar.ws.timeout"; | |||
private static final Logger LOG = Loggers.get(WSLoader.class); | |||
private static final String FAIL_MSG = "Server is not accessible and data is not cached"; | |||
private static final int CONNECT_TIMEOUT = 5_000; | |||
private static final int DEFAULT_READ_TIMEOUT = 60_000; | |||
private static final String REQUEST_METHOD = "GET"; | |||
public enum ServerStatus { | |||
UNKNOWN, ACCESSIBLE, NOT_ACCESSIBLE; | |||
UNKNOWN, ACCESSIBLE, NOT_ACCESSIBLE | |||
} | |||
public enum LoadStrategy { | |||
SERVER_FIRST, CACHE_FIRST, SERVER_ONLY, CACHE_ONLY; | |||
SERVER_FIRST, CACHE_FIRST, SERVER_ONLY, CACHE_ONLY | |||
} | |||
private final LoadStrategy defautLoadStrategy; | |||
private final ServerClient client; | |||
private final WsClient client; | |||
private final PersistentCache cache; | |||
private final UserProperties userProperties; | |||
private ServerStatus serverStatus; | |||
private DataLoader<String> stringServerLoader = new DataLoader<String>() { | |||
@Override | |||
public String load(String id) throws IOException { | |||
InputStream is = client.load(id, REQUEST_METHOD, true, CONNECT_TIMEOUT, getReadTimeout()); | |||
String str = IOUtils.toString(is, StandardCharsets.UTF_8); | |||
try { | |||
cache.put(id, str.getBytes(StandardCharsets.UTF_8)); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Error saving to WS cache", e); | |||
GetRequest getRequest = new GetRequest(id); | |||
try (Reader reader = client.wsConnector().call(getRequest).getContentReader()) { | |||
String str = IOUtils.toString(reader); | |||
try { | |||
cache.put(id, str.getBytes(StandardCharsets.UTF_8)); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Error saving to WS cache", e); | |||
} | |||
return str; | |||
} | |||
return str; | |||
} | |||
}; | |||
private DataLoader<String> stringCacheLoader = new DataLoader<String>() { | |||
@@ -83,13 +80,14 @@ public class WSLoader { | |||
private DataLoader<InputStream> streamServerLoader = new DataLoader<InputStream>() { | |||
@Override | |||
public InputStream load(String id) throws IOException { | |||
InputStream is = client.load(id, REQUEST_METHOD, true, CONNECT_TIMEOUT, getReadTimeout()); | |||
try { | |||
cache.put(id, is); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Error saving to WS cache", e); | |||
GetRequest getRequest = new GetRequest(id); | |||
try (InputStream is = client.wsConnector().call(getRequest).getContentStream()) { | |||
try { | |||
cache.put(id, is); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Error saving to WS cache", e); | |||
} | |||
} | |||
is.close(); | |||
return cache.getStream(id); | |||
} | |||
}; | |||
@@ -101,7 +99,7 @@ public class WSLoader { | |||
} | |||
}; | |||
private class NotAvailableException extends Exception { | |||
private static class NotAvailableException extends Exception { | |||
private static final long serialVersionUID = 1L; | |||
public NotAvailableException(String message) { | |||
@@ -113,18 +111,13 @@ public class WSLoader { | |||
} | |||
} | |||
public WSLoader(LoadStrategy strategy, PersistentCache cache, ServerClient client, UserProperties settings) { | |||
public WSLoader(LoadStrategy strategy, PersistentCache cache, WsClient client) { | |||
this.defautLoadStrategy = strategy; | |||
this.userProperties = settings; | |||
this.serverStatus = UNKNOWN; | |||
this.cache = cache; | |||
this.client = client; | |||
} | |||
private int getReadTimeout() { | |||
return userProperties.properties().containsKey(SONAR_WS_TIMEOUT_PROPS) ? (Integer.parseInt(userProperties.property(SONAR_WS_TIMEOUT_PROPS)) * 1000) : DEFAULT_READ_TIMEOUT; | |||
} | |||
@Nonnull | |||
public WSLoaderResult<InputStream> loadStream(String id) { | |||
return load(id, defautLoadStrategy, streamServerLoader, streamCacheLoader); | |||
@@ -200,7 +193,7 @@ public class WSLoader { | |||
throw new IllegalStateException(FAIL_MSG, serverNotAvailable.getCause()); | |||
} | |||
} | |||
throw new IllegalStateException("Server is not available", serverNotAvailable.getCause()); | |||
throw new IllegalStateException("Server is not available: " + client.wsConnector().baseUrl(), serverNotAvailable.getCause()); | |||
} | |||
} | |||
@@ -19,71 +19,63 @@ | |||
*/ | |||
package org.sonar.batch.report; | |||
import com.github.kevinsawicki.http.HttpRequest; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.gson.Gson; | |||
import java.io.BufferedWriter; | |||
import com.google.common.io.Files; | |||
import com.squareup.okhttp.HttpUrl; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStreamWriter; | |||
import java.io.Writer; | |||
import java.net.MalformedURLException; | |||
import java.net.URISyntaxException; | |||
import java.net.URL; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.Date; | |||
import javax.annotation.CheckForNull; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.io.FileUtils; | |||
import org.picocontainer.Startable; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.batch.BatchSide; | |||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.api.platform.Server; | |||
import org.sonar.api.utils.TempFolder; | |||
import org.sonar.api.utils.ZipUtils; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.api.utils.text.JsonWriter; | |||
import org.sonar.batch.analysis.DefaultAnalysisMode; | |||
import org.sonar.batch.bootstrap.ServerClient; | |||
import org.sonar.batch.protocol.output.BatchReportWriter; | |||
import org.sonar.batch.scan.ImmutableProjectReactor; | |||
import org.sonar.batch.util.BatchUtils; | |||
import org.sonarqube.ws.WsCe; | |||
import org.sonarqube.ws.client.WsClient; | |||
import org.sonarqube.ws.client.ce.SubmitWsRequest; | |||
import static java.lang.String.format; | |||
import static org.apache.commons.lang.StringUtils.defaultIfBlank; | |||
@BatchSide | |||
public class ReportPublisher implements Startable { | |||
private static final Logger LOG = LoggerFactory.getLogger(ReportPublisher.class); | |||
private static final Logger LOG = Loggers.get(ReportPublisher.class); | |||
public static final String KEEP_REPORT_PROP_KEY = "sonar.batch.keepReport"; | |||
public static final String VERBOSE_KEY = "sonar.verbose"; | |||
public static final String DUMP_REPORT_PROP_KEY = "sonar.batch.dumpReportDir"; | |||
public static final String JSON_DETAILS_FILE = "analysis-details.json"; | |||
public static final String METADATA_DUMP_FILENAME = "analysis-details.json"; | |||
private final ServerClient serverClient; | |||
private final Server server; | |||
private final Settings settings; | |||
private final WsClient wsClient; | |||
private final AnalysisContextReportPublisher contextPublisher; | |||
private final ImmutableProjectReactor projectReactor; | |||
private final DefaultAnalysisMode analysisMode; | |||
private final TempFolder temp; | |||
private final AnalysisContextReportPublisher contextPublisher; | |||
private ReportPublisherStep[] publishers; | |||
private final ReportPublisherStep[] publishers; | |||
private File reportDir; | |||
private BatchReportWriter writer; | |||
public ReportPublisher(Settings settings, ServerClient serverClient, Server server, AnalysisContextReportPublisher contextPublisher, | |||
public ReportPublisher(Settings settings, WsClient wsClient, AnalysisContextReportPublisher contextPublisher, | |||
ImmutableProjectReactor projectReactor, DefaultAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers) { | |||
this.serverClient = serverClient; | |||
this.server = server; | |||
this.settings = settings; | |||
this.wsClient = wsClient; | |||
this.contextPublisher = contextPublisher; | |||
this.projectReactor = projectReactor; | |||
this.settings = settings; | |||
this.analysisMode = analysisMode; | |||
this.temp = temp; | |||
this.publishers = publishers; | |||
@@ -101,7 +93,7 @@ public class ReportPublisher implements Startable { | |||
if (!settings.getBoolean(KEEP_REPORT_PROP_KEY) && !settings.getBoolean(VERBOSE_KEY)) { | |||
FileUtils.deleteQuietly(reportDir); | |||
} else { | |||
LOG.info("Batch report generated in " + reportDir); | |||
LOG.info("Analysis report generated in " + reportDir); | |||
} | |||
} | |||
@@ -117,22 +109,22 @@ public class ReportPublisher implements Startable { | |||
// If this is a issues mode analysis then we should not upload reports | |||
String taskId = null; | |||
if (!analysisMode.isIssues()) { | |||
File report = prepareReport(); | |||
File report = generateReportFile(); | |||
if (!analysisMode.isMediumTest()) { | |||
taskId = sendOrDumpReport(report); | |||
taskId = upload(report); | |||
} | |||
} | |||
logSuccess(LoggerFactory.getLogger(getClass()), taskId); | |||
logSuccess(taskId); | |||
} | |||
private File prepareReport() { | |||
private File generateReportFile() { | |||
try { | |||
long startTime = System.currentTimeMillis(); | |||
for (ReportPublisherStep publisher : publishers) { | |||
publisher.publish(writer); | |||
} | |||
long stopTime = System.currentTimeMillis(); | |||
LOG.info("Analysis reports generated in " + (stopTime - startTime) + "ms, dir size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(reportDir))); | |||
LOG.info("Analysis report generated in " + (stopTime - startTime) + "ms, dir size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(reportDir))); | |||
startTime = System.currentTimeMillis(); | |||
File reportZip = temp.newFile("batch-report", ".zip"); | |||
@@ -141,129 +133,81 @@ public class ReportPublisher implements Startable { | |||
LOG.info("Analysis reports compressed in " + (stopTime - startTime) + "ms, zip size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(reportZip))); | |||
return reportZip; | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Unable to prepare batch report", e); | |||
throw new IllegalStateException("Unable to prepare analysis report", e); | |||
} | |||
} | |||
@CheckForNull | |||
/** | |||
* Uploads the report file to server and returns the generated task id | |||
*/ | |||
@VisibleForTesting | |||
String sendOrDumpReport(File report) { | |||
ProjectDefinition projectDefinition = projectReactor.getRoot(); | |||
String effectiveKey = projectDefinition.getKeyWithBranch(); | |||
String relativeUrl = String.format("/api/ce/submit?projectKey=%s&projectName=%s&projectBranch=%s", | |||
projectDefinition.getKey(), BatchUtils.encodeForUrl(projectDefinition.getName()), BatchUtils.encodeForUrl(projectDefinition.getBranch())); | |||
String dumpDirLocation = settings.getString(DUMP_REPORT_PROP_KEY); | |||
if (dumpDirLocation == null) { | |||
return uploadMultiPartReport(report, relativeUrl); | |||
} else { | |||
dumpReport(dumpDirLocation, effectiveKey, relativeUrl, report); | |||
return null; | |||
} | |||
} | |||
private String uploadMultiPartReport(File report, String relativeUrl) { | |||
LOG.debug("Publish results"); | |||
String upload(File report) { | |||
LOG.debug("Upload report"); | |||
long startTime = System.currentTimeMillis(); | |||
URL url; | |||
try { | |||
url = new URL(serverClient.getURL() + relativeUrl); | |||
} catch (MalformedURLException e) { | |||
throw new IllegalArgumentException("Invalid URL", e); | |||
} | |||
HttpRequest request = HttpRequest.post(url); | |||
request.trustAllCerts(); | |||
request.trustAllHosts(); | |||
request.header("User-Agent", format("SonarQube %s", server.getVersion())); | |||
request.basic(serverClient.getLogin(), serverClient.getPassword()); | |||
request.part("report", null, "application/octet-stream", report); | |||
if (!request.ok()) { | |||
throw serverClient.handleHttpException(url.toString(), request.code(), request.body(), null); | |||
} | |||
ProjectDefinition projectDefinition = projectReactor.getRoot(); | |||
SubmitWsRequest submitRequest = new SubmitWsRequest(); | |||
submitRequest.setProjectKey(projectDefinition.getKey()); | |||
submitRequest.setProjectName(projectDefinition.getName()); | |||
submitRequest.setProjectBranch(projectDefinition.getBranch()); | |||
submitRequest.setReport(report); | |||
WsCe.SubmitResponse submitResponse = wsClient.computeEngine().submit(submitRequest); | |||
long stopTime = System.currentTimeMillis(); | |||
LOG.info("Analysis reports sent to server in " + (stopTime - startTime) + "ms"); | |||
String responseStr = request.body(); | |||
SubmitResponse response = new Gson().fromJson(responseStr, SubmitResponse.class); | |||
return response.getTaskId(); | |||
} | |||
private static class SubmitResponse { | |||
private String taskId; | |||
public String getTaskId() { | |||
return taskId; | |||
} | |||
public void setTaskId(String taskId) { | |||
this.taskId = taskId; | |||
} | |||
} | |||
private static void dumpReport(String dumpDirLocation, String projectKey, String relativeUrl, File report) { | |||
LOG.debug("Dump report to file"); | |||
try { | |||
dumpReportImpl(dumpDirLocation, projectKey, relativeUrl, report); | |||
} catch (IOException | URISyntaxException e) { | |||
LOG.error("Failed to dump report to directory " + dumpDirLocation, e); | |||
} | |||
} | |||
private static void dumpReportImpl(String dumpDirLocation, String projectKey, String relativeUrl, File report) throws IOException, URISyntaxException { | |||
File dumpDir = new File(dumpDirLocation); | |||
if (!dumpDir.exists() || !dumpDir.isDirectory()) { | |||
LOG.warn("Report dump directory '{}' does not exist or is not a directory", dumpDirLocation); | |||
return; | |||
} | |||
long dateTime = new Date().getTime(); | |||
File dumpedZip = new File(dumpDir, format("batch-report_%s_%s.zip", projectKey, dateTime)); | |||
FileUtils.copyFile(report, new FileOutputStream(dumpedZip)); | |||
File dumpedMetadata = new File(dumpDir, format("batch-report_%s_%s.txt", projectKey, dateTime)); | |||
FileUtils.write(dumpedMetadata, relativeUrl); | |||
LOG.info("Batch report dumped to {}", dumpedZip.getAbsolutePath()); | |||
LOG.info("Analysis report uploaded in " + (stopTime - startTime) + "ms"); | |||
return submitResponse.getTaskId(); | |||
} | |||
@VisibleForTesting | |||
void logSuccess(Logger logger, @Nullable String taskId) { | |||
if (analysisMode.isIssues() || analysisMode.isMediumTest()) { | |||
logger.info("ANALYSIS SUCCESSFUL"); | |||
void logSuccess(@Nullable String taskId) { | |||
if (taskId == null) { | |||
LOG.info("ANALYSIS SUCCESSFUL"); | |||
} else { | |||
String baseUrl = settings.getString(CoreProperties.SERVER_BASE_URL); | |||
if (baseUrl.equals(settings.getDefaultValue(CoreProperties.SERVER_BASE_URL))) { | |||
// If server base URL was not configured in Sonar server then is is better to take URL configured on batch side | |||
baseUrl = serverClient.getURL(); | |||
} | |||
if (!baseUrl.endsWith("/")) { | |||
baseUrl += "/"; | |||
} | |||
Map<String, String> metadata = new LinkedHashMap<>(); | |||
String effectiveKey = projectReactor.getRoot().getKeyWithBranch(); | |||
String url = baseUrl + "dashboard/index/" + effectiveKey; | |||
logger.info("ANALYSIS SUCCESSFUL, you can browse {}", url); | |||
logger.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report."); | |||
String taskUrl = null; | |||
if (taskId != null) { | |||
taskUrl = baseUrl + "api/ce/task?id=" + taskId; | |||
logger.info("More about the report processing at {}", taskUrl); | |||
} | |||
writeJson(url, taskUrl); | |||
metadata.put("projectKey", effectiveKey); | |||
URL dashboardUrl = HttpUrl.parse(publicUrl()).newBuilder() | |||
.addPathSegment("dashboard").addPathSegment("index").addPathSegment(effectiveKey) | |||
.build() | |||
.url(); | |||
metadata.put("dashboardUrl", dashboardUrl.toExternalForm()); | |||
URL taskUrl = HttpUrl.parse(publicUrl()).newBuilder() | |||
.addPathSegment("api").addPathSegment("ce").addPathSegment("task") | |||
.addQueryParameter("id", taskId) | |||
.build() | |||
.url(); | |||
metadata.put("ceTaskId", taskId); | |||
metadata.put("ceTaskUrl", taskUrl.toExternalForm()); | |||
LOG.info("ANALYSIS SUCCESSFUL, you can browse {}", dashboardUrl); | |||
LOG.info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report"); | |||
LOG.info("More about the report processing at {}", taskUrl); | |||
dumpMetadata(metadata); | |||
} | |||
} | |||
private void writeJson(String dashboardUrl, @Nullable String taskUrl) { | |||
File exportFile = new File(projectReactor.getRoot().getWorkDir(), JSON_DETAILS_FILE); | |||
try (Writer output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(exportFile), StandardCharsets.UTF_8))) { | |||
private void dumpMetadata(Map<String, String> metadata) { | |||
File file = new File(projectReactor.getRoot().getWorkDir(), METADATA_DUMP_FILENAME); | |||
try (Writer output = Files.newWriter(file, StandardCharsets.UTF_8)) { | |||
JsonWriter json = JsonWriter.of(output); | |||
json.beginObject(); | |||
json.prop("dashboardUrl", dashboardUrl); | |||
if (taskUrl != null) { | |||
json.prop("ceTaskUrl", taskUrl); | |||
for (Map.Entry<String, String> entry : metadata.entrySet()) { | |||
json.prop(entry.getKey(), entry.getValue()); | |||
} | |||
json.endObject(); | |||
LOG.debug("Analysis URLs written to {}", exportFile); | |||
LOG.debug("Report metadata written to {}", file); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Unable to write analysis URLs in file " + exportFile.getAbsolutePath(), e); | |||
throw new IllegalStateException("Unable to dump " + file, e); | |||
} | |||
} | |||
/** | |||
* The public URL is optionally configured on server. If not, then the regular URL is returned. | |||
* See https://jira.sonarsource.com/browse/SONAR-4239 | |||
*/ | |||
private String publicUrl() { | |||
return defaultIfBlank(settings.getString(CoreProperties.SERVER_BASE_URL), wsClient.wsConnector().baseUrl()); | |||
} | |||
} |
@@ -21,12 +21,12 @@ package org.sonar.batch.repository; | |||
import com.google.common.base.Throwables; | |||
import org.sonar.api.utils.HttpDownloader.HttpException; | |||
import com.google.common.collect.HashBasedTable; | |||
import com.google.common.collect.Table; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.HttpURLConnection; | |||
import java.util.Date; | |||
import java.util.Map; | |||
@@ -42,6 +42,7 @@ import org.sonar.batch.util.BatchUtils; | |||
import org.sonarqube.ws.WsBatch.WsProjectResponse; | |||
import org.sonarqube.ws.WsBatch.WsProjectResponse.FileDataByPath; | |||
import org.sonarqube.ws.WsBatch.WsProjectResponse.Settings; | |||
import org.sonarqube.ws.client.HttpException; | |||
public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoader { | |||
private static final Logger LOG = LoggerFactory.getLogger(DefaultProjectRepositoriesLoader.class); | |||
@@ -85,7 +86,7 @@ public class DefaultProjectRepositoriesLoader implements ProjectRepositoriesLoad | |||
for (Throwable t : Throwables.getCausalChain(e)) { | |||
if (t instanceof HttpException) { | |||
HttpException http = (HttpException) t; | |||
return http.getResponseCode() != 404; | |||
return http.code() != HttpURLConnection.HTTP_NOT_FOUND; | |||
} | |||
} | |||
@@ -32,9 +32,10 @@ import javax.annotation.Nullable; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import static com.google.common.base.Preconditions.checkState; | |||
public class DefaultQualityProfileLoader implements QualityProfileLoader { | |||
private static final String WS_URL = "/api/qualityprofiles/search.protobuf"; | |||
@@ -62,12 +63,6 @@ public class DefaultQualityProfileLoader implements QualityProfileLoader { | |||
return loadResource(url, fromCache); | |||
} | |||
private static void validate(Collection<QualityProfile> profiles) { | |||
if (profiles == null || profiles.isEmpty()) { | |||
throw new IllegalStateException("No quality profiles has been found this project, you probably don't have any language plugin suitable for this analysis."); | |||
} | |||
} | |||
private List<QualityProfile> loadResource(String url, @Nullable MutableBoolean fromCache) { | |||
WSLoaderResult<InputStream> result = wsLoader.loadStream(url); | |||
if (fromCache != null) { | |||
@@ -85,7 +80,8 @@ public class DefaultQualityProfileLoader implements QualityProfileLoader { | |||
} | |||
List<QualityProfile> profilesList = profiles.getProfilesList(); | |||
validate(profilesList); | |||
checkState(profilesList != null && !profilesList.isEmpty(), | |||
"No quality profiles has been found this project, you probably don't have any language plugin suitable for this analysis."); | |||
return profilesList; | |||
} | |||
@@ -19,17 +19,15 @@ | |||
*/ | |||
package org.sonar.batch.analysis; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.mockito.Mock; | |||
import org.mockito.MockitoAnnotations; | |||
import org.sonar.api.batch.AnalysisMode; | |||
import org.sonar.batch.bootstrap.ServerClient; | |||
import org.sonar.batch.cache.WSLoader; | |||
import org.sonar.batch.cache.WSLoader.LoadStrategy; | |||
import org.sonar.home.cache.PersistentCache; | |||
import org.sonarqube.ws.client.WsClient; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@@ -38,27 +36,22 @@ public class AnalysisWSLoaderProviderTest { | |||
private PersistentCache cache; | |||
@Mock | |||
private ServerClient client; | |||
private WsClient client; | |||
@Mock | |||
private AnalysisMode mode; | |||
private AnalysisWSLoaderProvider loaderProvider; | |||
private Map<String, String> propMap; | |||
private AnalysisProperties props; | |||
@Before | |||
public void setUp() { | |||
MockitoAnnotations.initMocks(this); | |||
loaderProvider = new AnalysisWSLoaderProvider(); | |||
propMap = new HashMap<>(); | |||
} | |||
@Test | |||
public void testDefault() { | |||
props = new AnalysisProperties(propMap, null); | |||
WSLoader loader = loaderProvider.provide(props, mode, cache, client); | |||
WSLoader loader = loaderProvider.provide(mode, cache, client); | |||
assertThat(loader.getDefaultStrategy()).isEqualTo(LoadStrategy.SERVER_ONLY); | |||
} | |||
} |
@@ -19,18 +19,17 @@ | |||
*/ | |||
package org.sonar.batch.bootstrap; | |||
import org.sonar.batch.cache.WSLoaderResult; | |||
import org.sonar.batch.cache.WSLoader; | |||
import java.io.File; | |||
import java.util.List; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.batch.cache.WSLoader; | |||
import org.sonar.batch.cache.WSLoaderResult; | |||
import org.sonar.core.platform.RemotePlugin; | |||
import org.sonar.home.cache.FileCache; | |||
import java.io.File; | |||
import java.util.List; | |||
import org.sonarqube.ws.client.WsClient; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Matchers.any; | |||
@@ -48,15 +47,15 @@ public class BatchPluginInstallerTest { | |||
public ExpectedException thrown = ExpectedException.none(); | |||
FileCache fileCache = mock(FileCache.class); | |||
ServerClient serverClient = mock(ServerClient.class); | |||
WsClient wsClient = mock(WsClient.class); | |||
BatchPluginPredicate pluginPredicate = mock(BatchPluginPredicate.class); | |||
@Test | |||
public void listRemotePlugins() { | |||
WSLoader wsLoader = mock(WSLoader.class); | |||
when(wsLoader.loadString("/deploy/plugins/index.txt")).thenReturn(new WSLoaderResult<String>("checkstyle\nsqale", true)); | |||
BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, serverClient, fileCache, pluginPredicate); | |||
when(wsLoader.loadString("/deploy/plugins/index.txt")).thenReturn(new WSLoaderResult<>("checkstyle\nsqale", true)); | |||
BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate); | |||
List<RemotePlugin> remotePlugins = installer.listRemotePlugins(); | |||
assertThat(remotePlugins).extracting("key").containsOnly("checkstyle", "sqale"); | |||
@@ -68,7 +67,7 @@ public class BatchPluginInstallerTest { | |||
when(fileCache.get(eq("checkstyle-plugin.jar"), eq("fakemd5_1"), any(FileCache.Downloader.class))).thenReturn(pluginJar); | |||
WSLoader wsLoader = mock(WSLoader.class); | |||
BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, serverClient, fileCache, pluginPredicate); | |||
BatchPluginInstaller installer = new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate); | |||
RemotePlugin remote = new RemotePlugin("checkstyle").setFile("checkstyle-plugin.jar", "fakemd5_1"); | |||
File file = installer.download(remote); | |||
@@ -83,6 +82,6 @@ public class BatchPluginInstallerTest { | |||
WSLoader wsLoader = mock(WSLoader.class); | |||
doThrow(new IllegalStateException()).when(wsLoader).loadString("/deploy/plugins/index.txt"); | |||
new BatchPluginInstaller(wsLoader, serverClient, fileCache, pluginPredicate).installRemotes(); | |||
new BatchPluginInstaller(wsLoader, wsClient, fileCache, pluginPredicate).installRemotes(); | |||
} | |||
} |
@@ -1,138 +0,0 @@ | |||
/* | |||
* 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.batch.bootstrap; | |||
import org.sonar.batch.util.BatchUtils; | |||
import java.io.File; | |||
import java.nio.charset.StandardCharsets; | |||
import java.nio.file.Files; | |||
import org.junit.After; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.batch.bootstrapper.EnvironmentInformation; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Matchers.eq; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
public class ServerClientTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
private MockHttpServer server = null; | |||
private GlobalProperties bootstrapProps = mock(GlobalProperties.class); | |||
@After | |||
public void stopServer() { | |||
if (server != null) { | |||
server.stop(); | |||
} | |||
} | |||
@Test | |||
public void should_remove_url_ending_slash() { | |||
GlobalProperties settings = mock(GlobalProperties.class); | |||
when(settings.property("sonar.host.url")).thenReturn("http://localhost:8080/sonar/"); | |||
ServerClient client = new ServerClient(settings, new EnvironmentInformation("Junit", "4")); | |||
assertThat(client.getURL()).isEqualTo("http://localhost:8080/sonar"); | |||
} | |||
@Test | |||
public void should_request_url() throws Exception { | |||
startServer(null, "this is the content"); | |||
assertThat(newServerClient().downloadString("/foo")).isEqualTo("this is the content"); | |||
} | |||
@Test | |||
public void should_escape_html_from_url() throws Exception { | |||
startServer(null, "this is the content"); | |||
assertThat(newServerClient().downloadString("/<foo>")).isEqualTo("this is the content"); | |||
} | |||
@Test | |||
public void should_download_file() throws Exception { | |||
startServer(null, "this is the content"); | |||
File file = temp.newFile(); | |||
newServerClient().download("/foo", file); | |||
assertThat(new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)).isEqualTo("this is the content"); | |||
} | |||
@Test | |||
public void should_fail_if_unauthorized_with_no_login_password() throws Exception { | |||
startServer(401, null); | |||
thrown.expectMessage("Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties sonar.login and sonar.password."); | |||
newServerClient().downloadString("/foo"); | |||
} | |||
@Test | |||
public void should_fail_if_unauthorized_with_login_password_provided() throws Exception { | |||
startServer(401, null); | |||
when(bootstrapProps.property(eq("sonar.login"))).thenReturn("login"); | |||
when(bootstrapProps.property(eq("sonar.password"))).thenReturn("password"); | |||
thrown.expectMessage("Not authorized. Please check the properties sonar.login and sonar.password"); | |||
newServerClient().downloadString("/foo"); | |||
} | |||
@Test | |||
public void should_display_json_error_when_403() throws Exception { | |||
startServer(403, "{\"errors\":[{\"msg\":\"Insufficient privileges\"}]}"); | |||
thrown.expectMessage("Insufficient privileges"); | |||
newServerClient().downloadString("/foo"); | |||
} | |||
@Test | |||
public void should_fail_if_error() throws Exception { | |||
startServer(500, null); | |||
thrown.expectMessage("Fail to execute request [code=500, url=http://localhost:" + server.getPort() + "/foo]"); | |||
newServerClient().downloadString("/foo"); | |||
} | |||
@Test | |||
public void string_encode() { | |||
assertThat(BatchUtils.encodeForUrl("my value")).isEqualTo("my+value"); | |||
} | |||
private ServerClient newServerClient() { | |||
when(bootstrapProps.property("sonar.host.url")).thenReturn("http://localhost:" + server.getPort()); | |||
return new ServerClient(bootstrapProps, new EnvironmentInformation("Junit", "4")); | |||
} | |||
private void startServer(Integer responseStatus, String responseData) throws Exception { | |||
server = new MockHttpServer(); | |||
server.start(); | |||
if (responseStatus != null) { | |||
server.setMockResponseStatus(responseStatus); | |||
} | |||
if (responseData != null) { | |||
server.setMockResponseData(responseData); | |||
} | |||
} | |||
} |
@@ -0,0 +1,129 @@ | |||
/* | |||
* 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.batch.bootstrap; | |||
import com.squareup.okhttp.Interceptor; | |||
import com.squareup.okhttp.MediaType; | |||
import com.squareup.okhttp.Protocol; | |||
import com.squareup.okhttp.Request; | |||
import com.squareup.okhttp.Response; | |||
import com.squareup.okhttp.ResponseBody; | |||
import java.util.List; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.api.utils.log.LogTester; | |||
import org.sonar.api.utils.log.LoggerLevel; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
public class WsClientLoggingInterceptorTest { | |||
@Rule | |||
public LogTester logTester = new LogTester(); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
WsClientLoggingInterceptor underTest = new WsClientLoggingInterceptor(); | |||
Interceptor.Chain chain = mock(Interceptor.Chain.class); | |||
@Test | |||
public void log_and_profile_request_if_debug_level() throws Exception { | |||
Request request = newRequest(); | |||
Response response = newResponse(request, 200, ""); | |||
when(chain.request()).thenReturn(request); | |||
when(chain.proceed(request)).thenReturn(response); | |||
logTester.setLevel(LoggerLevel.DEBUG); | |||
Response result = underTest.intercept(chain); | |||
// do not fail the execution -> interceptor returns the response | |||
assertThat(result).isSameAs(response); | |||
// check logs | |||
List<String> debugLogs = logTester.logs(LoggerLevel.DEBUG); | |||
assertThat(debugLogs).hasSize(1); | |||
assertThat(debugLogs.get(0)).contains("GET 200 https://localhost:9000/api/issues/search | time="); | |||
List<String> traceLogs = logTester.logs(LoggerLevel.TRACE); | |||
assertThat(traceLogs).hasSize(1); | |||
assertThat(traceLogs.get(0)).isEqualTo("GET https://localhost:9000/api/issues/search"); | |||
} | |||
@Test | |||
public void fail_if_requires_authentication() throws Exception { | |||
expectedException.expect(MessageException.class); | |||
expectedException | |||
.expectMessage("Not authorized. Analyzing this project requires to be authenticated. Please provide the values of the properties sonar.login and sonar.password."); | |||
Request request = newRequest(); | |||
Response response = newResponse(request, 401, ""); | |||
when(chain.request()).thenReturn(request); | |||
when(chain.proceed(request)).thenReturn(response); | |||
underTest.intercept(chain); | |||
} | |||
@Test | |||
public void fail_if_credentials_are_not_valid() throws Exception { | |||
expectedException.expect(MessageException.class); | |||
expectedException.expectMessage("Not authorized. Please check the properties sonar.login and sonar.password."); | |||
Request request = new Request.Builder() | |||
.url("https://localhost:9000/api/issues/search") | |||
.header("Authorization", "Basic BAD_CREDENTIALS") | |||
.get() | |||
.build(); | |||
Response response = newResponse(request, 401, ""); | |||
when(chain.request()).thenReturn(request); | |||
when(chain.proceed(request)).thenReturn(response); | |||
underTest.intercept(chain); | |||
} | |||
@Test | |||
public void fail_if_requires_permission() throws Exception { | |||
expectedException.expect(MessageException.class); | |||
expectedException.expectMessage("missing scan permission, missing another permission"); | |||
Request request = newRequest(); | |||
Response response = newResponse(request, 403, "{\"errors\":[{\"msg\":\"missing scan permission\"}, {\"msg\":\"missing another permission\"}]}"); | |||
when(chain.request()).thenReturn(request); | |||
when(chain.proceed(request)).thenReturn(response); | |||
underTest.intercept(chain); | |||
} | |||
private Request newRequest() { | |||
return new Request.Builder().url("https://localhost:9000/api/issues/search").get().build(); | |||
} | |||
private Response newResponse(Request getRequest, int code, String jsonBody) { | |||
return new Response.Builder().request(getRequest) | |||
.code(code) | |||
.protocol(Protocol.HTTP_1_1) | |||
.body(ResponseBody.create(MediaType.parse("application/json"), jsonBody)) | |||
.build(); | |||
} | |||
} |
@@ -0,0 +1,81 @@ | |||
/* | |||
* 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.batch.bootstrap; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.junit.Test; | |||
import org.sonar.batch.bootstrapper.EnvironmentInformation; | |||
import org.sonarqube.ws.client.HttpConnector; | |||
import org.sonarqube.ws.client.WsClient; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.batch.bootstrap.WsClientProvider.CONNECT_TIMEOUT_MS; | |||
import static org.sonar.batch.bootstrap.WsClientProvider.DEFAULT_READ_TIMEOUT_SEC; | |||
public class WsClientProviderTest { | |||
WsClientProvider underTest = new WsClientProvider(); | |||
EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.3"); | |||
@Test | |||
public void provide_client_with_default_settings() { | |||
GlobalProperties settings = new GlobalProperties(new HashMap<String, String>()); | |||
WsClient client = underTest.provide(settings, env); | |||
assertThat(client).isNotNull(); | |||
HttpConnector httpConnector = (HttpConnector) client.wsConnector(); | |||
assertThat(httpConnector.baseUrl()).isEqualTo("http://localhost:9000/"); | |||
assertThat(httpConnector.okHttpClient().getProxy()).isNull(); | |||
assertThat(httpConnector.okHttpClient().getConnectTimeout()).isEqualTo(5_000); | |||
assertThat(httpConnector.okHttpClient().getReadTimeout()).isEqualTo(60_000); | |||
assertThat(httpConnector.userAgent()).isEqualTo("Maven Plugin/2.3"); | |||
assertThat(httpConnector.okHttpClient().interceptors()) | |||
.hasSize(1) | |||
.hasOnlyElementsOfType(WsClientLoggingInterceptor.class); | |||
} | |||
@Test | |||
public void provide_client_with_custom_settings() { | |||
Map<String, String> props = new HashMap<>(); | |||
props.put("sonar.host.url", "https://here/sonarqube"); | |||
props.put("sonar.login", "theLogin"); | |||
props.put("sonar.password", "thePassword"); | |||
props.put("sonar.ws.timeout", "42"); | |||
GlobalProperties settings = new GlobalProperties(props); | |||
WsClient client = underTest.provide(settings, env); | |||
assertThat(client).isNotNull(); | |||
HttpConnector httpConnector = (HttpConnector) client.wsConnector(); | |||
assertThat(httpConnector.baseUrl()).isEqualTo("https://here/sonarqube/"); | |||
assertThat(httpConnector.okHttpClient().getProxy()).isNull(); | |||
assertThat(httpConnector.userAgent()).isEqualTo("Maven Plugin/2.3"); | |||
} | |||
@Test | |||
public void build_singleton() { | |||
GlobalProperties settings = new GlobalProperties(new HashMap<String, String>()); | |||
WsClient first = underTest.provide(settings, env); | |||
WsClient second = underTest.provide(settings, env); | |||
assertThat(first).isSameAs(second); | |||
} | |||
} |
@@ -19,24 +19,22 @@ | |||
*/ | |||
package org.sonar.batch.cache; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import com.google.common.io.Files; | |||
import org.junit.rules.ExpectedException; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.charset.StandardCharsets; | |||
import java.util.Date; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.junit.Rule; | |||
import org.junit.Before; | |||
import org.sonar.batch.bootstrap.ServerClient; | |||
import org.sonar.home.cache.PersistentCache; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
public class DefaultProjectCacheStatusTest { | |||
@Rule | |||
public TemporaryFolder tmp = new TemporaryFolder(); | |||
@@ -45,13 +43,10 @@ public class DefaultProjectCacheStatusTest { | |||
public ExpectedException exception = ExpectedException.none(); | |||
ProjectCacheStatus cacheStatus; | |||
PersistentCache cache; | |||
ServerClient client; | |||
PersistentCache cache = mock(PersistentCache.class); | |||
@Before | |||
public void setUp() { | |||
client = mock(ServerClient.class); | |||
cache = mock(PersistentCache.class); | |||
when(cache.getDirectory()).thenReturn(tmp.getRoot().toPath()); | |||
cacheStatus = new DefaultProjectCacheStatus(cache); | |||
} |
@@ -19,22 +19,20 @@ | |||
*/ | |||
package org.sonar.batch.cache; | |||
import org.sonar.batch.protocol.input.ProjectRepositories; | |||
import java.util.HashMap; | |||
import org.junit.Test; | |||
import org.sonar.batch.bootstrap.GlobalProperties; | |||
import org.sonar.batch.bootstrap.ServerClient; | |||
import org.sonar.batch.protocol.input.ProjectRepositories; | |||
import org.sonar.core.platform.ComponentContainer; | |||
import org.sonar.home.cache.PersistentCache; | |||
import java.util.HashMap; | |||
import org.sonarqube.ws.client.WsClient; | |||
import static org.mockito.Mockito.mock; | |||
import org.sonar.core.platform.ComponentContainer; | |||
import org.junit.Test; | |||
public class ProjectSyncContainerTest { | |||
private ComponentContainer createParentContainer() { | |||
PersistentCache cache = mock(PersistentCache.class); | |||
ServerClient server = mock(ServerClient.class); | |||
WsClient server = mock(WsClient.class); | |||
GlobalProperties globalProps = new GlobalProperties(new HashMap<String, String>()); | |||
ComponentContainer parent = new ComponentContainer(); |
@@ -23,33 +23,28 @@ import org.junit.Before; | |||
import org.junit.Test; | |||
import org.mockito.Mock; | |||
import org.mockito.MockitoAnnotations; | |||
import org.sonar.batch.bootstrap.GlobalProperties; | |||
import org.sonar.batch.bootstrap.ServerClient; | |||
import org.sonar.batch.cache.WSLoader.LoadStrategy; | |||
import org.sonar.home.cache.PersistentCache; | |||
import org.sonarqube.ws.client.WsClient; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
public class StrategyWSLoaderProviderTest { | |||
@Mock | |||
private PersistentCache cache; | |||
@Mock | |||
private ServerClient client; | |||
private GlobalProperties globalProps; | |||
private WsClient client; | |||
@Before | |||
public void setUp() { | |||
MockitoAnnotations.initMocks(this); | |||
globalProps = mock(GlobalProperties.class); | |||
} | |||
@Test | |||
public void testStrategy() { | |||
StrategyWSLoaderProvider provider = new StrategyWSLoaderProvider(LoadStrategy.CACHE_FIRST); | |||
WSLoader wsLoader = provider.provide(cache, client, globalProps); | |||
WSLoader wsLoader = provider.provide(cache, client); | |||
assertThat(wsLoader.getDefaultStrategy()).isEqualTo(LoadStrategy.CACHE_FIRST); | |||
} | |||
@@ -57,8 +52,8 @@ public class StrategyWSLoaderProviderTest { | |||
@Test | |||
public void testSingleton() { | |||
StrategyWSLoaderProvider provider = new StrategyWSLoaderProvider(LoadStrategy.CACHE_FIRST); | |||
WSLoader wsLoader = provider.provide(cache, client, globalProps); | |||
WSLoader wsLoader = provider.provide(cache, client); | |||
assertThat(provider.provide(null, null, null)).isEqualTo(wsLoader); | |||
assertThat(provider.provide(null, null)).isEqualTo(wsLoader); | |||
} | |||
} |
@@ -19,34 +19,28 @@ | |||
*/ | |||
package org.sonar.batch.cache; | |||
import com.google.common.collect.ImmutableMap; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.URI; | |||
import org.apache.commons.io.IOUtils; | |||
import org.hamcrest.Matchers; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.mockito.InOrder; | |||
import org.mockito.Mock; | |||
import org.mockito.Mockito; | |||
import org.mockito.MockitoAnnotations; | |||
import org.mockito.invocation.InvocationOnMock; | |||
import org.mockito.stubbing.Answer; | |||
import org.sonar.api.utils.HttpDownloader; | |||
import org.sonar.batch.bootstrap.ServerClient; | |||
import org.sonar.batch.bootstrap.UserProperties; | |||
import org.sonar.batch.cache.WSLoader.LoadStrategy; | |||
import org.sonar.home.cache.PersistentCache; | |||
import org.sonarqube.ws.client.HttpException; | |||
import org.sonarqube.ws.client.MockWsResponse; | |||
import org.sonarqube.ws.client.WsClient; | |||
import org.sonarqube.ws.client.WsConnector; | |||
import org.sonarqube.ws.client.WsRequest; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.fail; | |||
import static org.mockito.Matchers.anyBoolean; | |||
import static org.mockito.Matchers.anyInt; | |||
import static org.mockito.Matchers.anyString; | |||
import static org.mockito.Matchers.any; | |||
import static org.mockito.Matchers.eq; | |||
import static org.mockito.Mockito.inOrder; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.times; | |||
import static org.mockito.Mockito.verify; | |||
@@ -58,35 +52,20 @@ public class WSLoaderTest { | |||
private final static String cacheValue = "cache"; | |||
private final static String serverValue = "server"; | |||
@Mock | |||
private ServerClient client; | |||
@Mock | |||
private PersistentCache cache; | |||
@Rule | |||
public ExpectedException exception = ExpectedException.none(); | |||
private UserProperties props; | |||
@Before | |||
public void setUp() throws IOException { | |||
MockitoAnnotations.initMocks(this); | |||
when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenReturn(IOUtils.toInputStream(serverValue)); | |||
when(cache.getString(ID)).thenReturn(cacheValue); | |||
when(client.getURI(anyString())).thenAnswer(new Answer<URI>() { | |||
@Override | |||
public URI answer(InvocationOnMock invocation) throws Throwable { | |||
return new URI((String) invocation.getArguments()[0]); | |||
} | |||
}); | |||
props = mock(UserProperties.class); | |||
} | |||
WsClient ws = mock(WsClient.class, Mockito.RETURNS_DEEP_STUBS); | |||
PersistentCache cache = mock(PersistentCache.class); | |||
@Test | |||
public void dont_retry_server_offline() throws IOException { | |||
turnServerOffline(); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props); | |||
when(cache.getString(ID)).thenReturn(cacheValue); | |||
WSLoader underTest = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); | |||
assertResult(loader.loadString(ID), cacheValue, true); | |||
assertResult(loader.loadString(ID), cacheValue, true); | |||
assertResult(underTest.loadString(ID), cacheValue, true); | |||
assertResult(underTest.loadString(ID), cacheValue, true); | |||
assertUsedServer(1); | |||
assertUsedCache(2); | |||
@@ -94,84 +73,67 @@ public class WSLoaderTest { | |||
@Test | |||
public void get_stream_from_cache() throws IOException { | |||
InputStream is = mock(InputStream.class); | |||
InputStream is = IOUtils.toInputStream("is"); | |||
when(cache.getStream(ID)).thenReturn(is); | |||
WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, client, props); | |||
WSLoaderResult<InputStream> result = loader.loadStream(ID); | |||
assertThat(result.get()).isEqualTo(is); | |||
verify(cache).getStream(ID); | |||
verifyNoMoreInteractions(cache, client); | |||
} | |||
@Test | |||
public void put_stream_in_cache() throws IOException { | |||
InputStream is1 = mock(InputStream.class); | |||
InputStream is2 = mock(InputStream.class); | |||
when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenReturn(is1); | |||
when(cache.getStream(ID)).thenReturn(is2); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props); | |||
WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws); | |||
WSLoaderResult<InputStream> result = loader.loadStream(ID); | |||
assertThat(result.get()).isEqualTo(is2); | |||
verify(client).load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt()); | |||
verify(cache).put(ID, is1); | |||
assertThat(result.get()).isEqualTo(is); | |||
verify(cache).getStream(ID); | |||
verifyNoMoreInteractions(cache, client); | |||
verifyNoMoreInteractions(cache, ws); | |||
} | |||
@Test | |||
public void default_timeout() throws IOException { | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props); | |||
loader.loadStream(ID); | |||
verify(client).load(anyString(), anyString(), anyBoolean(), anyInt(), eq(60_000)); | |||
verifyNoMoreInteractions(client); | |||
} | |||
@Test | |||
public void change_timeout() throws IOException { | |||
when(props.properties()).thenReturn(ImmutableMap.of(WSLoader.SONAR_WS_TIMEOUT_PROPS, "20")); | |||
when(props.property(WSLoader.SONAR_WS_TIMEOUT_PROPS)).thenReturn("20"); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props); | |||
loader.loadStream(ID); | |||
verify(client).load(anyString(), anyString(), anyBoolean(), anyInt(), eq(20_000)); | |||
verifyNoMoreInteractions(client); | |||
public void put_stream_in_cache() throws IOException { | |||
InputStream input = IOUtils.toInputStream("is"); | |||
when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(input)); | |||
when(cache.getStream(ID)).thenReturn(input); | |||
// SERVER_FIRST -> load from server then put to cache | |||
WSLoader underTest = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); | |||
WSLoaderResult<InputStream> result = underTest.loadStream(ID); | |||
assertThat(result.get()).isEqualTo(input); | |||
WsConnector wsConnector = ws.wsConnector(); | |||
InOrder inOrder = inOrder(wsConnector, cache); | |||
inOrder.verify(wsConnector).call(any(WsRequest.class)); | |||
inOrder.verify(cache).put(eq(ID), any(InputStream.class)); | |||
inOrder.verify(cache).getStream(ID); | |||
verifyNoMoreInteractions(cache, wsConnector); | |||
} | |||
@Test | |||
public void test_cache_strategy_fallback() throws IOException { | |||
turnCacheEmpty(); | |||
WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, client, props); | |||
when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); | |||
WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws); | |||
assertResult(loader.loadString(ID), serverValue, false); | |||
InOrder inOrder = Mockito.inOrder(client, cache); | |||
InOrder inOrder = inOrder(ws.wsConnector(), cache); | |||
inOrder.verify(cache).getString(ID); | |||
inOrder.verify(client).load(eq(ID), anyString(), anyBoolean(), anyInt(), anyInt()); | |||
inOrder.verify(ws.wsConnector()).call(any(WsRequest.class)); | |||
} | |||
@Test | |||
public void test_server_strategy_fallback() throws IOException { | |||
turnServerOffline(); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props); | |||
when(cache.getString(ID)).thenReturn(cacheValue); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); | |||
assertResult(loader.loadString(ID), cacheValue, true); | |||
InOrder inOrder = Mockito.inOrder(client, cache); | |||
inOrder.verify(client).load(eq(ID), anyString(), anyBoolean(), anyInt(), anyInt()); | |||
InOrder inOrder = inOrder(ws.wsConnector(), cache); | |||
inOrder.verify(ws.wsConnector()).call(any(WsRequest.class)); | |||
inOrder.verify(cache).getString(ID); | |||
} | |||
@Test | |||
public void test_put_cache() throws IOException { | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props); | |||
when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); | |||
loader.loadString(ID); | |||
verify(cache).put(ID, serverValue.getBytes()); | |||
} | |||
@@ -181,7 +143,7 @@ public class WSLoaderTest { | |||
turnServerOffline(); | |||
when(cache.getString(ID)).thenThrow(new NullPointerException()); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); | |||
try { | |||
loader.loadString(ID); | |||
@@ -196,7 +158,7 @@ public class WSLoaderTest { | |||
public void test_throw_cache_exception() throws IOException { | |||
when(cache.getString(ID)).thenThrow(new IllegalStateException()); | |||
WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, client, props); | |||
WSLoader loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, ws); | |||
try { | |||
loader.loadString(ID); | |||
@@ -209,12 +171,12 @@ public class WSLoaderTest { | |||
@Test | |||
public void test_throw_http_exceptions() { | |||
HttpDownloader.HttpException httpException = mock(HttpDownloader.HttpException.class); | |||
HttpException httpException = new HttpException("url", 500, "Internal Error"); | |||
IllegalStateException wrapperException = new IllegalStateException(httpException); | |||
when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenThrow(wrapperException); | |||
when(ws.wsConnector().call(any(WsRequest.class))).thenThrow(wrapperException); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); | |||
try { | |||
loader.loadString(ID); | |||
@@ -230,9 +192,9 @@ public class WSLoaderTest { | |||
turnServerOffline(); | |||
exception.expect(IllegalStateException.class); | |||
exception.expectMessage(Matchers.is("Server is not available")); | |||
exception.expectMessage(Matchers.containsString("Server is not available")); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, client, props); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, ws); | |||
loader.loadString(ID); | |||
} | |||
@@ -244,7 +206,7 @@ public class WSLoaderTest { | |||
exception.expect(IllegalStateException.class); | |||
exception.expectMessage(Matchers.is("Server is not accessible and data is not cached")); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); | |||
loader.loadString(ID); | |||
} | |||
@@ -255,13 +217,14 @@ public class WSLoaderTest { | |||
exception.expect(IllegalStateException.class); | |||
exception.expectMessage(Matchers.is("Data is not cached")); | |||
WSLoader loader = new WSLoader(LoadStrategy.CACHE_ONLY, cache, client, props); | |||
WSLoader loader = new WSLoader(LoadStrategy.CACHE_ONLY, cache, ws); | |||
loader.loadString(ID); | |||
} | |||
@Test | |||
public void test_server_strategy() throws IOException { | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props); | |||
when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); | |||
assertResult(loader.loadString(ID), serverValue, false); | |||
// should not fetch from cache | |||
@@ -272,13 +235,14 @@ public class WSLoaderTest { | |||
@Test(expected = IllegalStateException.class) | |||
public void test_server_only() throws IOException { | |||
turnServerOffline(); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, client, props); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, ws); | |||
loader.loadString(ID); | |||
} | |||
@Test | |||
public void test_string() { | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, props); | |||
when(ws.wsConnector().call(any(WsRequest.class))).thenReturn(new MockWsResponse().setContent(serverValue)); | |||
WSLoader loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, ws); | |||
assertResult(loader.loadString(ID), serverValue, false); | |||
} | |||
@@ -287,14 +251,7 @@ public class WSLoaderTest { | |||
} | |||
private void assertUsedServer(int times) { | |||
verify(client, times(times)).load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt()); | |||
} | |||
private void assertResult(WSLoaderResult<InputStream> result, byte[] expected, boolean fromCache) throws IOException { | |||
byte[] content = IOUtils.toByteArray(result.get()); | |||
assertThat(result).isNotNull(); | |||
assertThat(content).isEqualTo(expected); | |||
assertThat(result.isFromCache()).isEqualTo(fromCache); | |||
verify(ws.wsConnector(), times(times)).call(any(WsRequest.class)); | |||
} | |||
private void assertResult(WSLoaderResult<String> result, String expected, boolean fromCache) { | |||
@@ -304,7 +261,7 @@ public class WSLoaderTest { | |||
} | |||
private void turnServerOffline() { | |||
when(client.load(anyString(), anyString(), anyBoolean(), anyInt(), anyInt())).thenThrow(new IllegalStateException()); | |||
when(ws.wsConnector().call(any(WsRequest.class))).thenThrow(new IllegalStateException()); | |||
} | |||
private void turnCacheEmpty() throws IOException { |
@@ -1,111 +0,0 @@ | |||
/* | |||
* 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.batch.cache; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.batch.bootstrap.GlobalProperties; | |||
import org.sonar.batch.bootstrap.MockHttpServer; | |||
import org.sonar.batch.bootstrap.ServerClient; | |||
import org.sonar.batch.bootstrap.Slf4jLogger; | |||
import org.sonar.batch.bootstrap.UserProperties; | |||
import org.sonar.batch.bootstrapper.EnvironmentInformation; | |||
import org.sonar.batch.cache.WSLoader.LoadStrategy; | |||
import org.sonar.home.cache.DirectoryLock; | |||
import org.sonar.home.cache.PersistentCache; | |||
import org.sonar.home.cache.TTLCacheInvalidation; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
public class WSLoaderWithServerTest { | |||
private static final String RESPONSE_STRING = "this is the content"; | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
private MockHttpServer server; | |||
private PersistentCache cache; | |||
private ServerClient client; | |||
private WSLoader loader; | |||
private UserProperties userProps; | |||
@Before | |||
public void setUp() throws Exception { | |||
server = new MockHttpServer(); | |||
server.start(); | |||
GlobalProperties bootstrapProps = mock(GlobalProperties.class); | |||
when(bootstrapProps.property("sonar.host.url")).thenReturn("http://localhost:" + server.getPort()); | |||
client = new ServerClient(bootstrapProps, new EnvironmentInformation("Junit", "4")); | |||
cache = new PersistentCache(temp.getRoot().toPath(), new TTLCacheInvalidation(100_000L), new Slf4jLogger(), mock(DirectoryLock.class)); | |||
userProps = mock(UserProperties.class); | |||
} | |||
@After | |||
public void tearDown() { | |||
if (server != null) { | |||
server.stop(); | |||
} | |||
} | |||
@Test | |||
public void testCacheOnly() { | |||
loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, client, userProps); | |||
makeRequests(); | |||
loader = new WSLoader(LoadStrategy.CACHE_ONLY, cache, client, userProps); | |||
makeRequests(); | |||
assertThat(server.getNumberRequests()).isEqualTo(3); | |||
} | |||
@Test | |||
public void testCacheFirst() { | |||
loader = new WSLoader(LoadStrategy.CACHE_FIRST, cache, client, userProps); | |||
makeRequests(); | |||
assertThat(server.getNumberRequests()).isEqualTo(1); | |||
} | |||
@Test | |||
public void testServerFirst() { | |||
loader = new WSLoader(LoadStrategy.SERVER_FIRST, cache, client, userProps); | |||
makeRequests(); | |||
assertThat(server.getNumberRequests()).isEqualTo(3); | |||
} | |||
@Test | |||
public void testCacheStrategyDisabled() { | |||
loader = new WSLoader(LoadStrategy.SERVER_ONLY, cache, client, userProps); | |||
makeRequests(); | |||
assertThat(server.getNumberRequests()).isEqualTo(3); | |||
} | |||
private void makeRequests() { | |||
server.setMockResponseData(RESPONSE_STRING); | |||
assertThat(loader.loadString("/foo").get()).isEqualTo(RESPONSE_STRING); | |||
assertThat(loader.loadString("/foo").get()).isEqualTo(RESPONSE_STRING); | |||
assertThat(loader.loadString("/foo").get()).isEqualTo(RESPONSE_STRING); | |||
} | |||
} |
@@ -19,132 +19,126 @@ | |||
*/ | |||
package org.sonar.batch.report; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.slf4j.Logger; | |||
import org.mockito.Mockito; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.batch.bootstrap.ProjectDefinition; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.api.platform.Server; | |||
import org.sonar.api.utils.TempFolder; | |||
import org.sonar.api.utils.log.LogTester; | |||
import org.sonar.api.utils.log.LoggerLevel; | |||
import org.sonar.batch.analysis.DefaultAnalysisMode; | |||
import org.sonar.batch.bootstrap.ServerClient; | |||
import org.sonar.batch.scan.ImmutableProjectReactor; | |||
import org.sonar.test.JsonAssert; | |||
import org.sonarqube.ws.client.WsClient; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.charset.StandardCharsets; | |||
import java.nio.file.Files; | |||
import java.nio.file.Path; | |||
import static org.apache.commons.io.FileUtils.readFileToString; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyNoMoreInteractions; | |||
import static org.mockito.Mockito.when; | |||
public class ReportPublisherTest { | |||
private DefaultAnalysisMode mode; | |||
private ImmutableProjectReactor reactor; | |||
private Settings settings; | |||
private ProjectDefinition root; | |||
@Rule | |||
public LogTester logTester = new LogTester(); | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
DefaultAnalysisMode mode = mock(DefaultAnalysisMode.class); | |||
Settings settings = new Settings(); | |||
WsClient wsClient = mock(WsClient.class, Mockito.RETURNS_DEEP_STUBS); | |||
ImmutableProjectReactor reactor = mock(ImmutableProjectReactor.class); | |||
ProjectDefinition root; | |||
AnalysisContextReportPublisher contextPublisher = mock(AnalysisContextReportPublisher.class); | |||
@Before | |||
public void setUp() { | |||
settings = new Settings(); | |||
mode = mock(DefaultAnalysisMode.class); | |||
reactor = mock(ImmutableProjectReactor.class); | |||
root = ProjectDefinition.create().setKey("struts").setWorkDir(temp.getRoot()); | |||
when(reactor.getRoot()).thenReturn(root); | |||
when(wsClient.wsConnector().baseUrl()).thenReturn("https://localhost"); | |||
} | |||
@Test | |||
public void should_log_successful_analysis() { | |||
settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver/"); | |||
ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode, | |||
mock(TempFolder.class), new ReportPublisherStep[0]); | |||
Logger logger = mock(Logger.class); | |||
job.logSuccess(logger, null); | |||
verify(logger).info("ANALYSIS SUCCESSFUL, you can browse {}", "http://myserver/dashboard/index/struts"); | |||
verify(logger).info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report."); | |||
verifyNoMoreInteractions(logger); | |||
public void log_and_dump_information_about_report_uploading() throws IOException { | |||
ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); | |||
underTest.logSuccess("TASK-123"); | |||
assertThat(logTester.logs(LoggerLevel.INFO)) | |||
.contains("ANALYSIS SUCCESSFUL, you can browse https://localhost/dashboard/index/struts") | |||
.contains("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report") | |||
.contains("More about the report processing at https://localhost/api/ce/task?id=TASK-123"); | |||
File detailsFile = new File(temp.getRoot(), "analysis-details.json"); | |||
JsonAssert.assertJson(readFileToString(detailsFile)).isSimilarTo("{" + | |||
"\"projectKey\": \"struts\"," + | |||
"\"dashboardUrl\": \"https://localhost/dashboard/index/struts\"," + | |||
"\"ceTaskId\": \"TASK-123\"," + | |||
"\"ceTaskUrl\": \"https://localhost/api/ce/task?id=TASK-123\"" + | |||
"}" | |||
); | |||
} | |||
@Test | |||
public void should_log_successful_analysis_with_ce_task() { | |||
settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver/"); | |||
ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode, | |||
mock(TempFolder.class), new ReportPublisherStep[0]); | |||
Logger logger = mock(Logger.class); | |||
job.logSuccess(logger, "abc123"); | |||
verify(logger).info("ANALYSIS SUCCESSFUL, you can browse {}", "http://myserver/dashboard/index/struts"); | |||
verify(logger).info("Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report."); | |||
verify(logger).info("More about the report processing at {}", "http://myserver/api/ce/task?id=abc123"); | |||
verifyNoMoreInteractions(logger); | |||
public void log_public_url_if_defined() throws IOException { | |||
settings.setProperty(CoreProperties.SERVER_BASE_URL, "https://publicserver/sonarqube"); | |||
ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); | |||
underTest.logSuccess("TASK-123"); | |||
assertThat(logTester.logs(LoggerLevel.INFO)) | |||
.contains("ANALYSIS SUCCESSFUL, you can browse https://publicserver/sonarqube/dashboard/index/struts") | |||
.contains("More about the report processing at https://publicserver/sonarqube/api/ce/task?id=TASK-123"); | |||
File detailsFile = new File(temp.getRoot(), "analysis-details.json"); | |||
JsonAssert.assertJson(readFileToString(detailsFile)).isSimilarTo("{" + | |||
"\"projectKey\": \"struts\"," + | |||
"\"dashboardUrl\": \"https://publicserver/sonarqube/dashboard/index/struts\"," + | |||
"\"ceTaskId\": \"TASK-123\"," + | |||
"\"ceTaskUrl\": \"https://publicserver/sonarqube/api/ce/task?id=TASK-123\"" + | |||
"}" | |||
); | |||
} | |||
@Test | |||
public void should_write_json_file() throws IOException { | |||
settings.setProperty(CoreProperties.SERVER_BASE_URL, "http://myserver/"); | |||
ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode, | |||
mock(TempFolder.class), new ReportPublisherStep[0]); | |||
job.logSuccess(mock(Logger.class), "abc123"); | |||
File jsonFile = new File(temp.getRoot(), "analysis-details.json"); | |||
assertThat(jsonFile).exists(); | |||
public void log_but_not_dump_information_when_report_is_not_uploaded() { | |||
ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); | |||
String jsonFileContent = new String(Files.readAllBytes(jsonFile.toPath()), StandardCharsets.UTF_8); | |||
String expectedContent = "\"dashboardUrl\":\"http://myserver/dashboard/index/struts\",\"ceTaskUrl\":\"http://myserver/api/ce/task?id=abc123\""; | |||
assertThat(jsonFileContent).contains(expectedContent); | |||
underTest.logSuccess(/* report not uploaded, no server task */null); | |||
} | |||
@Test | |||
public void should_log_successful_issues_analysis() { | |||
when(mode.isIssues()).thenReturn(true); | |||
ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode, | |||
mock(TempFolder.class), new ReportPublisherStep[0]); | |||
assertThat(logTester.logs(LoggerLevel.INFO)) | |||
.contains("ANALYSIS SUCCESSFUL") | |||
.doesNotContain("dashboard/index"); | |||
Logger logger = mock(Logger.class); | |||
job.logSuccess(logger, null); | |||
verify(logger).info("ANALYSIS SUCCESSFUL"); | |||
verifyNoMoreInteractions(logger); | |||
File detailsFile = new File(temp.getRoot(), "analysis-details.json"); | |||
assertThat(detailsFile).doesNotExist(); | |||
} | |||
@Test | |||
public void should_not_delete_report() throws IOException { | |||
settings.setProperty("sonar.verbose", true); | |||
public void should_not_delete_report_if_property_is_set() throws IOException { | |||
settings.setProperty("sonar.batch.keepReport", true); | |||
Path reportDir = temp.getRoot().toPath().resolve("batch-report"); | |||
Files.createDirectory(reportDir); | |||
ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode, | |||
mock(TempFolder.class), new ReportPublisherStep[0]); | |||
ReportPublisher underTest = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); | |||
job.start(); | |||
job.stop(); | |||
underTest.start(); | |||
underTest.stop(); | |||
assertThat(reportDir).isDirectory(); | |||
} | |||
@Test | |||
public void should_delete_report() throws IOException { | |||
public void should_delete_report_by_default() throws IOException { | |||
Path reportDir = temp.getRoot().toPath().resolve("batch-report"); | |||
Files.createDirectory(reportDir); | |||
ReportPublisher job = new ReportPublisher(settings, mock(ServerClient.class), mock(Server.class), mock(AnalysisContextReportPublisher.class), reactor, mode, | |||
mock(TempFolder.class), new ReportPublisherStep[0]); | |||
ReportPublisher job = new ReportPublisher(settings, wsClient, contextPublisher, reactor, mode, mock(TempFolder.class), new ReportPublisherStep[0]); | |||
job.start(); | |||
job.stop(); |
@@ -19,25 +19,22 @@ | |||
*/ | |||
package org.sonar.batch.repository; | |||
import org.sonar.api.utils.MessageException; | |||
import com.google.common.io.Resources; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.URI; | |||
import org.sonar.api.utils.HttpDownloader.HttpException; | |||
import org.apache.commons.lang.mutable.MutableBoolean; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.batch.cache.WSLoader; | |||
import org.sonar.batch.cache.WSLoaderResult; | |||
import org.sonarqube.ws.WsBatch.WsProjectResponse; | |||
import org.sonarqube.ws.client.HttpException; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Matchers.anyString; | |||
import static org.mockito.Mockito.mock; | |||
@@ -56,7 +53,7 @@ public class DefaultProjectRepositoriesLoaderTest { | |||
public void prepare() throws IOException { | |||
wsLoader = mock(WSLoader.class); | |||
InputStream is = mockData(); | |||
when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<InputStream>(is, true)); | |||
when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, true)); | |||
loader = new DefaultProjectRepositoriesLoader(wsLoader); | |||
} | |||
@@ -72,13 +69,13 @@ public class DefaultProjectRepositoriesLoaderTest { | |||
InputStream is = mock(InputStream.class); | |||
when(is.read()).thenThrow(IOException.class); | |||
when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<InputStream>(is, false)); | |||
when(wsLoader.loadStream(anyString())).thenReturn(new WSLoaderResult<>(is, false)); | |||
loader.load(PROJECT_KEY, false, null); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void failFastHttpError() { | |||
HttpException http = new HttpException(URI.create("uri"), 403); | |||
HttpException http = new HttpException("url", 403, "Forbidden"); | |||
IllegalStateException e = new IllegalStateException("http error", http); | |||
when(wsLoader.loadStream(anyString())).thenThrow(e); | |||
loader.load(PROJECT_KEY, false, null); | |||
@@ -89,7 +86,7 @@ public class DefaultProjectRepositoriesLoaderTest { | |||
thrown.expect(MessageException.class); | |||
thrown.expectMessage("http error"); | |||
HttpException http = new HttpException(URI.create("uri"), 403); | |||
HttpException http = new HttpException("uri", 403, "Forbidden"); | |||
MessageException e = MessageException.of("http error", http); | |||
when(wsLoader.loadStream(anyString())).thenThrow(e); | |||
loader.load(PROJECT_KEY, false, null); |
@@ -13,20 +13,30 @@ | |||
<name>SonarQube :: Web Service</name> | |||
<description>Protocol Buffers specification of Web Services</description> | |||
<properties> | |||
<sonar.exclusions>target/generated-sources/**/*</sonar.exclusions> | |||
<sonar.test.exclusions>target/generated-test-sources/**/*</sonar.test.exclusions> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.google.protobuf</groupId> | |||
<artifactId>protobuf-java</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.guava</groupId> | |||
<artifactId>guava</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.squareup.okhttp</groupId> | |||
<artifactId>okhttp</artifactId> | |||
<version>${okhttp.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.code.findbugs</groupId> | |||
<artifactId>jsr305</artifactId> | |||
<scope>provided</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.guava</groupId> | |||
<artifactId>guava</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-io</groupId> | |||
<artifactId>commons-io</artifactId> | |||
@@ -36,32 +46,14 @@ | |||
<dependency> | |||
<groupId>${project.groupId}</groupId> | |||
<artifactId>sonar-testing-harness</artifactId> | |||
<version>${project.version}</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>javax.servlet</groupId> | |||
<artifactId>javax.servlet-api</artifactId> | |||
<groupId>com.squareup.okhttp</groupId> | |||
<artifactId>mockwebserver</artifactId> | |||
<version>${okhttp.version}</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<!-- Jetty dependencies --> | |||
<dependency> | |||
<groupId>org.eclipse.jetty</groupId> | |||
<artifactId>jetty-server</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.eclipse.jetty</groupId> | |||
<artifactId>test-jetty-servlet</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.googlecode.json-simple</groupId> | |||
<artifactId>json-simple</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.github.kevinsawicki</groupId> | |||
<artifactId>http-request</artifactId> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -27,7 +27,7 @@ import java.util.Locale; | |||
import java.util.Map; | |||
/** | |||
* @since 3.1 | |||
* @since 5.3 | |||
*/ | |||
public final class MediaTypes { | |||
@@ -35,6 +35,7 @@ public final class MediaTypes { | |||
public static final String XML = "application/xml"; | |||
public static final String TXT = "text/plain"; | |||
public static final String PROTOBUF = "application/x-protobuf"; | |||
public static final String ZIP = "application/zip"; | |||
public static final String DEFAULT = "application/octet-stream"; | |||
private static final Map<String, String> MAP = new ImmutableMap.Builder<String, String>() |
@@ -0,0 +1,75 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import javax.annotation.Nullable; | |||
import org.sonarqube.ws.MediaTypes; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static com.google.common.base.Strings.isNullOrEmpty; | |||
import static java.util.Objects.requireNonNull; | |||
abstract class BaseRequest<SELF extends BaseRequest> implements WsRequest { | |||
private final String path; | |||
private String mediaType = MediaTypes.JSON; | |||
// keep the same order -> do not use HashMap | |||
private final Map<String, String> params = new LinkedHashMap<>(); | |||
BaseRequest(String path) { | |||
this.path = path; | |||
} | |||
@Override | |||
public String getPath() { | |||
return path; | |||
} | |||
@Override | |||
public String getMediaType() { | |||
return mediaType; | |||
} | |||
/** | |||
* Expected media type of response. Default is {@link MediaTypes#JSON}. | |||
*/ | |||
public SELF setMediaType(String s) { | |||
requireNonNull(s, "media type of response cannot be null"); | |||
this.mediaType = s; | |||
return (SELF) this; | |||
} | |||
public SELF setParam(String key, @Nullable Object value) { | |||
checkArgument(!isNullOrEmpty(key), "a WS parameter key cannot be null"); | |||
if (value != null) { | |||
this.params.put(key, value.toString()); | |||
} | |||
return (SELF) this; | |||
} | |||
@Override | |||
public Map<String, String> getParams() { | |||
return params; | |||
} | |||
} |
@@ -0,0 +1,63 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import com.google.protobuf.Message; | |||
import com.google.protobuf.Parser; | |||
import java.io.InputStream; | |||
import org.sonarqube.ws.MediaTypes; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static com.google.common.base.Strings.isNullOrEmpty; | |||
public abstract class BaseService { | |||
private final WsConnector wsConnector; | |||
private final String controller; | |||
public BaseService(WsConnector wsConnector, String controllerPath) { | |||
checkArgument(!isNullOrEmpty(controllerPath)); | |||
this.wsConnector = wsConnector; | |||
this.controller = controllerPath; | |||
} | |||
protected <T extends Message> T call(BaseRequest request, Parser<T> parser) { | |||
request.setMediaType(MediaTypes.PROTOBUF); | |||
WsResponse response = wsConnector.call(request); | |||
return convert(response, parser); | |||
} | |||
protected WsResponse call(WsRequest request) { | |||
return wsConnector.call(request); | |||
} | |||
public <T extends Message> T convert(WsResponse response, Parser<T> parser) { | |||
try (InputStream byteStream = response.getContentStream()) { | |||
// HTTP header "Content-Type" is not verified. It may be different than protobuf. | |||
return parser.parseFrom(byteStream); | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Fail to parse protobuf response of " + response.getRequestUrl(), e); | |||
} | |||
} | |||
protected String path(String action) { | |||
return String.format("%s/%s", controller, action); | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
/** | |||
* @since 5.3 | |||
*/ | |||
public class GetRequest extends BaseRequest<GetRequest> { | |||
public GetRequest(String path) { | |||
super(path); | |||
} | |||
@Override | |||
public Method getMethod() { | |||
return Method.GET; | |||
} | |||
} |
@@ -17,73 +17,192 @@ | |||
* 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.sonarqube.ws.client; | |||
import com.google.protobuf.Message; | |||
import com.google.protobuf.Parser; | |||
import com.squareup.okhttp.Call; | |||
import com.squareup.okhttp.Credentials; | |||
import com.squareup.okhttp.Headers; | |||
import com.squareup.okhttp.HttpUrl; | |||
import com.squareup.okhttp.Interceptor; | |||
import com.squareup.okhttp.MediaType; | |||
import com.squareup.okhttp.MultipartBuilder; | |||
import com.squareup.okhttp.OkHttpClient; | |||
import com.squareup.okhttp.Request; | |||
import com.squareup.okhttp.RequestBody; | |||
import com.squareup.okhttp.Response; | |||
import java.io.IOException; | |||
import java.net.Proxy; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.concurrent.TimeUnit; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static com.google.common.base.Strings.isNullOrEmpty; | |||
import static com.google.common.base.Strings.nullToEmpty; | |||
import static java.lang.String.format; | |||
public class HttpConnector implements WsConnector { | |||
public static final int DEFAULT_CONNECT_TIMEOUT_MILLISECONDS = 30000; | |||
public static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60000; | |||
public static final int DEFAULT_CONNECT_TIMEOUT_MILLISECONDS = 30_000; | |||
public static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60_000; | |||
/** | |||
* Visibility relaxed for unit tests | |||
* Base URL with trailing slash, for instance "https://localhost/sonarqube/". | |||
* It is required for further usage of {@link HttpUrl#resolve(String)}. | |||
*/ | |||
final HttpRequestFactory requestFactory; | |||
private final HttpUrl baseUrl; | |||
private final String userAgent; | |||
private final String credentials; | |||
private final String proxyCredentials; | |||
private final OkHttpClient okHttpClient = new OkHttpClient(); | |||
private HttpConnector(Builder builder) { | |||
this.requestFactory = new HttpRequestFactory(builder.url) | |||
.setLogin(builder.login) | |||
.setPassword(builder.password) | |||
.setProxyHost(builder.proxyHost) | |||
.setProxyPort(builder.proxyPort) | |||
.setProxyLogin(builder.proxyLogin) | |||
.setProxyPassword(builder.proxyPassword) | |||
.setConnectTimeoutInMilliseconds(builder.connectTimeoutMs) | |||
.setReadTimeoutInMilliseconds(builder.readTimeoutMs); | |||
this.baseUrl = HttpUrl.parse(builder.url.endsWith("/") ? builder.url : format("%s/", builder.url)); | |||
this.userAgent = builder.userAgent; | |||
if (isNullOrEmpty(builder.login)) { | |||
// no login nor access token | |||
this.credentials = null; | |||
} else { | |||
// password is null when login represents an access token. In this case | |||
// the Basic credentials consider an empty password. | |||
this.credentials = Credentials.basic(builder.login, nullToEmpty(builder.password)); | |||
} | |||
if (builder.proxy != null) { | |||
this.okHttpClient.setProxy(builder.proxy); | |||
} | |||
// proxy credentials can be used on system-wide proxies, so even if builder.proxy is null | |||
if (isNullOrEmpty(builder.proxyLogin)) { | |||
this.proxyCredentials = null; | |||
} else { | |||
this.proxyCredentials = Credentials.basic(builder.proxyLogin, nullToEmpty(builder.proxyPassword)); | |||
} | |||
this.okHttpClient.setConnectTimeout(builder.connectTimeoutMs, TimeUnit.MILLISECONDS); | |||
this.okHttpClient.setReadTimeout(builder.readTimeoutMs, TimeUnit.MILLISECONDS); | |||
this.okHttpClient.interceptors().addAll(builder.interceptors); | |||
} | |||
@Override | |||
public String execute(WsRequest wsRequest) { | |||
return requestFactory.execute(wsRequest); | |||
public String baseUrl() { | |||
return baseUrl.url().toExternalForm(); | |||
} | |||
public OkHttpClient okHttpClient() { | |||
return okHttpClient; | |||
} | |||
@CheckForNull | |||
public String userAgent() { | |||
return userAgent; | |||
} | |||
@CheckForNull | |||
public String credentials() { | |||
return credentials; | |||
} | |||
@Override | |||
public <T extends Message> T execute(WsRequest wsRequest, Parser<T> protobufParser) { | |||
return requestFactory.execute(wsRequest, protobufParser); | |||
public WsResponse call(WsRequest httpRequest) { | |||
if (httpRequest instanceof GetRequest) { | |||
return get((GetRequest) httpRequest); | |||
} | |||
if (httpRequest instanceof PostRequest) { | |||
return post((PostRequest) httpRequest); | |||
} | |||
throw new IllegalArgumentException(format("Unsupported implementation: %s", httpRequest.getClass())); | |||
} | |||
/** | |||
* Create a builder of {@link WsClient}s. | |||
*/ | |||
public static Builder newHttpConnector() { | |||
return new Builder(); | |||
private WsResponse get(GetRequest getRequest) { | |||
HttpUrl.Builder urlBuilder = prepareUrlBuilder(getRequest); | |||
Request.Builder okRequestBuilder = prepareOkRequestBuilder(getRequest, urlBuilder).get(); | |||
return doCall(okRequestBuilder.build()); | |||
} | |||
/** | |||
* Create a client with default configuration. Use {@link #newHttpConnector()} to define | |||
* a custom configuration (credentials, HTTP proxy, HTTP timeouts). | |||
*/ | |||
public static HttpConnector newDefaultHttpConnector(String serverUrl) { | |||
return newHttpConnector().url(serverUrl).build(); | |||
private WsResponse post(PostRequest postRequest) { | |||
HttpUrl.Builder urlBuilder = prepareUrlBuilder(postRequest); | |||
Request.Builder okRequestBuilder = prepareOkRequestBuilder(postRequest, urlBuilder); | |||
Map<String, PostRequest.Part> parts = postRequest.getParts(); | |||
if (parts.isEmpty()) { | |||
okRequestBuilder.post(RequestBody.create(null, "")); | |||
} else { | |||
MultipartBuilder body = new MultipartBuilder().type(MultipartBuilder.FORM); | |||
for (Map.Entry<String, PostRequest.Part> param : parts.entrySet()) { | |||
PostRequest.Part part = param.getValue(); | |||
body.addPart( | |||
Headers.of("Content-Disposition", format("form-data; name=\"%s\"", param.getKey())), | |||
RequestBody.create(MediaType.parse(part.getMediaType()), part.getFile())); | |||
} | |||
okRequestBuilder.post(body.build()); | |||
} | |||
return doCall(okRequestBuilder.build()); | |||
} | |||
private HttpUrl.Builder prepareUrlBuilder(WsRequest wsRequest) { | |||
String path = wsRequest.getPath(); | |||
HttpUrl.Builder urlBuilder = baseUrl | |||
.resolve(path.startsWith("/") ? path.replaceAll("^(/)+", "") : path) | |||
.newBuilder(); | |||
for (Map.Entry<String, String> param : wsRequest.getParams().entrySet()) { | |||
urlBuilder.addQueryParameter(param.getKey(), param.getValue()); | |||
} | |||
return urlBuilder; | |||
} | |||
private Request.Builder prepareOkRequestBuilder(WsRequest getRequest, HttpUrl.Builder urlBuilder) { | |||
Request.Builder okHttpRequestBuilder = new Request.Builder() | |||
.url(urlBuilder.build()) | |||
.addHeader("Accept", getRequest.getMediaType()) | |||
.addHeader("Accept-Charset", "UTF-8"); | |||
if (credentials != null) { | |||
okHttpRequestBuilder.header("Authorization", credentials); | |||
} | |||
if (proxyCredentials != null) { | |||
okHttpRequestBuilder.header("Proxy-Authorization", proxyCredentials); | |||
} | |||
if (userAgent != null) { | |||
okHttpRequestBuilder.addHeader("User-Agent", userAgent); | |||
} | |||
return okHttpRequestBuilder; | |||
} | |||
private HttpResponse doCall(Request okRequest) { | |||
Call call = okHttpClient.newCall(okRequest); | |||
try { | |||
Response okResponse = call.execute(); | |||
if (!okResponse.isSuccessful()) { | |||
throw new HttpException(okRequest.urlString(), okResponse.code(), okResponse.message()); | |||
} | |||
return new HttpResponse(okResponse); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Fail to request " + okRequest.urlString(), e); | |||
} | |||
} | |||
public static class Builder { | |||
private String url; | |||
private String userAgent; | |||
private String login; | |||
private String password; | |||
private String url; | |||
private String proxyHost; | |||
private Proxy proxy; | |||
private String proxyLogin; | |||
private String proxyPassword; | |||
private int proxyPort = 0; | |||
private int connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MILLISECONDS; | |||
private int readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLISECONDS; | |||
private final List<Interceptor> interceptors = new ArrayList<>(); | |||
private Builder() { | |||
/** | |||
* Optional User Agent | |||
*/ | |||
public Builder userAgent(@Nullable String userAgent) { | |||
this.userAgent = userAgent; | |||
return this; | |||
} | |||
/** | |||
@@ -95,43 +214,26 @@ public class HttpConnector implements WsConnector { | |||
} | |||
/** | |||
* Optional login, for example "admin" | |||
* Optional login/password, for example "admin" | |||
*/ | |||
public Builder login(@Nullable String login) { | |||
public Builder credentials(@Nullable String login, @Nullable String password) { | |||
this.login = login; | |||
return this; | |||
} | |||
/** | |||
* Optional password related to {@link #login(String)}, for example "admin" | |||
*/ | |||
public Builder password(@Nullable String password) { | |||
this.password = password; | |||
return this; | |||
} | |||
/** | |||
* Host and port of the optional HTTP proxy | |||
* Optional access token, for example {@code "ABCDE"}. Alternative to {@link #credentials(String, String)} | |||
*/ | |||
public Builder proxy(@Nullable String proxyHost, int proxyPort) { | |||
this.proxyHost = proxyHost; | |||
this.proxyPort = proxyPort; | |||
return this; | |||
} | |||
public Builder proxyLogin(@Nullable String proxyLogin) { | |||
this.proxyLogin = proxyLogin; | |||
return this; | |||
} | |||
public Builder proxyPassword(@Nullable String proxyPassword) { | |||
this.proxyPassword = proxyPassword; | |||
public Builder token(@Nullable String token) { | |||
this.login = token; | |||
this.password = null; | |||
return this; | |||
} | |||
/** | |||
* Sets a specified timeout value, in milliseconds, to be used when opening HTTP connection. | |||
* A timeout of zero is interpreted as an infinite timeout. Default value is {@link HttpConnector#DEFAULT_CONNECT_TIMEOUT_MILLISECONDS} | |||
* A timeout of zero is interpreted as an infinite timeout. Default value is {@link #DEFAULT_CONNECT_TIMEOUT_MILLISECONDS} | |||
*/ | |||
public Builder connectTimeoutMilliseconds(int i) { | |||
this.connectTimeoutMs = i; | |||
@@ -140,20 +242,35 @@ public class HttpConnector implements WsConnector { | |||
/** | |||
* Sets the read timeout to a specified timeout, in milliseconds. | |||
* A timeout of zero is interpreted as an infinite timeout. Default value is {@link HttpConnector#DEFAULT_READ_TIMEOUT_MILLISECONDS} | |||
* A timeout of zero is interpreted as an infinite timeout. Default value is {@link #DEFAULT_READ_TIMEOUT_MILLISECONDS} | |||
*/ | |||
public Builder readTimeoutMilliseconds(int i) { | |||
this.readTimeoutMs = i; | |||
return this; | |||
} | |||
public Builder proxy(@Nullable Proxy proxy) { | |||
this.proxy = proxy; | |||
return this; | |||
} | |||
public Builder proxyCredentials(@Nullable String proxyLogin, @Nullable String proxyPassword) { | |||
this.proxyLogin = proxyLogin; | |||
this.proxyPassword = proxyPassword; | |||
return this; | |||
} | |||
/** | |||
* Build a new client | |||
* Adds a OkHttp interceptor, for example to log request URLs or response errors. | |||
* See https://github.com/square/okhttp/wiki/Interceptors | |||
*/ | |||
public Builder interceptor(Interceptor interceptor) { | |||
this.interceptors.add(interceptor); | |||
return this; | |||
} | |||
public HttpConnector build() { | |||
if (url == null || "".equals(url)) { | |||
throw new IllegalStateException("Server URL must be set"); | |||
} | |||
checkArgument(!isNullOrEmpty(url), "Server URL is not defined"); | |||
return new HttpConnector(this); | |||
} | |||
@@ -20,24 +20,27 @@ | |||
package org.sonarqube.ws.client; | |||
/** | |||
* @since 3.6 | |||
* @since 5.3 | |||
*/ | |||
public class HttpException extends RuntimeException { | |||
private final String url; | |||
private final int status; | |||
private final int code; | |||
public HttpException(String url, int status, String message) { | |||
super(String.format("Error %d on %s : %s", status, url, message)); | |||
public HttpException(String url, int code, String message) { | |||
super(String.format("Error %d on %s : %s", code, url, message)); | |||
this.url = url; | |||
this.status = status; | |||
this.code = code; | |||
} | |||
public String url() { | |||
return url; | |||
} | |||
public int status() { | |||
return status; | |||
/** | |||
* @see java.net.HttpURLConnection constants | |||
*/ | |||
public int code() { | |||
return code; | |||
} | |||
} |
@@ -1,229 +0,0 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import com.github.kevinsawicki.http.HttpRequest; | |||
import com.google.common.base.Throwables; | |||
import com.google.protobuf.InvalidProtocolBufferException; | |||
import com.google.protobuf.Message; | |||
import com.google.protobuf.Parser; | |||
import java.io.InputStream; | |||
import java.util.Arrays; | |||
import javax.annotation.Nullable; | |||
import org.sonarqube.ws.MediaTypes; | |||
import static java.net.HttpURLConnection.HTTP_CREATED; | |||
import static java.net.HttpURLConnection.HTTP_NO_CONTENT; | |||
import static java.net.HttpURLConnection.HTTP_OK; | |||
/** | |||
* Not an API. Please do not use this class, except maybe for unit tests. | |||
*/ | |||
public class HttpRequestFactory { | |||
private static final int[] RESPONSE_SUCCESS = {HTTP_OK, HTTP_CREATED, HTTP_NO_CONTENT}; | |||
private final String baseUrl; | |||
private String login; | |||
private String password; | |||
private String proxyHost; | |||
private String proxyLogin; | |||
private String proxyPassword; | |||
private int proxyPort; | |||
private int connectTimeoutInMilliseconds; | |||
private int readTimeoutInMilliseconds; | |||
public HttpRequestFactory(String baseUrl) { | |||
this.baseUrl = baseUrl; | |||
} | |||
public HttpRequestFactory setLogin(@Nullable String login) { | |||
this.login = login; | |||
return this; | |||
} | |||
public HttpRequestFactory setPassword(@Nullable String password) { | |||
this.password = password; | |||
return this; | |||
} | |||
public HttpRequestFactory setProxyHost(@Nullable String proxyHost) { | |||
this.proxyHost = proxyHost; | |||
return this; | |||
} | |||
public HttpRequestFactory setProxyLogin(@Nullable String proxyLogin) { | |||
this.proxyLogin = proxyLogin; | |||
return this; | |||
} | |||
public HttpRequestFactory setProxyPassword(@Nullable String proxyPassword) { | |||
this.proxyPassword = proxyPassword; | |||
return this; | |||
} | |||
public HttpRequestFactory setProxyPort(int proxyPort) { | |||
this.proxyPort = proxyPort; | |||
return this; | |||
} | |||
public HttpRequestFactory setConnectTimeoutInMilliseconds(int connectTimeoutInMilliseconds) { | |||
this.connectTimeoutInMilliseconds = connectTimeoutInMilliseconds; | |||
return this; | |||
} | |||
public HttpRequestFactory setReadTimeoutInMilliseconds(int readTimeoutInMilliseconds) { | |||
this.readTimeoutInMilliseconds = readTimeoutInMilliseconds; | |||
return this; | |||
} | |||
public String getBaseUrl() { | |||
return baseUrl; | |||
} | |||
public String getLogin() { | |||
return login; | |||
} | |||
public String getPassword() { | |||
return password; | |||
} | |||
public String getProxyHost() { | |||
return proxyHost; | |||
} | |||
public String getProxyLogin() { | |||
return proxyLogin; | |||
} | |||
public String getProxyPassword() { | |||
return proxyPassword; | |||
} | |||
public int getProxyPort() { | |||
return proxyPort; | |||
} | |||
public int getConnectTimeoutInMilliseconds() { | |||
return connectTimeoutInMilliseconds; | |||
} | |||
public int getReadTimeoutInMilliseconds() { | |||
return readTimeoutInMilliseconds; | |||
} | |||
public String execute(WsRequest wsRequest) { | |||
HttpRequest httpRequest = wsRequestToHttpRequest(wsRequest); | |||
return execute(httpRequest); | |||
} | |||
public <T extends Message> T execute(WsRequest wsRequest, Parser<T> protobufParser) { | |||
HttpRequest httpRequest = wsRequestToHttpRequest(wsRequest); | |||
InputStream response = executeWithStream(httpRequest); | |||
try { | |||
return protobufParser.parseFrom(response); | |||
} catch (InvalidProtocolBufferException e) { | |||
Throwables.propagate(e); | |||
} | |||
throw new IllegalStateException("Uncatched exception when parsing protobuf response"); | |||
} | |||
private HttpRequest wsRequestToHttpRequest(WsRequest wsRequest) { | |||
HttpRequest httpRequest = WsRequest.Method.GET.equals(wsRequest.getMethod()) | |||
? HttpRequest.get(buildUrl(wsRequest.getEndpoint()), wsRequest.getParams(), true) | |||
: HttpRequest.post(buildUrl(wsRequest.getEndpoint()), wsRequest.getParams(), true); | |||
httpRequest = prepare(httpRequest); | |||
switch (wsRequest.getMediaType()) { | |||
case PROTOBUF: | |||
httpRequest.accept(MediaTypes.PROTOBUF); | |||
break; | |||
case JSON: | |||
httpRequest.accept(MediaTypes.JSON); | |||
break; | |||
case TEXT: | |||
httpRequest.accept(MediaTypes.TXT); | |||
break; | |||
default: | |||
httpRequest.accept(MediaTypes.DEFAULT); | |||
break; | |||
} | |||
return httpRequest; | |||
} | |||
private String buildUrl(String part) { | |||
StringBuilder url = new StringBuilder(); | |||
url.append(baseUrl); | |||
if (!part.startsWith("/")) { | |||
url.append('/'); | |||
} | |||
url.append(part); | |||
return url.toString(); | |||
} | |||
private static String execute(HttpRequest request) { | |||
try { | |||
checkSuccess(request); | |||
return request.body(HttpRequest.CHARSET_UTF8); | |||
} catch (HttpRequest.HttpRequestException e) { | |||
throw new IllegalStateException("Fail to request " + request.url(), e); | |||
} | |||
} | |||
private static InputStream executeWithStream(HttpRequest request) { | |||
try { | |||
checkSuccess(request); | |||
return request.stream(); | |||
} catch (HttpRequest.HttpRequestException e) { | |||
throw new IllegalStateException("Fail to request " + request.url(), e); | |||
} | |||
} | |||
private static void checkSuccess(HttpRequest request) { | |||
boolean isSuccess = Arrays.binarySearch(RESPONSE_SUCCESS, request.code()) >= 0; | |||
if (!isSuccess) { | |||
throw new HttpException(request.url().toString(), request.code(), request.body()); | |||
} | |||
} | |||
private HttpRequest prepare(HttpRequest request) { | |||
if (proxyHost != null) { | |||
request.useProxy(proxyHost, proxyPort); | |||
if (proxyLogin != null) { | |||
request.proxyBasic(proxyLogin, proxyPassword); | |||
} | |||
} | |||
request | |||
.acceptGzipEncoding() | |||
.uncompress(true) | |||
.acceptJson() | |||
.acceptCharset(HttpRequest.CHARSET_UTF8) | |||
.connectTimeout(connectTimeoutInMilliseconds) | |||
.readTimeout(readTimeoutInMilliseconds) | |||
.trustAllCerts() | |||
.trustAllHosts(); | |||
if (login != null) { | |||
request.basic(login, password); | |||
} | |||
return request; | |||
} | |||
} |
@@ -0,0 +1,90 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import com.squareup.okhttp.Response; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.Reader; | |||
import static java.net.HttpURLConnection.HTTP_NO_CONTENT; | |||
class HttpResponse implements WsResponse { | |||
private final Response okResponse; | |||
HttpResponse(Response okResponse) { | |||
this.okResponse = okResponse; | |||
} | |||
@Override | |||
public String getRequestUrl() { | |||
return okResponse.request().urlString(); | |||
} | |||
@Override | |||
public boolean hasContent() { | |||
return okResponse.code() != HTTP_NO_CONTENT; | |||
} | |||
@Override | |||
public String getContentType() { | |||
return okResponse.header("Content-Type"); | |||
} | |||
/** | |||
* Get stream of bytes | |||
*/ | |||
@Override | |||
public InputStream getContentStream() { | |||
try { | |||
return okResponse.body().byteStream(); | |||
} catch (IOException e) { | |||
throw fail(e); | |||
} | |||
} | |||
/** | |||
* Get stream of characters, decoded with the charset | |||
* of the Content-Type header. If that header is either absent or lacks a | |||
* charset, this will attempt to decode the response body as UTF-8. | |||
*/ | |||
@Override | |||
public Reader getContentReader() { | |||
try { | |||
return okResponse.body().charStream(); | |||
} catch (IOException e) { | |||
throw fail(e); | |||
} | |||
} | |||
@Override | |||
public String getContent() { | |||
try { | |||
return okResponse.body().string(); | |||
} catch (IOException e) { | |||
throw fail(e); | |||
} | |||
} | |||
private RuntimeException fail(Exception e) { | |||
throw new IllegalStateException("Fail to read response of " + getRequestUrl(), e); | |||
} | |||
} |
@@ -0,0 +1,89 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import org.sonarqube.ws.client.ce.ComputeEngineService; | |||
import org.sonarqube.ws.client.component.ComponentsService; | |||
import org.sonarqube.ws.client.issue.IssuesService; | |||
import org.sonarqube.ws.client.permission.PermissionsService; | |||
import org.sonarqube.ws.client.qualityprofile.QualityProfilesService; | |||
import org.sonarqube.ws.client.usertoken.UserTokensService; | |||
/** | |||
* Entry point of the Java Client for SonarQube Web Services | |||
* | |||
* @since 5.3 | |||
*/ | |||
public class HttpWsClient implements WsClient { | |||
private final ComputeEngineService ceWsClient; | |||
private final PermissionsService permissionsService; | |||
private final ComponentsService componentsService; | |||
private final QualityProfilesService qualityProfilesService; | |||
private final IssuesService issuesService; | |||
private final UserTokensService userTokensService; | |||
private final WsConnector wsConnector; | |||
public HttpWsClient(WsConnector wsConnector) { | |||
this.wsConnector = wsConnector; | |||
this.ceWsClient = new ComputeEngineService(wsConnector); | |||
this.permissionsService = new PermissionsService(wsConnector); | |||
this.componentsService = new ComponentsService(wsConnector); | |||
this.qualityProfilesService = new QualityProfilesService(wsConnector); | |||
this.issuesService = new IssuesService(wsConnector); | |||
this.userTokensService = new UserTokensService(wsConnector); | |||
} | |||
@Override | |||
public WsConnector wsConnector() { | |||
return wsConnector; | |||
} | |||
@Override | |||
public PermissionsService permissions() { | |||
return this.permissionsService; | |||
} | |||
@Override | |||
public ComputeEngineService computeEngine() { | |||
return ceWsClient; | |||
} | |||
@Override | |||
public ComponentsService components() { | |||
return componentsService; | |||
} | |||
@Override | |||
public QualityProfilesService qualityProfiles() { | |||
return qualityProfilesService; | |||
} | |||
@Override | |||
public IssuesService issues() { | |||
return issuesService; | |||
} | |||
@Override | |||
public UserTokensService userTokens() { | |||
return userTokensService; | |||
} | |||
} |
@@ -0,0 +1,108 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import com.google.common.base.Throwables; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.Reader; | |||
import java.io.StringReader; | |||
import java.nio.charset.StandardCharsets; | |||
import org.apache.commons.io.IOUtils; | |||
import org.sonarqube.ws.MediaTypes; | |||
import static java.util.Objects.requireNonNull; | |||
public class MockWsResponse implements WsResponse { | |||
private String requestUrl; | |||
private byte[] content; | |||
private String contentType; | |||
@Override | |||
public String getContentType() { | |||
requireNonNull(contentType); | |||
return contentType; | |||
} | |||
public MockWsResponse setContentType(String contentType) { | |||
this.contentType = contentType; | |||
return this; | |||
} | |||
public MockWsResponse setRequestUrl(String requestUrl) { | |||
this.requestUrl = requestUrl; | |||
return this; | |||
} | |||
public MockWsResponse setContent(InputStream is) { | |||
try { | |||
return setContent(IOUtils.toByteArray(is)); | |||
} catch (IOException e) { | |||
throw Throwables.propagate(e); | |||
} | |||
} | |||
public MockWsResponse setContent(byte[] b) { | |||
this.content = b; | |||
return this; | |||
} | |||
public MockWsResponse setContent(String s) { | |||
this.content = s.getBytes(StandardCharsets.UTF_8); | |||
return this; | |||
} | |||
@Override | |||
public boolean hasContent() { | |||
return content != null; | |||
} | |||
@Override | |||
public String getRequestUrl() { | |||
requireNonNull(requestUrl); | |||
return requestUrl; | |||
} | |||
@Override | |||
public InputStream getContentStream() { | |||
requireNonNull(content); | |||
return new ByteArrayInputStream(content); | |||
} | |||
@Override | |||
public Reader getContentReader() { | |||
requireNonNull(content); | |||
return new StringReader(new String(content, StandardCharsets.UTF_8)); | |||
} | |||
@Override | |||
public String getContent() { | |||
requireNonNull(content); | |||
return new String(content, StandardCharsets.UTF_8); | |||
} | |||
public static MockWsResponse createJson(String json) { | |||
return new MockWsResponse() | |||
.setContentType(MediaTypes.JSON) | |||
.setContentType(json); | |||
} | |||
} |
@@ -17,55 +17,53 @@ | |||
* 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.sonarqube.ws.client; | |||
import org.junit.rules.ExternalResource; | |||
import java.io.File; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
public final class MockHttpServerInterceptor extends ExternalResource { | |||
/** | |||
* @since 5.3 | |||
*/ | |||
public class PostRequest extends BaseRequest<PostRequest> { | |||
private MockHttpServer server; | |||
private final Map<String, Part> parts = new LinkedHashMap<>(); | |||
@Override | |||
protected final void before() throws Throwable { | |||
server = new MockHttpServer(); | |||
server.start(); | |||
public PostRequest(String path) { | |||
super(path); | |||
} | |||
@Override | |||
protected void after() { | |||
server.stop(); | |||
public Method getMethod() { | |||
return Method.POST; | |||
} | |||
public MockHttpServerInterceptor stubResponseBody(String body) { | |||
server.doReturnBody(body); | |||
public PostRequest setPart(String name, Part part) { | |||
this.parts.put(name, part); | |||
return this; | |||
} | |||
public MockHttpServerInterceptor stubStatusCode(int status) { | |||
server.doReturnStatus(status); | |||
return this; | |||
public Map<String, Part> getParts() { | |||
return parts; | |||
} | |||
public String requestedPath() { | |||
return server.requestPath(); | |||
} | |||
public static class Part { | |||
private final String mediaType; | |||
private final File file; | |||
public Map requestHeaders() { | |||
return server.requestHeaders(); | |||
} | |||
public Part(String mediaType, File file) { | |||
this.mediaType = mediaType; | |||
this.file = file; | |||
} | |||
public Map requestParams() { | |||
return server.requestParams(); | |||
} | |||
public String getMediaType() { | |||
return mediaType; | |||
} | |||
public int port() { | |||
return server.getPort(); | |||
public File getFile() { | |||
return file; | |||
} | |||
} | |||
public String url() { | |||
return "http://localhost:" + port(); | |||
} | |||
} |
@@ -17,74 +17,30 @@ | |||
* 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.sonarqube.ws.client; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.protobuf.Message; | |||
import com.google.protobuf.Parser; | |||
import org.sonarqube.ws.client.component.ComponentsWsClient; | |||
import org.sonarqube.ws.client.issue.IssuesWsClient; | |||
import org.sonarqube.ws.client.permission.PermissionsWsClient; | |||
import org.sonarqube.ws.client.qualityprofile.QualityProfilesWsClient; | |||
import org.sonarqube.ws.client.usertoken.UserTokensWsClient; | |||
import static org.sonarqube.ws.client.WsRequest.MediaType.PROTOBUF; | |||
import org.sonarqube.ws.client.ce.ComputeEngineService; | |||
import org.sonarqube.ws.client.component.ComponentsService; | |||
import org.sonarqube.ws.client.issue.IssuesService; | |||
import org.sonarqube.ws.client.permission.PermissionsService; | |||
import org.sonarqube.ws.client.qualityprofile.QualityProfilesService; | |||
import org.sonarqube.ws.client.usertoken.UserTokensService; | |||
/** | |||
* Entry point of the Java Client for SonarQube Web Services. | |||
* <p/> | |||
* Example: | |||
* <pre> | |||
* WsClient client = new WsClient(Connector); | |||
* </pre> | |||
* | |||
* @since 5.2 | |||
* @since 5.3 | |||
*/ | |||
public class WsClient { | |||
@VisibleForTesting | |||
final WsConnector wsConnector; | |||
private final PermissionsWsClient permissionsWsClient; | |||
private final ComponentsWsClient componentsWsClient; | |||
private final QualityProfilesWsClient qualityProfilesWsClient; | |||
private final IssuesWsClient issuesWsClient; | |||
private final UserTokensWsClient userTokensWsClient; | |||
public WsClient(WsConnector wsConnector) { | |||
this.wsConnector = wsConnector; | |||
this.permissionsWsClient = new PermissionsWsClient(this); | |||
this.componentsWsClient = new ComponentsWsClient(this); | |||
this.qualityProfilesWsClient = new QualityProfilesWsClient(this); | |||
this.issuesWsClient = new IssuesWsClient(this); | |||
userTokensWsClient = new UserTokensWsClient(this); | |||
} | |||
public String execute(WsRequest wsRequest) { | |||
return wsConnector.execute(wsRequest); | |||
} | |||
public interface WsClient { | |||
ComponentsService components(); | |||
public <T extends Message> T execute(WsRequest wsRequest, Parser<T> protobufParser) { | |||
return wsConnector.execute(wsRequest.setMediaType(PROTOBUF), protobufParser); | |||
} | |||
ComputeEngineService computeEngine(); | |||
public PermissionsWsClient permissionsClient() { | |||
return this.permissionsWsClient; | |||
} | |||
IssuesService issues(); | |||
public ComponentsWsClient componentsWsClient() { | |||
return componentsWsClient; | |||
} | |||
PermissionsService permissions(); | |||
public QualityProfilesWsClient qualityProfilesWsClient() { | |||
return qualityProfilesWsClient; | |||
} | |||
QualityProfilesService qualityProfiles(); | |||
public IssuesWsClient issuesWsClient() { | |||
return issuesWsClient; | |||
} | |||
UserTokensService userTokens(); | |||
public UserTokensWsClient userTokensWsClient() { | |||
return userTokensWsClient; | |||
} | |||
WsConnector wsConnector(); | |||
} |
@@ -20,11 +20,23 @@ | |||
package org.sonarqube.ws.client; | |||
import com.google.protobuf.Message; | |||
import com.google.protobuf.Parser; | |||
/** | |||
* @since 5.3 | |||
*/ | |||
public interface WsConnector { | |||
String execute(WsRequest wsRequest); | |||
<T extends Message> T execute(WsRequest wsRequest, Parser<T> protobufParser); | |||
/** | |||
* @throws IllegalStateException if the request could not be executed due to | |||
* a connectivity problem or timeout. Because networks can | |||
* fail during an exchange, it is possible that the remote server | |||
* accepted the request before the failure | |||
* @throws HttpException if the response code is not in range [200..300) | |||
*/ | |||
WsResponse call(WsRequest wsRequest); | |||
/** | |||
* Server base URL, always with trailing slash, for instance "http://localhost:9000/" | |||
*/ | |||
String baseUrl(); | |||
} |
@@ -20,77 +20,22 @@ | |||
package org.sonarqube.ws.client; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import javax.annotation.Nullable; | |||
import static java.util.Objects.requireNonNull; | |||
import static org.sonarqube.ws.client.WsRequest.Method.GET; | |||
import static org.sonarqube.ws.client.WsRequest.Method.POST; | |||
public class WsRequest { | |||
private final Map<String, Object> params = new HashMap<>(); | |||
private Method method = Method.GET; | |||
private MediaType mimeType = MediaType.JSON; | |||
private String endpoint; | |||
private WsRequest(String endpoint) { | |||
this.endpoint = endpoint; | |||
} | |||
public static WsRequest newPostRequest(String endpoint) { | |||
return new WsRequest(endpoint) | |||
.setMethod(POST); | |||
} | |||
public static WsRequest newGetRequest(String endpoint) { | |||
return new WsRequest(endpoint) | |||
.setMethod(GET); | |||
} | |||
public Method getMethod() { | |||
return method; | |||
} | |||
private WsRequest setMethod(Method method) { | |||
this.method = method; | |||
return this; | |||
} | |||
public MediaType getMediaType() { | |||
return mimeType; | |||
} | |||
public WsRequest setMediaType(MediaType type) { | |||
requireNonNull(type); | |||
this.mimeType = type; | |||
return this; | |||
} | |||
/** | |||
* @since 5.3 | |||
*/ | |||
public interface WsRequest { | |||
public WsRequest setParam(String key, @Nullable Object value) { | |||
requireNonNull(key, "a WS parameter key cannot be null"); | |||
if (value != null) { | |||
this.params.put(key, value); | |||
} else { | |||
this.params.remove(key); | |||
} | |||
Method getMethod(); | |||
return this; | |||
} | |||
String getPath(); | |||
public String getEndpoint() { | |||
return endpoint; | |||
} | |||
String getMediaType(); | |||
public Map<String, Object> getParams() { | |||
return params; | |||
} | |||
Map<String, String> getParams(); | |||
public enum Method { | |||
enum Method { | |||
GET, POST | |||
} | |||
public enum MediaType { | |||
PROTOBUF, JSON, TEXT | |||
} | |||
} |
@@ -0,0 +1,41 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import java.io.InputStream; | |||
import java.io.Reader; | |||
/** | |||
* @since 5.3 | |||
*/ | |||
public interface WsResponse { | |||
boolean hasContent(); | |||
String getContentType(); | |||
String getRequestUrl(); | |||
InputStream getContentStream(); | |||
Reader getContentReader(); | |||
String getContent(); | |||
} |
@@ -0,0 +1,44 @@ | |||
/* | |||
* 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.sonarqube.ws.client.ce; | |||
import org.sonarqube.ws.MediaTypes; | |||
import org.sonarqube.ws.WsCe; | |||
import org.sonarqube.ws.client.BaseService; | |||
import org.sonarqube.ws.client.PostRequest; | |||
import org.sonarqube.ws.client.WsConnector; | |||
public class ComputeEngineService extends BaseService { | |||
public ComputeEngineService(WsConnector wsConnector) { | |||
super(wsConnector, "api/ce"); | |||
} | |||
public WsCe.SubmitResponse submit(SubmitWsRequest request) { | |||
PostRequest.Part filePart = new PostRequest.Part(MediaTypes.ZIP, request.getReport()); | |||
PostRequest post = new PostRequest(path("submit")) | |||
.setParam("projectKey", request.getProjectKey()) | |||
.setParam("projectName", request.getProjectName()) | |||
.setParam("projectBranch", request.getProjectBranch()) | |||
.setPart("report", filePart); | |||
return call(post, WsCe.SubmitResponse.parser()); | |||
} | |||
} |
@@ -0,0 +1,71 @@ | |||
/* | |||
* 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.sonarqube.ws.client.ce; | |||
import java.io.File; | |||
import javax.annotation.CheckForNull; | |||
public class SubmitWsRequest { | |||
private String projectKey; | |||
private String projectName; | |||
private String projectBranch; | |||
private File report; | |||
public String getProjectKey() { | |||
return projectKey; | |||
} | |||
public SubmitWsRequest setProjectKey(String projectKey) { | |||
this.projectKey = projectKey; | |||
return this; | |||
} | |||
@CheckForNull | |||
public String getProjectName() { | |||
return projectName; | |||
} | |||
public SubmitWsRequest setProjectName(String projectName) { | |||
this.projectName = projectName; | |||
return this; | |||
} | |||
@CheckForNull | |||
public String getProjectBranch() { | |||
return projectBranch; | |||
} | |||
public SubmitWsRequest setProjectBranch(String projectBranch) { | |||
this.projectBranch = projectBranch; | |||
return this; | |||
} | |||
@CheckForNull | |||
public File getReport() { | |||
return report; | |||
} | |||
public SubmitWsRequest setReport(File report) { | |||
this.report = report; | |||
return this; | |||
} | |||
} |
@@ -0,0 +1,25 @@ | |||
/* | |||
* 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. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonarqube.ws.client.ce; | |||
import javax.annotation.ParametersAreNonnullByDefault; | |||
@@ -21,29 +21,22 @@ | |||
package org.sonarqube.ws.client.component; | |||
import org.sonarqube.ws.WsComponents.SearchWsResponse; | |||
import org.sonarqube.ws.client.WsClient; | |||
import org.sonarqube.ws.client.BaseService; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.client.WsConnector; | |||
import static org.sonarqube.ws.client.WsRequest.newGetRequest; | |||
public class ComponentsService extends BaseService { | |||
public class ComponentsWsClient { | |||
private static final String ENDPOINT = "api/components/"; | |||
private final WsClient wsClient; | |||
public ComponentsWsClient(WsClient wsClient) { | |||
this.wsClient = wsClient; | |||
public ComponentsService(WsConnector wsConnector) { | |||
super(wsConnector, "api/components"); | |||
} | |||
public SearchWsResponse search(SearchWsRequest request) { | |||
return wsClient.execute( | |||
newGetRequest(action("search")) | |||
.setParam("qualifiers", request.getQualifiers()) | |||
.setParam("p", request.getPage()) | |||
.setParam("ps", request.getPageSize()) | |||
.setParam("q", request.getQuery()), | |||
SearchWsResponse.parser()); | |||
} | |||
private static String action(String action) { | |||
return ENDPOINT + action; | |||
GetRequest get = new GetRequest(path("search")) | |||
.setParam("qualifiers", request.getQualifiers()) | |||
.setParam("p", request.getPage()) | |||
.setParam("ps", request.getPageSize()) | |||
.setParam("q", request.getQuery()); | |||
return call(get, SearchWsResponse.parser()); | |||
} | |||
} |
@@ -26,7 +26,7 @@ import com.google.common.collect.Iterables; | |||
import java.util.List; | |||
/** | |||
* @since 3.7 | |||
* @since 5.3 | |||
*/ | |||
public class IssueFilterParameters { | |||
@@ -25,9 +25,10 @@ import java.util.List; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.sonarqube.ws.Issues.SearchWsResponse; | |||
import org.sonarqube.ws.client.WsClient; | |||
import org.sonarqube.ws.client.BaseService; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.client.WsConnector; | |||
import static org.sonarqube.ws.client.WsRequest.newGetRequest; | |||
import static org.sonarqube.ws.client.issue.IssueFilterParameters.ACTION_PLANS; | |||
import static org.sonarqube.ws.client.issue.IssueFilterParameters.ADDITIONAL_FIELDS; | |||
import static org.sonarqube.ws.client.issue.IssueFilterParameters.ASC; | |||
@@ -62,17 +63,16 @@ import static org.sonarqube.ws.client.issue.IssueFilterParameters.SEVERITIES; | |||
import static org.sonarqube.ws.client.issue.IssueFilterParameters.STATUSES; | |||
import static org.sonarqube.ws.client.issue.IssueFilterParameters.TAGS; | |||
public class IssuesWsClient { | |||
public class IssuesService extends BaseService { | |||
private static final Joiner LIST_TO_PARAMS_STRING = Joiner.on(",").skipNulls(); | |||
private final WsClient wsClient; | |||
public IssuesWsClient(WsClient wsClient) { | |||
this.wsClient = wsClient; | |||
public IssuesService(WsConnector wsConnector) { | |||
super(wsConnector, "api/issues"); | |||
} | |||
public SearchWsResponse search(SearchWsRequest request) { | |||
return wsClient.execute( | |||
newGetRequest(action("search")) | |||
return call( | |||
new GetRequest(path("search")) | |||
.setParam(ACTION_PLANS, listToParamList(request.getActionPlans())) | |||
.setParam(ADDITIONAL_FIELDS, listToParamList(request.getAdditionalFields())) | |||
.setParam(ASC, request.getAsc()) | |||
@@ -113,10 +113,6 @@ public class IssuesWsClient { | |||
SearchWsResponse.parser()); | |||
} | |||
private static String action(String action) { | |||
return "api/issues/" + action; | |||
} | |||
@CheckForNull | |||
private static String listToParamList(@Nullable List<String> strings) { | |||
return strings == null |
@@ -27,10 +27,11 @@ import org.sonarqube.ws.WsPermissions.SearchTemplatesWsResponse; | |||
import org.sonarqube.ws.WsPermissions.UpdateTemplateWsResponse; | |||
import org.sonarqube.ws.WsPermissions.UsersWsResponse; | |||
import org.sonarqube.ws.WsPermissions.WsSearchGlobalPermissionsResponse; | |||
import org.sonarqube.ws.client.WsClient; | |||
import org.sonarqube.ws.client.BaseService; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.client.PostRequest; | |||
import org.sonarqube.ws.client.WsConnector; | |||
import static org.sonarqube.ws.client.WsRequest.newGetRequest; | |||
import static org.sonarqube.ws.client.WsRequest.newPostRequest; | |||
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_NAME; | |||
@@ -45,27 +46,26 @@ import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_T | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_TEMPLATE_NAME; | |||
import static org.sonarqube.ws.client.permission.PermissionsWsParameters.PARAM_USER_LOGIN; | |||
public class PermissionsWsClient { | |||
private final WsClient wsClient; | |||
public class PermissionsService extends BaseService { | |||
public PermissionsWsClient(WsClient wsClient) { | |||
this.wsClient = wsClient; | |||
public PermissionsService(WsConnector wsConnector) { | |||
super(wsConnector, PermissionsWsParameters.CONTROLLER); | |||
} | |||
public WsPermissions.WsGroupsResponse groups(GroupsWsRequest request) { | |||
return wsClient.execute(newGetRequest(action("groups")) | |||
GetRequest get = new GetRequest(path("groups")) | |||
.setParam(PARAM_PERMISSION, request.getPermission()) | |||
.setParam(PARAM_PROJECT_ID, request.getProjectId()) | |||
.setParam(PARAM_PROJECT_KEY, request.getProjectKey()) | |||
.setParam("p", request.getPage()) | |||
.setParam("ps", request.getPageSize()) | |||
.setParam("selected", request.getSelected()) | |||
.setParam("q", request.getQuery()), | |||
WsPermissions.WsGroupsResponse.parser()); | |||
.setParam("q", request.getQuery()); | |||
return call(get, WsPermissions.WsGroupsResponse.parser()); | |||
} | |||
public void addGroup(AddGroupWsRequest request) { | |||
wsClient.execute(newPostRequest(action("add_group")) | |||
call(new PostRequest(path("add_group")) | |||
.setParam(PARAM_PERMISSION, request.getPermission()) | |||
.setParam(PARAM_PROJECT_ID, request.getProjectId()) | |||
.setParam(PARAM_PROJECT_KEY, request.getProjectKey()) | |||
@@ -74,7 +74,7 @@ public class PermissionsWsClient { | |||
} | |||
public void addGroupToTemplate(AddGroupToTemplateWsRequest request) { | |||
wsClient.execute(newPostRequest(action("add_group_to_template")) | |||
call(new PostRequest(path("add_group_to_template")) | |||
.setParam(PARAM_GROUP_ID, request.getGroupId()) | |||
.setParam(PARAM_GROUP_NAME, request.getGroupName()) | |||
.setParam(PARAM_PERMISSION, request.getPermission()) | |||
@@ -83,7 +83,7 @@ public class PermissionsWsClient { | |||
} | |||
public void addUser(AddUserWsRequest request) { | |||
wsClient.execute(newPostRequest(action("add_user")) | |||
call(new PostRequest(path("add_user")) | |||
.setParam(PARAM_USER_LOGIN, request.getLogin()) | |||
.setParam(PARAM_PERMISSION, request.getPermission()) | |||
.setParam(PARAM_PROJECT_ID, request.getProjectId()) | |||
@@ -91,7 +91,7 @@ public class PermissionsWsClient { | |||
} | |||
public void addUserToTemplate(AddUserToTemplateWsRequest request) { | |||
wsClient.execute(newPostRequest(action("add_user_to_template")) | |||
call(new PostRequest(path("add_user_to_template")) | |||
.setParam(PARAM_PERMISSION, request.getPermission()) | |||
.setParam(PARAM_USER_LOGIN, request.getLogin()) | |||
.setParam(PARAM_TEMPLATE_ID, request.getTemplateId()) | |||
@@ -99,7 +99,7 @@ public class PermissionsWsClient { | |||
} | |||
public void applyTemplate(ApplyTemplateWsRequest request) { | |||
wsClient.execute(newPostRequest(action("apply_template")) | |||
call(new PostRequest(path("apply_template")) | |||
.setParam(PARAM_PROJECT_ID, request.getProjectId()) | |||
.setParam(PARAM_PROJECT_KEY, request.getProjectKey()) | |||
.setParam(PARAM_TEMPLATE_ID, request.getTemplateId()) | |||
@@ -107,22 +107,21 @@ public class PermissionsWsClient { | |||
} | |||
public CreateTemplateWsResponse createTemplate(CreateTemplateWsRequest request) { | |||
return wsClient.execute(newPostRequest( | |||
action("create_template")) | |||
.setParam(PARAM_NAME, request.getName()) | |||
.setParam(PARAM_DESCRIPTION, request.getDescription()) | |||
.setParam(PARAM_PROJECT_KEY_PATTERN, request.getProjectKeyPattern()), | |||
CreateTemplateWsResponse.parser()); | |||
PostRequest post = new PostRequest(path("create_template")) | |||
.setParam(PARAM_NAME, request.getName()) | |||
.setParam(PARAM_DESCRIPTION, request.getDescription()) | |||
.setParam(PARAM_PROJECT_KEY_PATTERN, request.getProjectKeyPattern()); | |||
return call(post, CreateTemplateWsResponse.parser()); | |||
} | |||
public void deleteTemplate(DeleteTemplateWsRequest request) { | |||
wsClient.execute(newPostRequest(action("delete_template")) | |||
call(new PostRequest(path("delete_template")) | |||
.setParam(PARAM_TEMPLATE_ID, request.getTemplateId()) | |||
.setParam(PARAM_TEMPLATE_NAME, request.getTemplateName())); | |||
} | |||
public void removeGroup(RemoveGroupWsRequest request) { | |||
wsClient.execute(newPostRequest(action("remove_group")) | |||
call(new PostRequest(path("remove_group")) | |||
.setParam(PARAM_PERMISSION, request.getPermission()) | |||
.setParam(PARAM_GROUP_ID, request.getGroupId()) | |||
.setParam(PARAM_GROUP_NAME, request.getGroupName()) | |||
@@ -131,7 +130,7 @@ public class PermissionsWsClient { | |||
} | |||
public void removeGroupFromTemplate(RemoveGroupFromTemplateWsRequest request) { | |||
wsClient.execute(newPostRequest(action("remove_group_from_template")) | |||
call(new PostRequest(path("remove_group_from_template")) | |||
.setParam(PARAM_PERMISSION, request.getPermission()) | |||
.setParam(PARAM_GROUP_ID, request.getGroupId()) | |||
.setParam(PARAM_GROUP_NAME, request.getGroupName()) | |||
@@ -140,7 +139,7 @@ public class PermissionsWsClient { | |||
} | |||
public void removeUser(RemoveUserWsRequest request) { | |||
wsClient.execute(newPostRequest(action("remove_user")) | |||
call(new PostRequest(path("remove_user")) | |||
.setParam(PARAM_PERMISSION, request.getPermission()) | |||
.setParam(PARAM_USER_LOGIN, request.getLogin()) | |||
.setParam(PARAM_PROJECT_ID, request.getProjectId()) | |||
@@ -148,7 +147,7 @@ public class PermissionsWsClient { | |||
} | |||
public void removeUserFromTemplate(RemoveUserFromTemplateWsRequest request) { | |||
wsClient.execute(newPostRequest(action("remove_user_from_template")) | |||
call(new PostRequest(path("remove_user_from_template")) | |||
.setParam(PARAM_PERMISSION, request.getPermission()) | |||
.setParam(PARAM_USER_LOGIN, request.getLogin()) | |||
.setParam(PARAM_TEMPLATE_ID, request.getTemplateId()) | |||
@@ -156,62 +155,50 @@ public class PermissionsWsClient { | |||
} | |||
public WsSearchGlobalPermissionsResponse searchGlobalPermissions() { | |||
return wsClient.execute( | |||
newGetRequest(action("search_global_permissions")), | |||
WsSearchGlobalPermissionsResponse.parser()); | |||
GetRequest get = new GetRequest(path("search_global_permissions")); | |||
return call(get, WsSearchGlobalPermissionsResponse.parser()); | |||
} | |||
public SearchProjectPermissionsWsResponse searchProjectPermissions(SearchProjectPermissionsWsRequest request) { | |||
return wsClient.execute( | |||
newGetRequest(action("search_project_permissions")) | |||
.setParam(PARAM_PROJECT_ID, request.getProjectId()) | |||
.setParam(PARAM_PROJECT_KEY, request.getProjectKey()) | |||
.setParam(PARAM_QUALIFIER, request.getQualifier()) | |||
.setParam("p", request.getPage()) | |||
.setParam("ps", request.getPageSize()) | |||
.setParam("q", request.getQuery()), | |||
SearchProjectPermissionsWsResponse.parser()); | |||
GetRequest get = new GetRequest(path("search_project_permissions")) | |||
.setParam(PARAM_PROJECT_ID, request.getProjectId()) | |||
.setParam(PARAM_PROJECT_KEY, request.getProjectKey()) | |||
.setParam(PARAM_QUALIFIER, request.getQualifier()) | |||
.setParam("p", request.getPage()) | |||
.setParam("ps", request.getPageSize()) | |||
.setParam("q", request.getQuery()); | |||
return call(get, SearchProjectPermissionsWsResponse.parser()); | |||
} | |||
public SearchTemplatesWsResponse searchTemplates(SearchTemplatesWsRequest request) { | |||
return wsClient.execute( | |||
newGetRequest(action("search_templates")) | |||
.setParam("q", request.getQuery()), | |||
SearchTemplatesWsResponse.parser()); | |||
GetRequest get = new GetRequest(path("search_templates")) | |||
.setParam("q", request.getQuery()); | |||
return call(get, SearchTemplatesWsResponse.parser()); | |||
} | |||
public void setDefaultTemplate(SetDefaultTemplateWsRequest request) { | |||
wsClient.execute( | |||
newPostRequest(action("set_default_template")) | |||
.setParam(PARAM_QUALIFIER, request.getQualifier()) | |||
.setParam(PARAM_TEMPLATE_ID, request.getTemplateId()) | |||
.setParam(PARAM_TEMPLATE_NAME, request.getTemplateName())); | |||
call(new PostRequest(path("set_default_template")) | |||
.setParam(PARAM_QUALIFIER, request.getQualifier()) | |||
.setParam(PARAM_TEMPLATE_ID, request.getTemplateId()) | |||
.setParam(PARAM_TEMPLATE_NAME, request.getTemplateName())); | |||
} | |||
public UpdateTemplateWsResponse updateTemplate(UpdateTemplateWsRequest request) { | |||
return wsClient.execute( | |||
newPostRequest(action("update_template")) | |||
.setParam(PARAM_DESCRIPTION, request.getDescription()) | |||
.setParam(PARAM_ID, request.getId()) | |||
.setParam(PARAM_NAME, request.getName()) | |||
.setParam(PARAM_PROJECT_KEY_PATTERN, request.getProjectKeyPattern()), | |||
UpdateTemplateWsResponse.parser()); | |||
return call(new PostRequest(path("update_template")) | |||
.setParam(PARAM_DESCRIPTION, request.getDescription()) | |||
.setParam(PARAM_ID, request.getId()) | |||
.setParam(PARAM_NAME, request.getName()) | |||
.setParam(PARAM_PROJECT_KEY_PATTERN, request.getProjectKeyPattern()), UpdateTemplateWsResponse.parser()); | |||
} | |||
public UsersWsResponse users(UsersWsRequest request) { | |||
return wsClient.execute( | |||
newGetRequest(action("users")) | |||
.setParam(PARAM_PERMISSION, request.getPermission()) | |||
.setParam(PARAM_PROJECT_ID, request.getProjectId()) | |||
.setParam(PARAM_PROJECT_KEY, request.getProjectKey()) | |||
.setParam("selected", request.getSelected()) | |||
.setParam("p", request.getPage()) | |||
.setParam("ps", request.getPageSize()) | |||
.setParam("q", request.getQuery()), | |||
UsersWsResponse.parser()); | |||
} | |||
private static String action(String action) { | |||
return PermissionsWsParameters.ENDPOINT + "/" + action; | |||
return call(new GetRequest(path("users")) | |||
.setParam(PARAM_PERMISSION, request.getPermission()) | |||
.setParam(PARAM_PROJECT_ID, request.getProjectId()) | |||
.setParam(PARAM_PROJECT_KEY, request.getProjectKey()) | |||
.setParam("selected", request.getSelected()) | |||
.setParam("p", request.getPage()) | |||
.setParam("ps", request.getPageSize()) | |||
.setParam("q", request.getQuery()), UsersWsResponse.parser()); | |||
} | |||
} |
@@ -21,7 +21,7 @@ | |||
package org.sonarqube.ws.client.permission; | |||
public class PermissionsWsParameters { | |||
public static final String ENDPOINT = "api/permissions"; | |||
public static final String CONTROLLER = "api/permissions"; | |||
public static final String PARAM_PERMISSION = "permission"; | |||
public static final String PARAM_GROUP_NAME = "groupName"; |
@@ -21,20 +21,19 @@ | |||
package org.sonarqube.ws.client.qualityprofile; | |||
import org.sonarqube.ws.QualityProfiles.SearchWsResponse; | |||
import org.sonarqube.ws.client.WsClient; | |||
import org.sonarqube.ws.client.BaseService; | |||
import org.sonarqube.ws.client.GetRequest; | |||
import org.sonarqube.ws.client.WsConnector; | |||
import static org.sonarqube.ws.client.WsRequest.newGetRequest; | |||
public class QualityProfilesService extends BaseService { | |||
public class QualityProfilesWsClient { | |||
private final WsClient wsClient; | |||
public QualityProfilesWsClient(WsClient wsClient) { | |||
this.wsClient = wsClient; | |||
public QualityProfilesService(WsConnector wsConnector) { | |||
super(wsConnector, "api/qualityprofiles"); | |||
} | |||
public SearchWsResponse search(SearchWsRequest request) { | |||
return wsClient.execute( | |||
newGetRequest(action("search")) | |||
return call( | |||
new GetRequest(path("search")) | |||
.setParam("defaults", request.getDefaults()) | |||
.setParam("language", request.getLanguage()) | |||
.setParam("profileName", request.getProfileName()) | |||
@@ -42,7 +41,4 @@ public class QualityProfilesWsClient { | |||
SearchWsResponse.parser()); | |||
} | |||
private static String action(String action) { | |||
return "api/qualityprofiles/" + action; | |||
} | |||
} |
@@ -20,51 +20,27 @@ | |||
package org.sonarqube.ws.client.usertoken; | |||
import org.sonarqube.ws.WsComponents.SearchWsResponse; | |||
import org.sonarqube.ws.WsUserTokens.GenerateWsResponse; | |||
import org.sonarqube.ws.client.WsClient; | |||
import org.sonarqube.ws.client.BaseService; | |||
import org.sonarqube.ws.client.PostRequest; | |||
import org.sonarqube.ws.client.WsConnector; | |||
import static org.sonarqube.ws.client.WsRequest.newGetRequest; | |||
import static org.sonarqube.ws.client.WsRequest.newPostRequest; | |||
import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.ACTION_GENERATE; | |||
import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.ACTION_REVOKE; | |||
import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.ACTION_SEARCH; | |||
import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.CONTROLLER; | |||
import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.PARAM_LOGIN; | |||
import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.PARAM_NAME; | |||
import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.USER_TOKENS_ENDPOINT; | |||
public class UserTokensWsClient { | |||
private static final String SLASH = "/"; | |||
private final WsClient wsClient; | |||
public class UserTokensService extends BaseService { | |||
public UserTokensWsClient(WsClient wsClient) { | |||
this.wsClient = wsClient; | |||
public UserTokensService(WsConnector wsConnector) { | |||
super(wsConnector, CONTROLLER); | |||
} | |||
public GenerateWsResponse generate(GenerateWsRequest request) { | |||
return wsClient.execute( | |||
newPostRequest(action(ACTION_GENERATE)) | |||
return call( | |||
new PostRequest(path(ACTION_GENERATE)) | |||
.setParam(PARAM_LOGIN, request.getLogin()) | |||
.setParam(PARAM_NAME, request.getName()), | |||
GenerateWsResponse.parser()); | |||
} | |||
public void revoke(RevokeWsRequest request) { | |||
wsClient.execute( | |||
newPostRequest(action(ACTION_REVOKE)) | |||
.setParam(PARAM_LOGIN, request.getLogin()) | |||
.setParam(PARAM_NAME, request.getName())); | |||
} | |||
public SearchWsResponse search(SearchWsRequest request) { | |||
return wsClient.execute( | |||
newGetRequest(action(ACTION_SEARCH)) | |||
.setParam(PARAM_LOGIN, request.getLogin()), | |||
SearchWsResponse.parser() | |||
); | |||
} | |||
private static String action(String action) { | |||
return USER_TOKENS_ENDPOINT + SLASH + action; | |||
} | |||
} |
@@ -21,7 +21,7 @@ | |||
package org.sonarqube.ws.client.usertoken; | |||
public class UserTokensWsParameters { | |||
public static final String USER_TOKENS_ENDPOINT = "api/user_tokens"; | |||
public static final String CONTROLLER = "api/user_tokens"; | |||
public static final String ACTION_GENERATE = "generate"; | |||
public static final String ACTION_REVOKE = "revoke"; | |||
public static final String ACTION_SEARCH = "search"; |
@@ -0,0 +1,84 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonarqube.ws.MediaTypes; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.data.MapEntry.entry; | |||
public class BaseRequestTest { | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
FakeRequest underTest = new FakeRequest("api/foo"); | |||
@Test | |||
public void test_defaults() { | |||
assertThat(underTest.getMethod()).isEqualTo(WsRequest.Method.GET); | |||
assertThat(underTest.getParams()).isEmpty(); | |||
assertThat(underTest.getMediaType()).isEqualTo(MediaTypes.JSON); | |||
assertThat(underTest.getPath()).isEqualTo("api/foo"); | |||
} | |||
@Test | |||
public void setMediaType() { | |||
underTest.setMediaType(MediaTypes.PROTOBUF); | |||
assertThat(underTest.getMediaType()).isEqualTo(MediaTypes.PROTOBUF); | |||
} | |||
@Test | |||
public void keep_order_of_params() { | |||
assertThat(underTest.getParams()).isEmpty(); | |||
underTest.setParam("keyB", "b"); | |||
assertThat(underTest.getParams()).containsExactly(entry("keyB", "b")); | |||
underTest.setParam("keyA", "a"); | |||
assertThat(underTest.getParams()).containsExactly(entry("keyB", "b"), entry("keyA", "a")); | |||
} | |||
@Test | |||
public void null_param_value() { | |||
underTest.setParam("key", null); | |||
assertThat(underTest.getParams()).isEmpty(); | |||
} | |||
@Test | |||
public void fail_if_null_param_key() { | |||
expectedException.expect(IllegalArgumentException.class); | |||
underTest.setParam(null, "val"); | |||
} | |||
private static class FakeRequest extends BaseRequest<FakeRequest> { | |||
FakeRequest(String path) { | |||
super(path); | |||
} | |||
@Override | |||
public Method getMethod() { | |||
return Method.GET; | |||
} | |||
} | |||
} |
@@ -0,0 +1,93 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import java.io.IOException; | |||
import org.junit.Test; | |||
import org.sonarqube.ws.MediaTypes; | |||
import org.sonarqube.ws.Testing; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.fail; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
public class BaseServiceTest { | |||
WsConnector wsConnector = mock(WsConnector.class); | |||
@Test | |||
public void test_call() throws Exception { | |||
new BaseService(wsConnector, "api/issues") { | |||
public void test() throws IOException { | |||
GetRequest get = new GetRequest(path("issue")).setMediaType(MediaTypes.JSON); | |||
when(wsConnector.call(get)).thenReturn(new MockWsResponse().setContent("ok")); | |||
WsResponse response = call(get); | |||
assertThat(response.getContent()).isEqualTo("ok"); | |||
} | |||
}.test(); | |||
} | |||
@Test | |||
public void call_and_convert_protobuf() { | |||
new BaseService(wsConnector, "api/issues") { | |||
public void test() { | |||
GetRequest get = new GetRequest(path("issue")).setParam("key", "ABC"); | |||
when(wsConnector.call(get)).thenReturn(newProtobufFakeResponse()); | |||
Testing.Fake message = call(get, Testing.Fake.parser()); | |||
assertThat(message.getLabel()).isEqualTo("ok"); | |||
assertThat(get.getPath()).isEqualTo("api/issues/issue"); | |||
// media type automatically set to protobuf | |||
assertThat(get.getMediaType()).isEqualTo(MediaTypes.PROTOBUF); | |||
} | |||
}.test(); | |||
} | |||
@Test | |||
public void fail_to_parse_protobuf_response() { | |||
new BaseService(wsConnector, "api/issues") { | |||
public void test() { | |||
GetRequest get = new GetRequest(path("issue")).setParam("key", "ABC"); | |||
when(wsConnector.call(get)).thenReturn(MockWsResponse.createJson("{}").setRequestUrl("http://local/api/issues/issue?key=ABC")); | |||
try { | |||
call(get, Testing.Fake.parser()); | |||
fail(); | |||
} catch (IllegalStateException e) { | |||
assertThat(e).hasMessage("Fail to parse protobuf response of http://local/api/issues/issue?key=ABC"); | |||
} | |||
} | |||
}.test(); | |||
} | |||
private static WsResponse newProtobufFakeResponse() { | |||
Testing.Fake message = Testing.Fake.newBuilder().setLabel("ok").build(); | |||
return new MockWsResponse().setContent(message.toByteArray()); | |||
} | |||
} |
@@ -0,0 +1,297 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import com.squareup.okhttp.Interceptor; | |||
import com.squareup.okhttp.Response; | |||
import com.squareup.okhttp.mockwebserver.MockResponse; | |||
import com.squareup.okhttp.mockwebserver.MockWebServer; | |||
import com.squareup.okhttp.mockwebserver.RecordedRequest; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.concurrent.atomic.AtomicBoolean; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonarqube.ws.MediaTypes; | |||
import static com.squareup.okhttp.Credentials.basic; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.fail; | |||
import static org.mockito.Mockito.mock; | |||
public class HttpConnectorTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
MockWebServer server; | |||
String serverUrl; | |||
@Before | |||
public void setUp() throws Exception { | |||
server = new MockWebServer(); | |||
server.start(); | |||
serverUrl = server.url("").url().toString(); | |||
} | |||
@Test | |||
public void test_default_settings() throws Exception { | |||
answerHelloWorld(); | |||
HttpConnector underTest = new HttpConnector.Builder().url(serverUrl).build(); | |||
assertThat(underTest.baseUrl()).isEqualTo(serverUrl); | |||
GetRequest request = new GetRequest("api/issues/search").setMediaType(MediaTypes.PROTOBUF); | |||
WsResponse response = underTest.call(request); | |||
// verify default timeouts on client | |||
assertThat(underTest.okHttpClient().getConnectTimeout()).isEqualTo(HttpConnector.DEFAULT_CONNECT_TIMEOUT_MILLISECONDS); | |||
assertThat(underTest.okHttpClient().getReadTimeout()).isEqualTo(HttpConnector.DEFAULT_READ_TIMEOUT_MILLISECONDS); | |||
// verify response | |||
assertThat(response.hasContent()).isTrue(); | |||
assertThat(response.getContent()).isEqualTo("hello, world!"); | |||
// verify the request received by server | |||
RecordedRequest recordedRequest = server.takeRequest(); | |||
assertThat(recordedRequest.getMethod()).isEqualTo("GET"); | |||
assertThat(recordedRequest.getPath()).isEqualTo("/api/issues/search"); | |||
assertThat(recordedRequest.getHeader("Accept")).isEqualTo(MediaTypes.PROTOBUF); | |||
assertThat(recordedRequest.getHeader("Accept-Charset")).isEqualTo("UTF-8"); | |||
assertThat(recordedRequest.getHeader("User-Agent")).startsWith("okhttp/"); | |||
// compression is handled by OkHttp | |||
assertThat(recordedRequest.getHeader("Accept-Encoding")).isEqualTo("gzip"); | |||
} | |||
@Test | |||
public void use_basic_authentication() throws Exception { | |||
answerHelloWorld(); | |||
HttpConnector underTest = new HttpConnector.Builder() | |||
.url(serverUrl) | |||
.credentials("theLogin", "thePassword") | |||
.build(); | |||
GetRequest request = new GetRequest("api/issues/search"); | |||
underTest.call(request); | |||
RecordedRequest recordedRequest = server.takeRequest(); | |||
assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(basic("theLogin", "thePassword")); | |||
} | |||
@Test | |||
public void use_basic_authentication_with_null_password() throws Exception { | |||
answerHelloWorld(); | |||
HttpConnector underTest = new HttpConnector.Builder() | |||
.url(serverUrl) | |||
.credentials("theLogin", null) | |||
.build(); | |||
GetRequest request = new GetRequest("api/issues/search"); | |||
underTest.call(request); | |||
RecordedRequest recordedRequest = server.takeRequest(); | |||
assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(basic("theLogin", "")); | |||
} | |||
/** | |||
* Access token replaces the couple {login,password} and is sent through | |||
* the login field | |||
*/ | |||
@Test | |||
public void use_access_token() throws Exception { | |||
answerHelloWorld(); | |||
HttpConnector underTest = new HttpConnector.Builder() | |||
.url(serverUrl) | |||
.token("theToken") | |||
.build(); | |||
GetRequest request = new GetRequest("api/issues/search"); | |||
underTest.call(request); | |||
RecordedRequest recordedRequest = server.takeRequest(); | |||
assertThat(recordedRequest.getHeader("Authorization")).isEqualTo(basic("theToken", "")); | |||
} | |||
@Test | |||
public void use_proxy_authentication() throws Exception { | |||
answerHelloWorld(); | |||
HttpConnector underTest = new HttpConnector.Builder() | |||
.url(serverUrl) | |||
.proxyCredentials("theProxyLogin", "theProxyPassword") | |||
.build(); | |||
GetRequest request = new GetRequest("api/issues/search"); | |||
underTest.call(request); | |||
RecordedRequest recordedRequest = server.takeRequest(); | |||
assertThat(recordedRequest.getHeader("Proxy-Authorization")).isEqualTo(basic("theProxyLogin", "theProxyPassword")); | |||
} | |||
@Test | |||
public void override_timeouts() { | |||
HttpConnector underTest = new HttpConnector.Builder() | |||
.url(serverUrl) | |||
.readTimeoutMilliseconds(42) | |||
.connectTimeoutMilliseconds(74) | |||
.build(); | |||
assertThat(underTest.okHttpClient().getReadTimeout()).isEqualTo(42); | |||
assertThat(underTest.okHttpClient().getConnectTimeout()).isEqualTo(74); | |||
} | |||
@Test | |||
public void send_user_agent() throws Exception { | |||
answerHelloWorld(); | |||
HttpConnector underTest = new HttpConnector.Builder() | |||
.url(serverUrl) | |||
.userAgent("Maven Plugin/2.3") | |||
.build(); | |||
underTest.call(new GetRequest("api/issues/search")); | |||
RecordedRequest recordedRequest = server.takeRequest(); | |||
assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("Maven Plugin/2.3"); | |||
} | |||
@Test | |||
public void fail_if_unknown_implementation_of_request() { | |||
HttpConnector underTest = new HttpConnector.Builder().url(serverUrl).build(); | |||
try { | |||
underTest.call(mock(WsRequest.class)); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessageContaining("Unsupported implementation: "); | |||
} | |||
} | |||
@Test | |||
public void send_post_request() throws Exception { | |||
answerHelloWorld(); | |||
PostRequest request = new PostRequest("api/issues/search") | |||
.setParam("severity", "MAJOR") | |||
.setMediaType(MediaTypes.PROTOBUF); | |||
HttpConnector underTest = new HttpConnector.Builder().url(serverUrl).build(); | |||
WsResponse response = underTest.call(request); | |||
// verify response | |||
assertThat(response.hasContent()).isTrue(); | |||
assertThat(response.getContent()).isEqualTo("hello, world!"); | |||
// verify the request received by server | |||
RecordedRequest recordedRequest = server.takeRequest(); | |||
assertThat(recordedRequest.getMethod()).isEqualTo("POST"); | |||
assertThat(recordedRequest.getPath()).isEqualTo("/api/issues/search?severity=MAJOR"); | |||
} | |||
@Test | |||
public void upload_file() throws Exception { | |||
answerHelloWorld(); | |||
File reportFile = temp.newFile(); | |||
FileUtils.write(reportFile, "the report content"); | |||
PostRequest request = new PostRequest("api/report/upload") | |||
.setParam("project", "theKey") | |||
.setPart("report", new PostRequest.Part(MediaTypes.TXT, reportFile)) | |||
.setMediaType(MediaTypes.PROTOBUF); | |||
HttpConnector underTest = new HttpConnector.Builder().url(serverUrl).build(); | |||
WsResponse response = underTest.call(request); | |||
assertThat(response.hasContent()).isTrue(); | |||
RecordedRequest recordedRequest = server.takeRequest(); | |||
assertThat(recordedRequest.getMethod()).isEqualTo("POST"); | |||
assertThat(recordedRequest.getPath()).isEqualTo("/api/report/upload?project=theKey"); | |||
String body = IOUtils.toString(recordedRequest.getBody().inputStream()); | |||
assertThat(body) | |||
.contains("Content-Disposition: form-data; name=\"report\"") | |||
.contains("Content-Type: text/plain") | |||
.contains("the report content"); | |||
} | |||
@Test | |||
public void http_error() throws Exception { | |||
server.enqueue(new MockResponse().setResponseCode(404)); | |||
PostRequest request = new PostRequest("api/issues/search"); | |||
HttpConnector underTest = new HttpConnector.Builder().url(serverUrl).build(); | |||
try { | |||
underTest.call(request); | |||
fail(); | |||
} catch (HttpException e) { | |||
assertThat(e.code()).isEqualTo(404); | |||
} | |||
} | |||
@Test | |||
public void intercept_request_and_response() { | |||
final AtomicBoolean called = new AtomicBoolean(false); | |||
Interceptor interceptor = new Interceptor() { | |||
@Override | |||
public Response intercept(Chain chain) throws IOException { | |||
called.set(true); | |||
return chain.proceed(chain.request()); | |||
} | |||
}; | |||
answerHelloWorld(); | |||
HttpConnector underTest = new HttpConnector.Builder() | |||
.url(serverUrl) | |||
.interceptor(interceptor) | |||
.build(); | |||
underTest.call(new GetRequest("")); | |||
assertThat(called.get()).isTrue(); | |||
} | |||
@Test | |||
public void support_base_url_ending_with_slash() throws Exception { | |||
assertThat(serverUrl).endsWith("/"); | |||
HttpConnector underTest = new HttpConnector.Builder().url(StringUtils.removeEnd(serverUrl, "/")).build(); | |||
GetRequest request = new GetRequest("api/issues/search"); | |||
answerHelloWorld(); | |||
WsResponse response = underTest.call(request); | |||
assertThat(response.hasContent()).isTrue(); | |||
} | |||
@Test | |||
public void support_base_url_with_context() { | |||
// just to be sure | |||
assertThat(serverUrl).endsWith("/"); | |||
HttpConnector underTest = new HttpConnector.Builder().url(serverUrl + "sonar").build(); | |||
GetRequest request = new GetRequest("api/issues/search"); | |||
answerHelloWorld(); | |||
assertThat(underTest.call(request).getRequestUrl()).isEqualTo(serverUrl + "sonar/api/issues/search"); | |||
request = new GetRequest("/api/issues/search"); | |||
answerHelloWorld(); | |||
assertThat(underTest.call(request).getRequestUrl()).isEqualTo(serverUrl + "sonar/api/issues/search"); | |||
} | |||
private void answerHelloWorld() { | |||
server.enqueue(new MockResponse().setBody("hello, world!")); | |||
} | |||
} |
@@ -27,7 +27,7 @@ public class HttpExceptionTest { | |||
@Test | |||
public void test_exception() throws Exception { | |||
HttpException exception = new HttpException("http://localhost:9000/api/search", 500, "Not found"); | |||
assertThat(exception.status()).isEqualTo(500); | |||
assertThat(exception.code()).isEqualTo(500); | |||
assertThat(exception.url()).isEqualTo("http://localhost:9000/api/search"); | |||
assertThat(exception.getMessage()).isEqualTo("Error 500 on http://localhost:9000/api/search : Not found"); | |||
} |
@@ -1,86 +0,0 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonarqube.ws.client.WsRequest.newGetRequest; | |||
public class HttpRequestFactoryTest { | |||
@Rule | |||
public MockHttpServerInterceptor httpServer = new MockHttpServerInterceptor(); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@Test | |||
public void test_get() { | |||
httpServer.stubStatusCode(200).stubResponseBody("{'issues': []}"); | |||
HttpRequestFactory factory = new HttpRequestFactory(httpServer.url()); | |||
String json = factory.execute(newGetRequest("/api/issues")); | |||
assertThat(json).isEqualTo("{'issues': []}"); | |||
assertThat(httpServer.requestedPath()).isEqualTo("/api/issues"); | |||
} | |||
@Test | |||
public void should_throw_illegal_state_exc_if_connect_exception() { | |||
expectedException.expect(IllegalStateException.class); | |||
expectedException.expectMessage("Fail to request http://localhost:1/api/issues"); | |||
HttpRequestFactory factory = new HttpRequestFactory("http://localhost:1"); | |||
factory.execute(newGetRequest("/api/issues")); | |||
} | |||
@Test | |||
public void test_authentication() { | |||
httpServer.stubStatusCode(200).stubResponseBody("{}"); | |||
HttpRequestFactory factory = new HttpRequestFactory(httpServer.url()).setLogin("karadoc").setPassword("legrascestlavie"); | |||
String json = factory.execute(newGetRequest("/api/issues")); | |||
assertThat(json).isEqualTo("{}"); | |||
assertThat(httpServer.requestedPath()).isEqualTo("/api/issues"); | |||
assertThat(httpServer.requestHeaders().get("Authorization")).isEqualTo("Basic a2FyYWRvYzpsZWdyYXNjZXN0bGF2aWU="); | |||
} | |||
@Test | |||
public void test_proxy() throws Exception { | |||
expectedException.expect(IllegalStateException.class); | |||
HttpRequestFactory factory = new HttpRequestFactory(httpServer.url()) | |||
.setProxyHost("localhost").setProxyPort(1) | |||
.setProxyLogin("john").setProxyPassword("smith"); | |||
factory.execute(newGetRequest("/api/issues")); | |||
} | |||
@Test | |||
public void beginning_slash_is_optional() throws Exception { | |||
HttpRequestFactory factory = new HttpRequestFactory(httpServer.url()); | |||
factory.execute(newGetRequest("api/foo")); | |||
assertThat(httpServer.requestedPath()).isEqualTo("/api/foo"); | |||
factory.execute(newGetRequest("/api/bar")); | |||
assertThat(httpServer.requestedPath()).isEqualTo("/api/bar"); | |||
} | |||
} |
@@ -1,128 +0,0 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import java.io.IOException; | |||
import java.net.HttpURLConnection; | |||
import java.util.Enumeration; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import javax.servlet.ServletException; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import org.eclipse.jetty.server.Handler; | |||
import org.eclipse.jetty.server.Request; | |||
import org.eclipse.jetty.server.Server; | |||
import org.eclipse.jetty.server.handler.AbstractHandler; | |||
import static org.apache.commons.io.IOUtils.write; | |||
public class MockHttpServer { | |||
private Server server; | |||
private String responseBody; | |||
private byte[] binaryResponseBody; | |||
private int responseStatus = HttpURLConnection.HTTP_OK; | |||
private String requestPath; | |||
private Map requestHeaders = new HashMap(), requestParams = new HashMap(); | |||
private String contentType; | |||
public void start() throws Exception { | |||
// 0 is random available port | |||
server = new Server(0); | |||
server.setHandler(getMockHandler()); | |||
server.start(); | |||
} | |||
public Handler getMockHandler() { | |||
Handler handler = new AbstractHandler() { | |||
@Override | |||
public void handle(String target, Request baseRequest, HttpServletRequest httpServletRequest, HttpServletResponse response) throws IOException, ServletException { | |||
requestPath = baseRequest.getUri().toString(); | |||
requestHeaders.clear(); | |||
Enumeration names = baseRequest.getHeaderNames(); | |||
while (names.hasMoreElements()) { | |||
String headerName = (String) names.nextElement(); | |||
requestHeaders.put(headerName, baseRequest.getHeader(headerName)); | |||
} | |||
requestParams.clear(); | |||
names = baseRequest.getParameterNames(); | |||
while (names.hasMoreElements()) { | |||
String headerName = (String) names.nextElement(); | |||
requestParams.put(headerName, baseRequest.getParameter(headerName)); | |||
} | |||
response.setStatus(responseStatus); | |||
response.setContentType("application/json;charset=utf-8"); | |||
if (responseBody != null) { | |||
write(responseBody, response.getOutputStream()); | |||
} else { | |||
write(binaryResponseBody, response.getOutputStream()); | |||
} | |||
baseRequest.setHandled(true); | |||
} | |||
}; | |||
return handler; | |||
} | |||
public void stop() { | |||
try { | |||
if (server != null) { | |||
server.stop(); | |||
} | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Fail to stop HTTP server", e); | |||
} | |||
} | |||
public MockHttpServer doReturnBody(String responseBody) { | |||
this.responseBody = responseBody; | |||
return this; | |||
} | |||
public MockHttpServer doReturnBody(byte[] responseBody) { | |||
this.binaryResponseBody = responseBody; | |||
return this; | |||
} | |||
public MockHttpServer doReturnStatus(int status) { | |||
this.responseStatus = status; | |||
return this; | |||
} | |||
public MockHttpServer doReturnContentType(String contentType) { | |||
this.contentType = contentType; | |||
return this; | |||
} | |||
public String requestPath() { | |||
return requestPath; | |||
} | |||
public Map requestHeaders() { | |||
return requestHeaders; | |||
} | |||
public Map requestParams() { | |||
return requestParams; | |||
} | |||
public int getPort() { | |||
return server.getConnectors()[0].getLocalPort(); | |||
} | |||
} |
@@ -0,0 +1,60 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonarqube.ws.MediaTypes; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class PostRequestTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@Test | |||
public void post_is_post() { | |||
PostRequest request = new PostRequest("api/issues/search"); | |||
assertThat(request.getMethod()).isEqualTo(WsRequest.Method.POST); | |||
} | |||
@Test | |||
public void empty_parts_and_params_by_default() { | |||
PostRequest request = new PostRequest("api/issues/search"); | |||
assertThat(request.getParts()).isEmpty(); | |||
assertThat(request.getParams()).isEmpty(); | |||
} | |||
@Test | |||
public void add_part() throws IOException { | |||
PostRequest request = new PostRequest("api/issues/search"); | |||
File reportFile = temp.newFile(); | |||
request.setPart("report", new PostRequest.Part(MediaTypes.JSON, reportFile)); | |||
assertThat(request.getParts()).hasSize(1); | |||
PostRequest.Part part = request.getParts().get("report"); | |||
assertThat(part.getMediaType()).isEqualTo(MediaTypes.JSON); | |||
assertThat(part.getFile()).isSameAs(reportFile); | |||
} | |||
} |
@@ -1,151 +0,0 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import com.google.common.net.HttpHeaders; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonarqube.ws.MediaTypes; | |||
import org.sonarqube.ws.WsComponents; | |||
import static java.net.HttpURLConnection.HTTP_OK; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonarqube.ws.client.HttpConnector.newDefaultHttpConnector; | |||
import static org.sonarqube.ws.client.HttpConnector.newHttpConnector; | |||
import static org.sonarqube.ws.client.WsRequest.newGetRequest; | |||
public class WsClientTest { | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
MockHttpServer server; | |||
WsClient underTest; | |||
@Before | |||
public void setUp() throws Exception { | |||
server = new MockHttpServer(); | |||
server.start(); | |||
underTest = new WsClient(newDefaultHttpConnector("http://localhost:" + server.getPort())); | |||
} | |||
@After | |||
public void stopServer() { | |||
if (server != null) { | |||
server.stop(); | |||
} | |||
} | |||
@Test | |||
public void return_protobuf_response() throws Exception { | |||
server.doReturnBody( | |||
WsComponents.SearchWsResponse | |||
.newBuilder() | |||
.addComponents(WsComponents.SearchWsResponse.Component.getDefaultInstance()) | |||
.build() | |||
.toByteArray()); | |||
server.doReturnStatus(HTTP_OK); | |||
server.doReturnContentType(MediaTypes.PROTOBUF); | |||
WsComponents.SearchWsResponse response = underTest.execute( | |||
newGetRequest("api/components/search") | |||
.setMediaType(WsRequest.MediaType.PROTOBUF), | |||
WsComponents.SearchWsResponse.parser()); | |||
assertThat(response.getComponentsCount()).isEqualTo(1); | |||
assertThat(server.requestHeaders().get(HttpHeaders.ACCEPT)) | |||
.isEqualTo(MediaTypes.PROTOBUF); | |||
} | |||
@Test | |||
public void return_json_response() throws Exception { | |||
String expectedResponse = "{\"key\":value}"; | |||
server.doReturnBody(expectedResponse); | |||
server.doReturnStatus(HTTP_OK); | |||
server.doReturnContentType(MediaTypes.JSON); | |||
String response = underTest.execute(newGetRequest("api/components/search")); | |||
assertThat(response).isEqualTo(expectedResponse); | |||
assertThat(server.requestHeaders().get(HttpHeaders.ACCEPT)).isEqualTo(MediaTypes.JSON); | |||
} | |||
@Test | |||
public void url_should_not_be_null() { | |||
expectedException.expect(IllegalStateException.class); | |||
expectedException.expectMessage("Server URL must be set"); | |||
new WsClient(newHttpConnector().build()); | |||
} | |||
@Test | |||
public void url_should_not_be_empty() { | |||
expectedException.expect(IllegalStateException.class); | |||
expectedException.expectMessage("Server URL must be set"); | |||
new WsClient(newDefaultHttpConnector("")); | |||
} | |||
@Test | |||
public void test_default_configuration() throws Exception { | |||
underTest = new WsClient(newDefaultHttpConnector("http://localhost:9000")); | |||
HttpRequestFactory requestFactory = ((HttpConnector) underTest.wsConnector).requestFactory; | |||
assertThat(requestFactory.getBaseUrl()).isEqualTo("http://localhost:9000"); | |||
assertThat(requestFactory.getLogin()).isNull(); | |||
assertThat(requestFactory.getPassword()).isNull(); | |||
assertThat(requestFactory.getConnectTimeoutInMilliseconds()).isEqualTo(HttpConnector.DEFAULT_CONNECT_TIMEOUT_MILLISECONDS); | |||
assertThat(requestFactory.getReadTimeoutInMilliseconds()).isEqualTo(HttpConnector.DEFAULT_READ_TIMEOUT_MILLISECONDS); | |||
assertThat(requestFactory.getProxyHost()).isNull(); | |||
assertThat(requestFactory.getProxyPort()).isEqualTo(0); | |||
assertThat(requestFactory.getProxyLogin()).isNull(); | |||
assertThat(requestFactory.getProxyPassword()).isNull(); | |||
} | |||
@Test | |||
public void test_custom_configuration() throws Exception { | |||
underTest = new WsClient(newHttpConnector() | |||
.url("http://localhost:9000") | |||
.login("eric") | |||
.password("pass") | |||
.connectTimeoutMilliseconds(12345) | |||
.readTimeoutMilliseconds(6789) | |||
.proxy("localhost", 2052) | |||
.proxyLogin("proxyLogin") | |||
.proxyPassword("proxyPass") | |||
.build()); | |||
HttpRequestFactory requestFactory = ((HttpConnector) underTest.wsConnector).requestFactory; | |||
assertThat(requestFactory.getBaseUrl()).isEqualTo("http://localhost:9000"); | |||
assertThat(requestFactory.getLogin()).isEqualTo("eric"); | |||
assertThat(requestFactory.getPassword()).isEqualTo("pass"); | |||
assertThat(requestFactory.getConnectTimeoutInMilliseconds()).isEqualTo(12345); | |||
assertThat(requestFactory.getReadTimeoutInMilliseconds()).isEqualTo(6789); | |||
assertThat(requestFactory.getProxyHost()).isEqualTo("localhost"); | |||
assertThat(requestFactory.getProxyPort()).isEqualTo(2052); | |||
assertThat(requestFactory.getProxyLogin()).isEqualTo("proxyLogin"); | |||
assertThat(requestFactory.getProxyPassword()).isEqualTo("proxyPass"); | |||
} | |||
} |
@@ -1,80 +0,0 @@ | |||
/* | |||
* 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.sonarqube.ws.client; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonarqube.ws.client.WsRequest.Method; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonarqube.ws.client.WsRequest.newGetRequest; | |||
import static org.sonarqube.ws.client.WsRequest.newPostRequest; | |||
public class WsRequestTest { | |||
static final String ENDPOINT = "api/issues/search"; | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
WsRequest underTest; | |||
@Test | |||
public void get_request() { | |||
underTest = newGetRequest("api/issues/search"); | |||
assertThat(underTest.getMethod()).isEqualTo(Method.GET); | |||
} | |||
@Test | |||
public void post_request() { | |||
underTest = newPostRequest("api/issues/search"); | |||
assertThat(underTest.getMethod()).isEqualTo(Method.POST); | |||
} | |||
@Test | |||
public void set_non_null_param() { | |||
underTest = newGetRequest("api/issues/search") | |||
.setParam("key", "value"); | |||
assertThat(underTest.getParams().get("key")).isEqualTo("value"); | |||
} | |||
@Test | |||
public void set_null_param_remove_existing_param() { | |||
underTest = newGetRequest(ENDPOINT) | |||
.setParam("key", "value") | |||
.setParam("key", null); | |||
assertThat(underTest.getParams().get("key")).isNull(); | |||
} | |||
@Test | |||
public void fail_if_key_is_null() { | |||
expectedException.expect(NullPointerException.class); | |||
expectedException.expectMessage("a WS parameter key cannot be null"); | |||
underTest = newGetRequest(ENDPOINT) | |||
.setParam(null, "value"); | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
// 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.testing; | |||
option java_package = "org.sonarqube.ws"; | |||
option java_outer_classname = "Testing"; | |||
option optimize_for = SPEED; | |||
message Fake { | |||
optional string label = 1; | |||
} |