Quellcode durchsuchen

SONAR-8097 add WS /api/organizations/create

tags/6.2-RC1
Sébastien Lesaint vor 7 Jahren
Ursprung
Commit
ea974db813

+ 202
- 0
server/sonar-server/src/main/java/org/sonar/server/organization/ws/CreateAction.java Datei anzeigen

@@ -0,0 +1,202 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.organization.ws;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Organizations.CreateWsResponse;

import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.Math.min;
import static org.sonar.core.util.Slug.slugify;
import static org.sonar.server.ws.WsUtils.writeProtobuf;

public class CreateAction implements OrganizationsAction {
private static final String ACTION = "create";
private static final String PARAM_NAME = "name";
private static final String PARAM_KEY = "key";
private static final String PARAM_DESCRIPTION = "description";
private static final String PARAM_URL = "url";
private static final String PARAM_AVATAR_URL = "avatar";
private static final int NAME_MIN_LENGTH = 2;
private static final int NAME_MAX_LENGTH = 64;
private static final int KEY_MIN_LENGTH = 2;
private static final int KEY_MAX_LENGTH = 32;

private final UserSession userSession;
private final DbClient dbClient;
private final UuidFactory uuidFactory;
private final System2 system2;

public CreateAction(UserSession userSession, DbClient dbClient, UuidFactory uuidFactory, System2 system2) {
this.userSession = userSession;
this.dbClient = dbClient;
this.uuidFactory = uuidFactory;
this.system2 = system2;
}

@Override
public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction(ACTION)
.setPost(true)
.setDescription("Create an organization.<br /> Requires 'Administer System' permission.")
.setResponseExample(getClass().getResource("example-create.json"))
.setInternal(true)
.setSince("6.2")
.setHandler(this);

action.createParam(PARAM_NAME)
.setRequired(true)
.setDescription("Name of the organization. <br />" +
"It must be between 2 and 64 chars longs.")
.setExampleValue("Foo Company");

action.createParam(PARAM_KEY)
.setRequired(false)
.setDescription("Key of the organization. <br />" +
"The key is unique to the whole SonarQube. <br/>" +
"When not specified, the key is computed from the name. <br />" +
"Otherwise, it must be between 2 and 32 chars long. All chars must be lower-case letters (a to z), digits or dash (but dash can neither be trailing nor heading)")
.setExampleValue("foo-company");

action.createParam(PARAM_DESCRIPTION)
.setRequired(false)
.setDescription("Description of the organization.<br/> It must be less than 256 chars long.")
.setExampleValue("The Foo company produces quality software for Bar.");

action.createParam(PARAM_URL)
.setRequired(false)
.setDescription("URL of the organization.<br/> It must be less than 256 chars long.")
.setExampleValue("https://www.foo.com");

action.createParam(PARAM_AVATAR_URL)
.setRequired(false)
.setDescription("URL of the organization avatar.<br/> It must be less than 256 chars long.")
.setExampleValue("https://www.foo.com/foo.png");
}

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

String name = getAndCheckName(request);
String requestKey = getAndCheckKey(request);
String key = useOrGenerateKey(requestKey, name);
checkParamMaxLength(request, PARAM_DESCRIPTION, 256);
checkParamMaxLength(request, PARAM_URL, 256);
checkParamMaxLength(request, PARAM_AVATAR_URL, 256);

try (DbSession dbSession = dbClient.openSession(false)) {
checkKeyIsNotUsed(dbSession, key, requestKey, name);

OrganizationDto dto = createOrganizationDto(request, name, key, system2.now());
dbClient.organizationDao().insert(dbSession, dto);
dbSession.commit();

writeResponse(request, response, dto);
}
}

private static void checkParamMaxLength(Request request, String key, int maxLength) {
String value = request.param(key);
if (value != null) {
checkArgument(value.length() <= maxLength, "%s '%s' must be at most %s chars long", key, value, maxLength);
}
}

private static String getAndCheckName(Request request) {
String name = request.mandatoryParam(PARAM_NAME);
checkArgument(name.length() >= NAME_MIN_LENGTH, "Name '%s' must be at least %s chars long", name, NAME_MIN_LENGTH);
checkArgument(name.length() <= NAME_MAX_LENGTH, "Name '%s' must be at most %s chars long", name, NAME_MAX_LENGTH);
return name;
}

@CheckForNull
private static String getAndCheckKey(Request request) {
String rqstKey = request.param(PARAM_KEY);
if (rqstKey != null) {
checkArgument(rqstKey.length() >= KEY_MIN_LENGTH, "Key '%s' must be at least %s chars long", rqstKey, KEY_MIN_LENGTH);
checkArgument(rqstKey.length() <= KEY_MAX_LENGTH, "Key '%s' must be at most %s chars long", rqstKey, KEY_MAX_LENGTH);
checkArgument(slugify(rqstKey).equals(rqstKey), "Key '%s' contains at least one invalid char", rqstKey);
}
return rqstKey;
}

private static String useOrGenerateKey(@Nullable String key, String name) {
if (key == null) {
return slugify(name.substring(0, min(name.length(), KEY_MAX_LENGTH)));
}
return key;
}

private void checkKeyIsNotUsed(DbSession dbSession, String key, @Nullable String requestKey, String name) {
boolean isUsed = checkKeyIsUsed(dbSession, key);
checkArgument(requestKey == null || !isUsed, "Key '%s' is already used. Specify another one.", key);
checkArgument(requestKey != null || !isUsed, "Key '%s' generated from name '%s' is already used. Specify one.", key, name);
}

private boolean checkKeyIsUsed(DbSession dbSession, String key) {
return dbClient.organizationDao().selectByKey(dbSession, key).isPresent();
}

private OrganizationDto createOrganizationDto(Request request, String name, String key, long now) {
return new OrganizationDto()
.setUuid(uuidFactory.create())
.setName(name)
.setKey(key)
.setDescription(request.param(PARAM_DESCRIPTION))
.setUrl(request.param(PARAM_URL))
.setAvatarUrl(request.param(PARAM_AVATAR_URL))
.setCreatedAt(now)
.setUpdatedAt(now);
}

private void writeResponse(Request request, Response response, OrganizationDto dto) {
CreateWsResponse.Organization.Builder builder = CreateWsResponse.Organization.newBuilder()
.setId(dto.getUuid())
.setName(dto.getName())
.setKey(dto.getKey());
if (dto.getDescription() != null) {
builder.setDescription(dto.getDescription());
}
if (dto.getUrl() != null) {
builder.setUrl(dto.getUrl());
}
if (dto.getAvatarUrl() != null) {
builder.setAvatar(dto.getAvatarUrl());
}
writeProtobuf(CreateWsResponse.newBuilder()
.setOrganization(builder.build())
.build(),
request,
response);
}

}

+ 25
- 0
server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsAction.java Datei anzeigen

@@ -0,0 +1,25 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.organization.ws;

import org.sonar.server.ws.WsAction;

public interface OrganizationsAction extends WsAction {
}

+ 41
- 0
server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWs.java Datei anzeigen

@@ -0,0 +1,41 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.organization.ws;

import org.sonar.api.server.ws.WebService;

public class OrganizationsWs implements WebService {
private final OrganizationsAction[] actions;

public OrganizationsWs(OrganizationsAction... actions) {
this.actions = actions;
}

@Override
public void define(Context context) {
NewController controller = context.createController("api/organizations")
.setSince("6.2")
.setDescription("Manage organizations.");
for (OrganizationsAction action : actions) {
action.define(controller);
}
controller.done();
}
}

+ 34
- 0
server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsModule.java Datei anzeigen

@@ -0,0 +1,34 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.organization.ws;

import org.sonar.core.platform.Module;

public class OrganizationsWsModule extends Module {

@Override
protected void configureModule() {
add(
OrganizationsWs.class,
// actions
CreateAction.class);
}

}

+ 23
- 0
server/sonar-server/src/main/java/org/sonar/server/organization/ws/package-info.java Datei anzeigen

@@ -0,0 +1,23 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program 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.
*
* This program 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.sonar.server.organization.ws;

import javax.annotation.ParametersAreNonnullByDefault;

+ 4
- 0
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java Datei anzeigen

@@ -135,6 +135,7 @@ import org.sonar.server.notification.NotificationDaemon;
import org.sonar.server.notification.NotificationService;
import org.sonar.server.notification.email.AlertsEmailTemplate;
import org.sonar.server.notification.email.EmailNotificationChannel;
import org.sonar.server.organization.ws.OrganizationsWsModule;
import org.sonar.server.permission.PermissionService;
import org.sonar.server.permission.PermissionUpdater;
import org.sonar.server.permission.ws.PermissionsWsModule;
@@ -324,6 +325,9 @@ public class PlatformLevel4 extends PlatformLevel {
// update center
UpdateCenterModule.class,

// organizations
OrganizationsWsModule.class,

// quality profile
ActiveRuleIndexer.class,
XMLProfileParser.class,

+ 10
- 0
server/sonar-server/src/main/resources/org/sonar/server/organization/ws/example-create.json Datei anzeigen

@@ -0,0 +1,10 @@
{
"organization": {
"id": "AU-Tpxb--iU5OvuD2FLy",
"key": "foo-company",
"name": "Foo Company",
"description": "The Foo company produces quality software for Bar.",
"url": "https://www.foo.com",
"avatar": "https://www.foo.com/foo.png"
}
}

+ 466
- 0
server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java Datei anzeigen

@@ -0,0 +1,466 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.organization.ws;

import java.io.IOException;
import java.net.URISyntaxException;
import javax.annotation.Nullable;
import org.apache.commons.io.IOUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.util.UuidFactory;
import org.sonar.core.util.Uuids;
import org.sonar.db.DbTester;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.MediaTypes;
import org.sonarqube.ws.Organizations.CreateWsResponse;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.test.JsonAssert.assertJson;

public class CreateActionTest {
private static final String SOME_UUID = "uuid";
private static final long SOME_DATE = 1_200_000L;
private static final String STRING_65_CHARS_LONG = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz1234567890-ab";
private static final String STRING_257_CHARS_LONG = String.format("%1$257.257s", "a").replace(" ", "a");

@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
@Rule
public final DbTester dbTester = DbTester.create(System2.INSTANCE);
@Rule
public ExpectedException expectedException = ExpectedException.none();

private UuidFactory uuidFactory = mock(UuidFactory.class);
private System2 system2 = mock(System2.class);
private CreateAction underTest = new CreateAction(userSession, dbTester.getDbClient(), uuidFactory, system2);
private WsActionTester wsTester = new WsActionTester(underTest);

@Test
public void verify_define() {
WebService.Action action = wsTester.getDef();
assertThat(action.key()).isEqualTo("create");
assertThat(action.isPost()).isTrue();
assertThat(action.description()).isEqualTo("Create an organization.<br /> Requires 'Administer System' permission.");
assertThat(action.isInternal()).isTrue();
assertThat(action.since()).isEqualTo("6.2");
assertThat(action.handler()).isEqualTo(underTest);
assertThat(action.params()).hasSize(5);
assertThat(action.responseExample()).isEqualTo(getClass().getResource("example-create.json"));
assertThat(action.param("name"))
.matches(WebService.Param::isRequired)
.matches(param -> "Foo Company".equals(param.exampleValue()))
.matches(param -> param.description() != null);
assertThat(action.param("key"))
.matches(param -> !param.isRequired())
.matches(param -> "foo-company".equals(param.exampleValue()))
.matches(param -> param.description() != null);
assertThat(action.param("description"))
.matches(param -> !param.isRequired())
.matches(param -> "The Foo company produces quality software for Bar.".equals(param.exampleValue()))
.matches(param -> param.description() != null);
assertThat(action.param("url"))
.matches(param -> !param.isRequired())
.matches(param -> "https://www.foo.com".equals(param.exampleValue()))
.matches(param -> param.description() != null);
assertThat(action.param("avatar"))
.matches(param -> !param.isRequired())
.matches(param -> "https://www.foo.com/foo.png".equals(param.exampleValue()))
.matches(param -> param.description() != null);
}

@Test
public void verify_response_example() throws URISyntaxException, IOException {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(Uuids.UUID_EXAMPLE_01, SOME_DATE);

String response = executeJsonRequest("Foo Company", "foo-company", "The Foo company produces quality software for Bar.", "https://www.foo.com", "https://www.foo.com/foo.png");

assertJson(response).isSimilarTo(IOUtils.toString(getClass().getResource("example-create.json")));
}

@Test
public void request_fails_if_user_does_not_have_SYSTEM_ADMIN_permission() {
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");

executeRequest("name");
}

@Test
public void request_fails_if_name_param_is_missing() {
giveUserSystemAdminPermission();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("The 'name' parameter is missing");

executeRequest(null);
}

@Test
public void request_fails_if_name_is_one_char_long() {
giveUserSystemAdminPermission();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Name 'a' must be at least 2 chars long");

executeRequest("a");
}

@Test
public void request_succeeds_if_name_is_two_chars_long() {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(SOME_UUID, SOME_DATE);

verifyResponseAndDb(executeRequest("ab"), SOME_UUID, "ab", "ab", SOME_DATE);
}

@Test
public void request_fails_if_name_is_65_chars_long() {
giveUserSystemAdminPermission();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Name '" + STRING_65_CHARS_LONG + "' must be at most 64 chars long");

executeRequest(STRING_65_CHARS_LONG);
}

@Test
public void request_succeeds_if_name_is_64_char_long() {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(SOME_UUID, SOME_DATE);

String name = STRING_65_CHARS_LONG.substring(0, 64);

verifyResponseAndDb(executeRequest(name), SOME_UUID, name, name.substring(0, 32), SOME_DATE);
}

@Test
public void request_fails_if_key_one_char_long() {
giveUserSystemAdminPermission();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Key 'a' must be at least 2 chars long");

executeRequest("foo", "a");
}

@Test
public void request_fails_if_key_is_33_chars_long() {
giveUserSystemAdminPermission();

String key = STRING_65_CHARS_LONG.substring(0, 33);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Key '" + key + "' must be at most 32 chars long");

executeRequest("foo", key);
}

@Test
public void request_succeeds_if_key_is_2_chars_long() {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(SOME_UUID, SOME_DATE);

verifyResponseAndDb(executeRequest("foo", "ab"), SOME_UUID, "foo", "ab", SOME_DATE);
}

@Test
public void requests_succeeds_if_key_is_32_chars_long() {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(SOME_UUID, SOME_DATE);

String key = STRING_65_CHARS_LONG.substring(0, 32);

verifyResponseAndDb(executeRequest("foo", key), SOME_UUID, "foo", key, SOME_DATE);
}

@Test
public void requests_fails_if_key_contains_non_ascii_chars_but_dash() {
giveUserSystemAdminPermission();

requestFailsWithInvalidCharInKey("ab@");
}

@Test
public void request_fails_if_key_starts_with_a_dash() {
giveUserSystemAdminPermission();

requestFailsWithInvalidCharInKey("-ab");
}

@Test
public void request_fails_if_key_ends_with_a_dash() {
giveUserSystemAdminPermission();

requestFailsWithInvalidCharInKey("ab-");
}

@Test
public void request_fails_if_key_contains_space() {
giveUserSystemAdminPermission();

requestFailsWithInvalidCharInKey("a b");
}

private void requestFailsWithInvalidCharInKey(String key) {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Key '" + key + "' contains at least one invalid char");

executeRequest("foo", key);
}

@Test
public void request_fails_if_key_is_specified_and_already_exists_in_DB() {
giveUserSystemAdminPermission();
String key = "the-key";
insertOrganization(key);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Key '" + key + "' is already used. Specify another one.");

executeRequest("foo", key);
}

@Test
public void request_fails_if_key_computed_from_name_already_exists_in_DB() {
giveUserSystemAdminPermission();
String key = STRING_65_CHARS_LONG.substring(0, 32);
insertOrganization(key);

String name = STRING_65_CHARS_LONG.substring(0, 64);

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Key '" + key + "' generated from name '" + name + "' is already used. Specify one.");

executeRequest(name);
}

@Test
public void request_succeeds_if_description_url_and_avatar_are_not_specified() {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(SOME_UUID, SOME_DATE);

CreateWsResponse response = executeRequest("foo", "bar", null, null, null);
verifyResponseAndDb(response, SOME_UUID, "foo", "bar", null, null, null, SOME_DATE);
}

@Test
public void request_succeeds_if_description_url_and_avatar_are_specified() {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(SOME_UUID, SOME_DATE);

CreateWsResponse response = executeRequest("foo", "bar", "moo", "doo", "boo");
verifyResponseAndDb(response, SOME_UUID, "foo", "bar", "moo", "doo", "boo", SOME_DATE);
}

@Test
public void request_succeeds_to_generate_key_from_name_more_then_32_chars_long() {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(SOME_UUID, SOME_DATE);

String name = STRING_65_CHARS_LONG.substring(0, 33);

CreateWsResponse response = executeRequest(name);
verifyResponseAndDb(response, SOME_UUID, name, name.substring(0, 32), SOME_DATE);
}

@Test
public void request_generates_key_ignoring_multiple_following_spaces() {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(SOME_UUID, SOME_DATE);

String name = "ab cd";

CreateWsResponse response = executeRequest(name);
verifyResponseAndDb(response, SOME_UUID, name, "ab-cd", SOME_DATE);
}

@Test
public void request_fails_if_description_is_257_chars_long() {
giveUserSystemAdminPermission();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("description '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");

executeRequest("foo", "bar", STRING_257_CHARS_LONG, null, null);
}

@Test
public void request_succeeds_if_description_is_256_chars_long() {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(SOME_UUID, SOME_DATE);
String description = STRING_257_CHARS_LONG.substring(0, 256);

CreateWsResponse response = executeRequest("foo", "bar", description, null, null);
verifyResponseAndDb(response, SOME_UUID, "foo", "bar", description, null, null, SOME_DATE);
}

@Test
public void request_fails_if_url_is_257_chars_long() {
giveUserSystemAdminPermission();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("url '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");

executeRequest("foo", "bar", null, STRING_257_CHARS_LONG, null);
}

@Test
public void request_succeeds_if_url_is_256_chars_long() {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(SOME_UUID, SOME_DATE);
String url = STRING_257_CHARS_LONG.substring(0, 256);

CreateWsResponse response = executeRequest("foo", "bar", null, url, null);
verifyResponseAndDb(response, SOME_UUID, "foo", "bar", null, url, null, SOME_DATE);
}

@Test
public void request_fails_if_avatar_is_257_chars_long() {
giveUserSystemAdminPermission();

expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("avatar '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");

executeRequest("foo", "bar", null, null, STRING_257_CHARS_LONG);
}

@Test
public void request_succeeds_if_avatar_is_256_chars_long() {
giveUserSystemAdminPermission();
mockForSuccessfulInsert(SOME_UUID, SOME_DATE);
String avatar = STRING_257_CHARS_LONG.substring(0, 256);

CreateWsResponse response = executeRequest("foo", "bar", null, null, avatar);
verifyResponseAndDb(response, SOME_UUID, "foo", "bar", null, null, avatar, SOME_DATE);
}

private void giveUserSystemAdminPermission() {
userSession.setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
}

private void mockForSuccessfulInsert(String uuid, long now) {
when(uuidFactory.create()).thenReturn(uuid);
when(system2.now()).thenReturn(now);
}

private CreateWsResponse executeRequest(@Nullable String name, @Nullable String key) {
return executeRequest(name, key, null, null, null);
}

private CreateWsResponse executeRequest(@Nullable String name) {
return executeRequest(name, null, null, null, null);
}

private CreateWsResponse executeRequest(@Nullable String name, @Nullable String key, @Nullable String description, @Nullable String url, @Nullable String avatar) {
TestRequest request = wsTester.newRequest()
.setMediaType(MediaTypes.PROTOBUF);
populateRequest(name, key, description, url, avatar, request);
try {
return CreateWsResponse.parseFrom(request.execute().getInputStream());
} catch (IOException e) {
throw new IllegalStateException(e);
}
}

private String executeJsonRequest(@Nullable String name, @Nullable String key, @Nullable String description, @Nullable String url, @Nullable String avatar) {
TestRequest request = wsTester.newRequest()
.setMediaType(MediaTypes.JSON);
populateRequest(name, key, description, url, avatar, request);
return request.execute().getInput();
}

private static void populateRequest(@Nullable String name, @Nullable String key, @Nullable String description, @Nullable String url, @Nullable String avatar,
TestRequest request) {
setParam(request, "name", name);
setParam(request, "key", key);
setParam(request, "description", description);
setParam(request, "url", url);
setParam(request, "avatar", avatar);
}

private static void setParam(TestRequest request, String param, @Nullable String value) {
if (value != null) {
request.setParam(param, value);
}
}

private void verifyResponseAndDb(CreateWsResponse response,
String uuid, String name, String key,
long createdAt) {
verifyResponseAndDb(response, uuid, name, key, null, null, null, createdAt);
}

private void verifyResponseAndDb(CreateWsResponse response,
String id, String name, String key,
@Nullable String description, @Nullable String url, @Nullable String avatar,
long createdAt) {
CreateWsResponse.Organization organization = response.getOrganization();
assertThat(organization.getId()).isEqualTo(id);
assertThat(organization.getName()).isEqualTo(name);
assertThat(organization.getKey()).isEqualTo(key);
if (description == null) {
assertThat(organization.hasDescription()).isFalse();
} else {
assertThat(organization.getDescription()).isEqualTo(description);
}
if (url == null) {
assertThat(organization.hasUrl()).isFalse();
} else {
assertThat(organization.getUrl()).isEqualTo(url);
}
if (avatar == null) {
assertThat(organization.hasAvatar()).isFalse();
} else {
assertThat(organization.getAvatar()).isEqualTo(avatar);
}

OrganizationDto dto = dbTester.getDbClient().organizationDao().selectByUuid(dbTester.getSession(), id).get();
assertThat(dto.getUuid()).isEqualTo(id);
assertThat(dto.getKey()).isEqualTo(key);
assertThat(dto.getName()).isEqualTo(name);
assertThat(dto.getDescription()).isEqualTo(description);
assertThat(dto.getUrl()).isEqualTo(url);
assertThat(dto.getAvatarUrl()).isEqualTo(avatar);
assertThat(dto.getCreatedAt()).isEqualTo(createdAt);
assertThat(dto.getUpdatedAt()).isEqualTo(createdAt);
}

private void insertOrganization(String key) {
dbTester.getDbClient().organizationDao().insert(dbTester.getSession(), new OrganizationDto()
.setUuid(key + "_uuid")
.setKey(key)
.setName(key + "_name")
.setCreatedAt((long) key.hashCode())
.setUpdatedAt((long) key.hashCode()));
dbTester.commit();
}
}

+ 39
- 0
sonar-ws/src/main/protobuf/ws-organizations.proto Datei anzeigen

@@ -0,0 +1,39 @@
// 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.organizations;

option java_package = "org.sonarqube.ws";
option java_outer_classname = "Organizations";
option optimize_for = SPEED;

// WS api/organizations/create
message CreateWsResponse {
optional Organization organization = 1;

message Organization {
optional string id = 1;
optional string key = 2;
optional string name = 3;
optional string description = 4;
optional string url = 5;
optional string avatar = 6;
}
}

Laden…
Abbrechen
Speichern