From cfe397774d465f2cf06a799f2223d3e250284d54 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 12 Jun 2017 11:16:11 +0200 Subject: [PATCH] SONAR-9356 skipOnboardingTutorial updates user flag --- .../main/java/org/sonar/db/user/UserDao.java | 1 + .../user/ws/SkipOnboardingTutorialAction.java | 36 ++++++- .../ws/SkipOnboardingTutorialActionTest.java | 102 ++++++++++++++++-- .../ws/client/user/UsersWsParameters.java | 2 +- 4 files changed, 129 insertions(+), 12 deletions(-) diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java index f45573f7dc5..ecc767b006f 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java @@ -45,6 +45,7 @@ public class UserDao implements Dao { this.system2 = system2; } + @CheckForNull public UserDto selectUserById(DbSession session, int userId) { return mapper(session).selectUser(userId); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/SkipOnboardingTutorialAction.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/SkipOnboardingTutorialAction.java index 39359a9f153..003208121d2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/SkipOnboardingTutorialAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/SkipOnboardingTutorialAction.java @@ -22,22 +22,52 @@ package org.sonar.server.user.ws; 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.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.user.UserDto; +import org.sonar.server.user.UserSession; + +import static com.google.common.base.Preconditions.checkState; +import static org.sonarqube.ws.client.user.UsersWsParameters.ACTION_SKIP_ONBOARDING_TUTORIAL; public class SkipOnboardingTutorialAction implements UsersWsAction { + private final UserSession userSession; + private final DbClient dbClient; + private final System2 system2; + + public SkipOnboardingTutorialAction(UserSession userSession, DbClient dbClient, System2 system2) { + this.userSession = userSession; + this.dbClient = dbClient; + this.system2 = system2; + } + @Override public void define(WebService.NewController context) { - context.createAction("skipOnboardingTutorial") + context.createAction(ACTION_SKIP_ONBOARDING_TUTORIAL) .setPost(true) .setInternal(true) - .setDescription("Stores that the user has skipped the onboarding tutorial and does not want to see it after future logins." + - " Calling this webservice several times will silently ignore subsequent requests.") + .setDescription("Stores that the user has skipped the onboarding tutorial and does not want to see it after future logins.
" + + "Requires authentication.") .setSince("6.5") .setHandler(this); } @Override public void handle(Request request, Response response) throws Exception { + userSession.checkLoggedIn(); + try (DbSession dbSession = dbClient.openSession(false)) { + String userLogin = userSession.getLogin(); + UserDto userDto = dbClient.userDao().selectActiveUserByLogin(dbSession, userLogin); + checkState(userDto != null, "User login '%s' cannot be found", userLogin); + if (!userDto.isOnboarded()) { + userDto.setOnboarded(true); + userDto.setUpdatedAt(system2.now()); + dbClient.userDao().update(dbSession, userDto); + dbSession.commit(); + } + } response.noContent(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ws/SkipOnboardingTutorialActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ws/SkipOnboardingTutorialActionTest.java index 85985b76ec0..df97cd664de 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/ws/SkipOnboardingTutorialActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/ws/SkipOnboardingTutorialActionTest.java @@ -19,8 +19,15 @@ */ package org.sonar.server.user.ws; +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.internal.TestSystem2; +import org.sonar.db.DbTester; +import org.sonar.db.user.UserDto; +import org.sonar.server.exceptions.UnauthorizedException; +import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestResponse; import org.sonar.server.ws.WsActionTester; @@ -28,21 +35,100 @@ import static org.assertj.core.api.Assertions.assertThat; public class SkipOnboardingTutorialActionTest { + private final static long PAST = 100_000_000_000L; + private final static long NOW = 500_000_000_000L; + + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + + @Rule + public DbTester db = DbTester.create(); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private TestSystem2 system2 = new TestSystem2().setNow(NOW); + + private WsActionTester ws = new WsActionTester(new SkipOnboardingTutorialAction(userSession, db.getDbClient(), system2)); + + @Test + public void mark_user_as_onboarded() { + UserDto user = db.users().insertUser(u -> u + .setOnboarded(false) + .setUpdatedAt(PAST)); + userSession.logIn(user); + + call(); + + UserDto userDto = selectUser(user.getLogin()); + assertThat(userDto.isOnboarded()).isEqualTo(true); + assertThat(userDto.getUpdatedAt()).isEqualTo(NOW); + } + + @Test + public void does_nothing_if_user_already_onboarded() { + UserDto user = db.users().insertUser(u -> u + .setOnboarded(true) + .setUpdatedAt(PAST)); + userSession.logIn(user); + + call(); + + UserDto userDto = selectUser(user.getLogin()); + assertThat(userDto.isOnboarded()).isEqualTo(true); + assertThat(userDto.getUpdatedAt()).isEqualTo(PAST); + } + + @Test + public void fail_for_anonymous() { + userSession.anonymous(); + expectedException.expect(UnauthorizedException.class); + expectedException.expectMessage("Authentication is required"); + + call(); + } + + @Test + public void fail_with_ISE_when_user_login_in_db_does_not_exist() { + db.users().insertUser(usert -> usert.setLogin("another")); + userSession.logIn("obiwan.kenobi"); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("User login 'obiwan.kenobi' cannot be found"); + + call(); + } + @Test - public void should_have_a_good_definition() { - WsActionTester ws = new WsActionTester(new SkipOnboardingTutorialAction()); + public void response_has_no_content() { + UserDto user = db.users().insertUser(u -> u.setOnboarded(false)); + userSession.logIn(user); + + TestResponse response = call(); + + assertThat(response.getStatus()).isEqualTo(204); + assertThat(response.getInput()).isEmpty(); + } + + @Test + public void test_definition() { + WsActionTester ws = new WsActionTester(new SkipOnboardingTutorialAction(userSession, db.getDbClient(), system2)); WebService.Action def = ws.getDef(); assertThat(def.isPost()).isTrue(); assertThat(def.isInternal()).isTrue(); assertThat(def.since()).isEqualTo("6.5"); assertThat(def.params()).isEmpty(); + assertThat(def.changelog()).isEmpty(); } - @Test - public void should_return_silently() { - WsActionTester ws = new WsActionTester(new SkipOnboardingTutorialAction()); - TestResponse response = ws.newRequest().setMethod("POST").execute(); - assertThat(response.getStatus()).isEqualTo(204); - assertThat(response.getInput()).isEmpty(); + private TestResponse call() { + return ws.newRequest().setMethod("POST").execute(); + } + + private UserDto selectUser(String userLogin) { + UserDto userDto = db.getDbClient().userDao().selectByLogin(db.getSession(), userLogin); + assertThat(userDto).isNotNull(); + return userDto; } + } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/user/UsersWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/user/UsersWsParameters.java index c79994c1a40..6ffecd751b0 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/user/UsersWsParameters.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/user/UsersWsParameters.java @@ -27,7 +27,7 @@ public class UsersWsParameters { public static final String ACTION_CREATE = "create"; public static final String ACTION_UPDATE = "update"; public static final String ACTION_GROUPS = "groups"; - public static final String ACTION_SKIP_ONBOARDING_TUTORIAL = "skipOnboardingTutorial"; + public static final String ACTION_SKIP_ONBOARDING_TUTORIAL = "skip_onboarding_tutorial"; public static final String PARAM_ORGANIZATION = "organization"; public static final String PARAM_LOGIN = "login"; -- 2.39.5