import org.sonar.server.user.db.GroupDao;
import org.sonar.server.user.db.UserDao;
import org.sonar.server.user.db.UserGroupDao;
+import org.sonar.server.user.index.UserIndex;
import org.sonar.server.user.index.UserIndexDefinition;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.user.ws.FavoritesWs;
pico.addSingleton(DefaultUserFinder.class);
pico.addSingleton(DefaultUserService.class);
pico.addSingleton(UsersWs.class);
+ pico.addSingleton(org.sonar.server.user.ws.CreateAction.class);
pico.addSingleton(FavoritesWs.class);
pico.addSingleton(UserPropertiesWs.class);
pico.addSingleton(UserIndexDefinition.class);
pico.addSingleton(UserIndexer.class);
+ pico.addSingleton(UserIndex.class);
pico.addSingleton(UserService.class);
pico.addSingleton(UserCreator.class);
import org.sonar.api.ServerComponent;
import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.server.user.index.UserDoc;
+import org.sonar.server.user.index.UserIndex;
import org.sonar.server.user.index.UserIndexer;
+import javax.annotation.CheckForNull;
+
public class UserService implements ServerComponent {
private final UserIndexer userIndexer;
+ private final UserIndex userIndex;
private final UserCreator userCreator;
- public UserService(UserIndexer userIndexer, UserCreator userCreator) {
+ public UserService(UserIndexer userIndexer, UserIndex userIndex, UserCreator userCreator) {
this.userIndexer = userIndexer;
+ this.userIndex = userIndex;
this.userCreator = userCreator;
}
userIndexer.index();
}
+ @CheckForNull
+ public UserDoc getByLogin(String login) {
+ return userIndex.getByLogin(login);
+ }
+
+ @CheckForNull
+ public UserDoc getNullableByLogin(String login) {
+ return userIndex.getNullableByLogin(login);
+ }
+
public void index() {
userIndexer.index();
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.user.index;
+
+import org.elasticsearch.action.get.GetRequestBuilder;
+import org.elasticsearch.action.get.GetResponse;
+import org.sonar.api.ServerComponent;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.exceptions.NotFoundException;
+
+import javax.annotation.CheckForNull;
+
+public class UserIndex implements ServerComponent {
+
+ private final EsClient esClient;
+
+ public UserIndex(EsClient esClient) {
+ this.esClient = esClient;
+ }
+
+ @CheckForNull
+ public UserDoc getNullableByLogin(String login) {
+ GetRequestBuilder request = esClient.prepareGet(UserIndexDefinition.INDEX, UserIndexDefinition.TYPE_USER, login)
+ .setFetchSource(true)
+ .setRouting(login);
+ GetResponse response = request.get();
+ if (response.isExists()) {
+ return new UserDoc(response.getSource());
+ }
+ return null;
+ }
+
+ public UserDoc getByLogin(String login) {
+ UserDoc userDoc = getNullableByLogin(login);
+ if (userDoc == null) {
+ throw new NotFoundException(String.format("User '%s' not found", login));
+ }
+ return userDoc;
+ }
+
+}
package org.sonar.server.user.index;
import org.apache.commons.dbutils.DbUtils;
+import org.elasticsearch.action.get.GetRequestBuilder;
+import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.sonar.core.persistence.DbSession;
import org.sonar.server.db.DbClient;
import org.sonar.server.es.BulkIndexer;
import org.sonar.server.es.EsClient;
+import javax.annotation.CheckForNull;
+
import java.sql.Connection;
import java.util.Iterator;
return maxUpdatedAt;
}
+ @CheckForNull
+ public UserDoc getNullableByKey(String login) {
+ GetRequestBuilder request = esClient.prepareGet()
+ .setType(UserIndexDefinition.INDEX)
+ .setIndex(UserIndexDefinition.TYPE_USER)
+ .setId(login)
+ .setFetchSource(true)
+ .setRouting(login);
+ GetResponse response = request.get();
+ if (response.isExists()) {
+ return new UserDoc(response.getSource());
+ }
+ return null;
+ }
+
private UpdateRequest newUpsertRequest(UserDoc user) {
return new UpdateRequest(UserIndexDefinition.INDEX, UserIndexDefinition.TYPE_USER, user.login())
.doc(user.getFields())
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
-import java.util.Iterator;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
reader = new StringReader(csv);
csvParser = new CSVParser(reader, CSVFormat.DEFAULT);
for (CSVRecord csvRecord : csvParser) {
- for (Iterator<String> iter = csvRecord.iterator(); iter.hasNext();) {
- result.add(iter.next());
+ for (String aCsvRecord : csvRecord) {
+ result.add(aCsvRecord);
}
}
return result;
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.user.ws;
+
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.user.NewUser;
+import org.sonar.server.user.ReactivationException;
+import org.sonar.server.user.UserService;
+
+public class CreateAction implements RequestHandler {
+
+ private static final String PARAM_LOGIN = "login";
+ private static final String PARAM_PASSWORD = "password";
+ private static final String PARAM_PASSWORD_CONFIRMATION = "password_confirmation";
+ private static final String PARAM_NAME = "name";
+ private static final String PARAM_EMAIL = "email";
+ private static final String PARAM_PREVENT_REACTIVATION = "prevent_reactivation";
+
+ private final UserService userService;
+
+ public CreateAction(UserService userService) {
+ this.userService = userService;
+ }
+
+ void define(WebService.NewController controller) {
+ WebService.NewAction action = controller.createAction("create")
+ .setDescription("Create a user. Requires Administer System permission")
+ .setSince("3.7")
+ .setPost(true)
+ .setHandler(this);
+
+ action.createParam(PARAM_LOGIN)
+ .setDescription("User login")
+ .setRequired(true)
+ .setExampleValue("myuser");
+
+ action.createParam(PARAM_PASSWORD)
+ .setDescription("User password")
+ .setRequired(true)
+ .setExampleValue("mypassword");
+
+ action.createParam(PARAM_PASSWORD_CONFIRMATION)
+ .setDescription("Must be the same value as \"password\"")
+ .setRequired(true)
+ .setExampleValue("mypassword");
+
+ action.createParam(PARAM_NAME)
+ .setDescription("User name")
+ .setRequired(true)
+ .setExampleValue("My Name");
+
+ action.createParam(PARAM_EMAIL)
+ .setDescription("User email")
+ .setExampleValue("myname@email.com");
+
+ action.createParam(PARAM_PREVENT_REACTIVATION)
+ .setDescription("If set to true and if the user has been removed, a status 409 will be returned")
+ .setDefaultValue(false)
+ .setBooleanPossibleValues();
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ NewUser newUser = NewUser.create()
+ .setLogin(request.mandatoryParam(PARAM_LOGIN))
+ .setName(request.mandatoryParam(PARAM_NAME))
+ .setEmail(request.param(PARAM_EMAIL))
+ .setPassword(request.mandatoryParam(PARAM_PASSWORD))
+ .setPasswordConfirmation(request.mandatoryParam(PARAM_PASSWORD_CONFIRMATION))
+ .setPreventReactivation(request.mandatoryParamAsBoolean(PARAM_PREVENT_REACTIVATION));
+
+ try {
+ userService.create(newUser);
+ } catch (ReactivationException e) {
+ // write409(response, e.ruleKey());
+ }
+ }
+
+ // private void writeResponse(Response response, RuleKey ruleKey) {
+ // Rule rule = service.getNonNullByKey(ruleKey);
+ // JsonWriter json = response.newJsonWriter().beginObject().name("rule");
+ // mapping.write(rule, json, null /* TODO replace by SearchOptions immutable constant */);
+ // json.endObject().close();
+ // }
+ //
+ // private void write409(Response response, RuleKey ruleKey) {
+ // Rule rule = service.getNonNullByKey(ruleKey);
+ //
+ // Response.Stream stream = response.stream();
+ // stream.setStatus(409);
+ // stream.setMediaType(MimeTypes.JSON);
+ // JsonWriter json = JsonWriter.of(new OutputStreamWriter(stream.output())).beginObject().name("rule");
+ // mapping.write(rule, json, null /* TODO replace by SearchOptions immutable constant */);
+ // json.endObject().close();
+ // }
+}
public class UsersWs implements WebService {
+ private final CreateAction createAction;
+
+ public UsersWs(CreateAction createAction) {
+ this.createAction = createAction;
+ }
+
@Override
public void define(Context context) {
NewController controller = context.createController("api/users")
.setDescription("Users management");
defineSearchAction(controller);
- defineCreateAction(controller);
+ createAction.define(controller);
defineUpdateAction(controller);
defineDeactivateAction(controller);
RailsHandler.addFormatParam(action);
}
- private void defineCreateAction(NewController controller) {
- NewAction action = controller.createAction("create")
- .setDescription("Create a user. Requires Administer System permission")
- .setSince("3.7")
- .setPost(true)
- .setHandler(RailsHandler.INSTANCE);
-
- action.createParam("login")
- .setDescription("User login")
- .setRequired(true)
- .setExampleValue("myuser");
-
- action.createParam("password")
- .setDescription("User password")
- .setRequired(true)
- .setExampleValue("mypassword");
-
- action.createParam("password_confirmation")
- .setDescription("Must be the same value as \"password\"")
- .setRequired(true)
- .setExampleValue("mypassword");
-
- action.createParam("name")
- .setDescription("User name")
- .setRequired(true)
- .setExampleValue("My Name");
-
- action.createParam("email")
- .setDescription("User email")
- .setExampleValue("myname@email.com");
-
- RailsHandler.addFormatParam(action);
- }
-
private void defineUpdateAction(NewController controller) {
NewAction action = controller.createAction("update")
.setDescription("Update a user. Requires Administer System permission")
.setScmAccounts(newArrayList("u1", "u_1")));
}
+ @Test
+ public void get_nullable_by_login() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+ GroupDto userGroup = new GroupDto().setName(CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE);
+ dbClient.groupDao().insert(session, userGroup);
+ session.commit();
+
+ service.create(NewUser.create()
+ .setLogin("user")
+ .setName("User")
+ .setEmail("user@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password")
+ .setScmAccounts(newArrayList("u1", "u_1")));
+
+ assertThat(service.getNullableByLogin("user")).isNotNull();
+ }
+
+ @Test
+ public void get_by_login() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+ GroupDto userGroup = new GroupDto().setName(CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE);
+ dbClient.groupDao().insert(session, userGroup);
+ session.commit();
+
+ service.create(NewUser.create()
+ .setLogin("user")
+ .setName("User")
+ .setEmail("user@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password")
+ .setScmAccounts(newArrayList("u1", "u_1")));
+
+ assertThat(service.getByLogin("user")).isNotNull();
+ }
+
@Test
public void index() throws Exception {
UserDto userDto = new UserDto().setLogin("user").setEmail("user@mail.com").setCreatedAt(System.currentTimeMillis()).setUpdatedAt(System.currentTimeMillis());
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.user.index;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.config.Settings;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.exceptions.NotFoundException;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+
+public class UserIndexTest {
+
+ @Rule
+ public EsTester esTester = new EsTester().addDefinitions(new UserIndexDefinition(new Settings()));
+
+ private UserIndex index;
+
+ @Before
+ public void setUp() {
+ index = new UserIndex(esTester.client());
+ }
+
+ @Test
+ public void get_nullable_by_login() throws Exception {
+ esTester.putDocuments(UserIndexDefinition.INDEX, UserIndexDefinition.TYPE_USER, this.getClass(), "get_nullable_by_login.json");
+
+ UserDoc userDoc = index.getNullableByLogin("user1");
+ assertThat(userDoc).isNotNull();
+ assertThat(userDoc.login()).isEqualTo("user1");
+ assertThat(userDoc.name()).isEqualTo("User1");
+ assertThat(userDoc.email()).isEqualTo("user1@mail.com");
+ assertThat(userDoc.active()).isTrue();
+ assertThat(userDoc.scmAccounts()).containsOnly("user_1", "u1");
+ assertThat(userDoc.createdAt()).isEqualTo(1500000000000L);
+ assertThat(userDoc.updatedAt()).isEqualTo(1500000000000L);
+
+ assertThat(index.getNullableByLogin("unknown")).isNull();
+ }
+
+ @Test
+ public void get_by_login() throws Exception {
+ esTester.putDocuments(UserIndexDefinition.INDEX, UserIndexDefinition.TYPE_USER, this.getClass(), "get_nullable_by_login.json");
+
+ UserDoc userDoc = index.getByLogin("user1");
+ assertThat(userDoc).isNotNull();
+ assertThat(userDoc.login()).isEqualTo("user1");
+ assertThat(userDoc.name()).isEqualTo("User1");
+ assertThat(userDoc.email()).isEqualTo("user1@mail.com");
+ assertThat(userDoc.active()).isTrue();
+ assertThat(userDoc.scmAccounts()).containsOnly("user_1", "u1");
+ assertThat(userDoc.createdAt()).isEqualTo(1500000000000L);
+ assertThat(userDoc.updatedAt()).isEqualTo(1500000000000L);
+ }
+
+ @Test
+ public void fail_to_get_by_login_on_unknown_user() throws Exception {
+ try {
+ index.getByLogin("unknown");
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("User 'unknown' not found");
+ }
+ }
+
+}
List<UserDoc> docs = esTester.getDocuments("users", "user", UserDoc.class);
assertThat(docs).hasSize(1);
UserDoc doc = docs.get(0);
+ assertThat(doc.login()).isEqualTo("user1");
assertThat(doc.name()).isEqualTo("User1");
assertThat(doc.email()).isEqualTo("user1@mail.com");
assertThat(doc.active()).isTrue();
import org.junit.Test;
import org.sonar.api.server.ws.RailsHandler;
import org.sonar.api.server.ws.WebService;
+import org.sonar.server.user.UserService;
import org.sonar.server.ws.WsTester;
import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
public class UsersWsTest {
@Before
public void setUp() throws Exception {
- WsTester tester = new WsTester(new UsersWs());
+ WsTester tester = new WsTester(new UsersWs(new CreateAction(mock(UserService.class))));
controller = tester.controller("api/users");
}
WebService.Action action = controller.action("create");
assertThat(action).isNotNull();
assertThat(action.isPost()).isTrue();
- assertThat(action.handler()).isInstanceOf(RailsHandler.class);
assertThat(action.params()).hasSize(6);
}
--- /dev/null
+{
+ "login": "user1",
+ "name": "User1",
+ "email": "user1@mail.com",
+ "active": true,
+ "scmAccounts": ["user_1", "u1"],
+ "createdAt": 1500000000000,
+ "updatedAt": 1500000000000
+}