]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-1330 Add user permission to edit single quality profile
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 21 Sep 2017 12:00:30 +0000 (14:00 +0200)
committerStas Vilchik <stas.vilchik@sonarsource.com>
Mon, 2 Oct 2017 15:18:15 +0000 (17:18 +0200)
18 files changed:
server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDto.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java [new file with mode: 0644]
server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml [new file with mode: 0644]
server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java [new file with mode: 0644]
server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/AddUserAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/QProfileWsSupport.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddUserActionTest.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/AddUserRequest.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesService.java
sonar-ws/src/test/java/org/sonarqube/ws/client/qualityprofile/QualityProfilesServiceTest.java

index fefec4144f957770991bd7158ca2c2ef1c072c31..f91d4c72718d61b1e32711957615bc56a9d3c107 100644 (file)
@@ -110,7 +110,7 @@ public class ComputeEngineContainerImplTest {
       picoContainer.getComponentAdapters().stream()
         .map(ComponentAdapter::getComponentImplementation)
         .collect(Collectors.toList())).contains((Class) StartableHazelcastMember.class,
-          (Class) CeDistributedInformationImpl.class);
+      (Class) CeDistributedInformationImpl.class);
     underTest.stop();
   }
 
@@ -139,7 +139,7 @@ public class ComputeEngineContainerImplTest {
           + 4 // content of ReportAnalysisFailureNotificationModule
           + 3 // CeCleaningModule + its content
           + 1 // CeDistributedInformation
-    );
+      );
     assertThat(picoContainer.getParent().getComponentAdapters()).hasSize(
       CONTAINER_ITSELF
         + 5 // level 3
@@ -152,7 +152,7 @@ public class ComputeEngineContainerImplTest {
     assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize(
       COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION
         + 26 // level 1
-        + 49 // content of DaoModule
+        + 50 // content of DaoModule
         + 3 // content of EsSearchModule
         + 65 // content of CorePropertyDefinitions
         + 1 // StopFlagContainer
@@ -161,8 +161,8 @@ public class ComputeEngineContainerImplTest {
       picoContainer.getComponentAdapters().stream()
         .map(ComponentAdapter::getComponentImplementation)
         .collect(Collectors.toList())).doesNotContain((Class) StartableHazelcastMember.class,
-          (Class) CeDistributedInformationImpl.class).contains(
-            (Class) StandaloneCeDistributedInformation.class);
+      (Class) CeDistributedInformationImpl.class).contains(
+      (Class) StandaloneCeDistributedInformation.class);
     assertThat(picoContainer.getParent().getParent().getParent().getParent()).isNull();
     underTest.stop();
 
index 35c2a91d202702166dca1c1ebbd6b1296ace3603..a621cdc7706a602fa1e3e9f97691e169e37ad0c5 100644 (file)
@@ -86,6 +86,7 @@ public final class SqTables {
     "project_qprofiles",
     "properties",
     "qprofile_changes",
+    "qprofile_edit_users",
     "quality_gates",
     "quality_gate_conditions",
     "rules",
index d791dbbff31f32a3c8a1485c2683821e661ba584..d6fa06ecacd44355993889f473de26bcf523d94b 100644 (file)
@@ -60,6 +60,7 @@ import org.sonar.db.qualitygate.QualityGateDao;
 import org.sonar.db.qualityprofile.ActiveRuleDao;
 import org.sonar.db.qualityprofile.DefaultQProfileDao;
 import org.sonar.db.qualityprofile.QProfileChangeDao;
+import org.sonar.db.qualityprofile.QProfileEditUsersDao;
 import org.sonar.db.qualityprofile.QualityProfileDao;
 import org.sonar.db.rule.RuleDao;
 import org.sonar.db.rule.RuleRepositoryDao;
@@ -114,6 +115,7 @@ public class DaoModule extends Module {
     PropertiesDao.class,
     PurgeDao.class,
     QProfileChangeDao.class,
+    QProfileEditUsersDao.class,
     QualityGateConditionDao.class,
     QualityGateDao.class,
     QualityProfileDao.class,
index f694b925d14154db3c482718e5c8ad83b3120ad5..bb269e0d2c4f7c9a0c8c43f42668b2215dc23f26 100644 (file)
@@ -58,6 +58,7 @@ import org.sonar.db.qualitygate.QualityGateDao;
 import org.sonar.db.qualityprofile.ActiveRuleDao;
 import org.sonar.db.qualityprofile.DefaultQProfileDao;
 import org.sonar.db.qualityprofile.QProfileChangeDao;
+import org.sonar.db.qualityprofile.QProfileEditUsersDao;
 import org.sonar.db.qualityprofile.QualityProfileDao;
 import org.sonar.db.rule.RuleDao;
 import org.sonar.db.rule.RuleRepositoryDao;
@@ -126,6 +127,7 @@ public class DbClient {
   private final EsQueueDao esQueueDao;
   private final PluginDao pluginDao;
   private final BranchDao branchDao;
+  private final QProfileEditUsersDao qProfileEditUsersDao;
 
   public DbClient(Database database, MyBatis myBatis, DBSessions dbSessions, Dao... daos) {
     this.database = database;
@@ -185,6 +187,7 @@ public class DbClient {
     esQueueDao = getDao(map, EsQueueDao.class);
     pluginDao = getDao(map, PluginDao.class);
     branchDao = getDao(map, BranchDao.class);
+    qProfileEditUsersDao = getDao(map, QProfileEditUsersDao.class);
   }
 
   public DbSession openSession(boolean batch) {
@@ -391,6 +394,10 @@ public class DbClient {
     return branchDao;
   }
 
+  public QProfileEditUsersDao qProfileEditUsersDao() {
+    return qProfileEditUsersDao;
+  }
+
   protected <K extends Dao> K getDao(Map<Class, Dao> map, Class<K> clazz) {
     return (K) map.get(clazz);
   }
index b8ee31f72e716d4dc0492eed9222a90c10403bd9..244cb18af54b6e3e093f126ef6df7b2a0552be79 100644 (file)
@@ -103,6 +103,7 @@ import org.sonar.db.qualityprofile.ActiveRuleMapper;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
 import org.sonar.db.qualityprofile.DefaultQProfileMapper;
 import org.sonar.db.qualityprofile.QProfileChangeMapper;
+import org.sonar.db.qualityprofile.QProfileEditUsersMapper;
 import org.sonar.db.qualityprofile.QualityProfileMapper;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleMapper;
@@ -228,6 +229,7 @@ public class MyBatis implements Startable {
       PropertiesMapper.class,
       PurgeMapper.class,
       QProfileChangeMapper.class,
+      QProfileEditUsersMapper.class,
       QualityGateConditionMapper.class,
       QualityGateMapper.class,
       QualityProfileMapper.class,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDao.java
new file mode 100644 (file)
index 0000000..f5443fb
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.db.qualityprofile;
+
+import org.sonar.api.utils.System2;
+import org.sonar.db.Dao;
+import org.sonar.db.DbSession;
+import org.sonar.db.user.UserDto;
+
+public class QProfileEditUsersDao implements Dao {
+
+  private final System2 system2;
+
+  public QProfileEditUsersDao(System2 system2) {
+    this.system2 = system2;
+  }
+
+  public boolean exists(DbSession dbSession, QProfileDto profile, UserDto user) {
+    return mapper(dbSession).selectByQProfileAndUser(profile.getKee(), user.getId()) != null;
+  }
+
+  public void insert(DbSession dbSession, QProfileEditUsersDto dto) {
+    mapper(dbSession).insert(dto, system2.now());
+  }
+
+  private static QProfileEditUsersMapper mapper(DbSession dbSession) {
+    return dbSession.getMapper(QProfileEditUsersMapper.class);
+  }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersDto.java
new file mode 100644 (file)
index 0000000..4dd5f30
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.db.qualityprofile;
+
+public class QProfileEditUsersDto {
+
+  private String uuid;
+  private int userId;
+  private String qProfileUuid;
+
+  public String getUuid() {
+    return uuid;
+  }
+
+  public QProfileEditUsersDto setUuid(String uuid) {
+    this.uuid = uuid;
+    return this;
+  }
+
+  public int getUserId() {
+    return userId;
+  }
+
+  public QProfileEditUsersDto setUserId(int userId) {
+    this.userId = userId;
+    return this;
+  }
+
+  public String getQProfileUuid() {
+    return qProfileUuid;
+  }
+
+  public QProfileEditUsersDto setQProfileUuid(String qProfileUuid) {
+    this.qProfileUuid = qProfileUuid;
+    return this;
+  }
+
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/QProfileEditUsersMapper.java
new file mode 100644 (file)
index 0000000..8efa5fd
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.db.qualityprofile;
+
+import org.apache.ibatis.annotations.Param;
+
+public interface QProfileEditUsersMapper {
+
+  QProfileEditUsersDto selectByQProfileAndUser(@Param("qProfileUuid") String qProfileUuid, @Param("userId") int userId);
+
+  void insert(@Param("dto") QProfileEditUsersDto dto, @Param("now") long now);
+}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QProfileEditUsersMapper.xml
new file mode 100644 (file)
index 0000000..1e39129
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.db.qualityprofile.QProfileEditUsersMapper">
+
+  <sql id="sqlColumns">
+    qeu.uuid as "uuid",
+    qeu.user_id as "userId",
+    qeu.qprofile_uuid as "qProfileUuid"
+  </sql>
+
+  <select id="selectByQProfileAndUser" parameterType="map" resultType="org.sonar.db.qualityprofile.QProfileEditUsersDto">
+    select
+    <include refid="sqlColumns"/>
+    from qprofile_edit_users qeu
+    where
+    qeu.user_id = #{userId, jdbcType=INTEGER}
+    and qeu.qprofile_uuid = #{qProfileUuid, jdbcType=VARCHAR}
+  </select>
+
+  <insert id="insert" useGeneratedKeys="false" parameterType="map">
+    insert into qprofile_edit_users(
+    uuid,
+    user_id,
+    qprofile_uuid,
+    created_at,
+    ) values (
+    #{dto.uuid, jdbcType=VARCHAR},
+    #{dto.userId, jdbcType=INTEGER},
+    #{dto.qProfileUuid, jdbcType=VARCHAR},
+    #{now, jdbcType=BIGINT}
+    )
+  </insert>
+
+</mapper>
+
index 9d7a3d2786afac70a41107d263e93f289119c652..986fbd7e2c3b11a0868d18b63de710cfc02f98e0 100644 (file)
@@ -29,6 +29,6 @@ public class DaoModuleTest {
   public void verify_count_of_added_components() {
     ComponentContainer container = new ComponentContainer();
     new DaoModule().configure(container);
-    assertThat(container.size()).isEqualTo(2 + 49);
+    assertThat(container.size()).isEqualTo(2 + 50);
   }
 }
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QProfileEditUsersDaoTest.java
new file mode 100644 (file)
index 0000000..a344258
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.db.qualityprofile;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.db.DbTester;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.user.UserDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+
+public class QProfileEditUsersDaoTest {
+
+  private static final long NOW = 10_000_000_000L;
+
+  private System2 system2 = new TestSystem2().setNow(NOW);
+
+  @Rule
+  public DbTester db = DbTester.create(system2);
+
+  private QProfileEditUsersDao underTest = db.getDbClient().qProfileEditUsersDao();
+
+  @Test
+  public void exists() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization);
+    QProfileDto anotherProfile = db.qualityProfiles().insert(organization);
+    UserDto user = db.users().insertUser();
+    UserDto anotherUser = db.users().insertUser();
+    db.qualityProfiles().addUserPermission(profile, user);
+
+    assertThat(underTest.exists(db.getSession(), profile, user)).isTrue();
+    assertThat(underTest.exists(db.getSession(), profile, anotherUser)).isFalse();
+    assertThat(underTest.exists(db.getSession(), anotherProfile, user)).isFalse();
+    assertThat(underTest.exists(db.getSession(), anotherProfile, anotherUser)).isFalse();
+  }
+
+  @Test
+  public void insert() {
+    underTest.insert(db.getSession(), new QProfileEditUsersDto()
+      .setUuid("ABCD")
+      .setUserId(100)
+      .setQProfileUuid("QPROFILE")
+    );
+
+    assertThat(db.selectFirst(db.getSession(), "select uuid as \"uuid\", user_id as \"userId\", qprofile_uuid as \"qProfileUuid\", created_at as \"createdAt\" from qprofile_edit_users")).contains(
+      entry("uuid", "ABCD"),
+      entry("userId", 100L),
+      entry("qProfileUuid", "QPROFILE"),
+      entry("createdAt", NOW));
+  }
+
+}
index 02c53fad43d33866d43e1d9b7abac21f391b74aa..7dc2a64ea4b7adaded2669f160993e1d3d37b1e2 100644 (file)
@@ -23,12 +23,14 @@ import java.util.Arrays;
 import java.util.Optional;
 import java.util.function.Consumer;
 import org.sonar.api.rule.Severity;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.user.UserDto;
 
 import static org.apache.commons.lang.math.RandomUtils.nextInt;
 import static org.apache.commons.lang.math.RandomUtils.nextLong;
@@ -108,4 +110,14 @@ public class QualityProfileDbTester {
     dbSession.commit();
     return this;
   }
+
+  public void addUserPermission(QProfileDto profile, UserDto user){
+    dbClient.qProfileEditUsersDao().insert(dbSession, new QProfileEditUsersDto()
+      .setUuid(UuidFactoryFast.getInstance().create())
+      .setUserId(user.getId())
+      .setQProfileUuid(profile.getKee())
+    );
+    dbSession.commit();
+
+  }
 }
index ca0ed070ecdc4023a6cce54c500e81afc7b3438b..eb29e55d7834c14cad73c48a5a6889b4d8c8416f 100644 (file)
@@ -25,19 +25,33 @@ import org.sonar.api.resources.Languages;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
+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.db.qualityprofile.QProfileDto;
+import org.sonar.db.qualityprofile.QProfileEditUsersDto;
+import org.sonar.db.user.UserDto;
 
 import static org.sonar.core.util.stream.MoreCollectors.toSet;
 import static org.sonar.server.qualityprofile.ws.QProfileWsSupport.createOrganizationParam;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ADD_USER;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LOGIN;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE;
 
 public class AddUserAction implements QProfileWsAction {
 
+  private final DbClient dbClient;
+  private final UuidFactory uuidFactory;
+  private final QProfileWsSupport wsSupport;
   private final Languages languages;
 
-  public AddUserAction(Languages languages) {
+  public AddUserAction(DbClient dbClient, UuidFactory uuidFactory, QProfileWsSupport wsSupport, Languages languages) {
+    this.dbClient = dbClient;
+    this.uuidFactory = uuidFactory;
+    this.wsSupport = wsSupport;
     this.languages = languages;
   }
 
@@ -73,6 +87,26 @@ public class AddUserAction implements QProfileWsAction {
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-    // TODO
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      OrganizationDto organization = wsSupport.getOrganizationByKey(dbSession, request.param(PARAM_ORGANIZATION));
+      QProfileDto profile = wsSupport.getProfile(dbSession, organization, request.mandatoryParam(PARAM_QUALITY_PROFILE), request.mandatoryParam(PARAM_LANGUAGE));
+      wsSupport.checkCanEdit(dbSession, profile);
+      UserDto user = wsSupport.getUser(dbSession, organization, request.mandatoryParam(PARAM_LOGIN));
+      addUser(dbSession, profile, user);
+    }
+    response.noContent();
   }
+
+  private void addUser(DbSession dbSession, QProfileDto profile, UserDto user) {
+    if (dbClient.qProfileEditUsersDao().exists(dbSession, profile, user)) {
+      return;
+    }
+    dbClient.qProfileEditUsersDao().insert(dbSession,
+      new QProfileEditUsersDto()
+        .setUuid(uuidFactory.create())
+        .setUserId(user.getId())
+        .setQProfileUuid(profile.getKee()));
+    dbSession.commit();
+  }
+
 }
index cdbdd95ba6cd10eb777ee5e0b2619de53d5c67dd..1649c4ccf9662077cd2a32bd905de795b75e3b28 100644 (file)
@@ -29,11 +29,15 @@ import org.sonar.db.DbSession;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.permission.OrganizationPermission;
 import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.user.UserDto;
 import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.user.UserSession;
 import org.sonar.server.ws.WsUtils;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
 import static org.sonar.server.ws.WsUtils.checkFound;
 import static org.sonar.server.ws.WsUtils.checkRequest;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ORGANIZATION;
@@ -94,12 +98,46 @@ public class QProfileWsSupport {
     return profile;
   }
 
-  public void checkPermission(DbSession dbSession, QProfileDto rulesProfile) {
-    OrganizationDto organization = getOrganization(dbSession, rulesProfile);
+  public QProfileDto getProfile(DbSession dbSession, OrganizationDto organization, String name, String language) {
+    QProfileDto profile = dbClient.qualityProfileDao().selectByNameAndLanguage(dbSession, organization, name, language);
+    checkFound(profile, "Quality Profile for language '%s' and name '%s' does not exist in organization '%s'", language, name, organization.getKey());
+    return profile;
+  }
+
+  public UserDto getUser(DbSession dbSession, OrganizationDto organization, String login) {
+    UserDto user = dbClient.userDao().selectActiveUserByLogin(dbSession, login);
+    checkFound(user, "User with login '%s' is not found'", login);
+    checkMembership(dbSession, organization, user);
+    return user;
+  }
+
+  public void checkPermission(DbSession dbSession, QProfileDto profile) {
+    OrganizationDto organization = getOrganization(dbSession, profile);
     userSession.checkPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
   }
 
+  public void checkCanEdit(DbSession dbSession, QProfileDto profile) {
+    checkNotBuiltInt(profile);
+    OrganizationDto organization = getOrganization(dbSession, profile);
+    userSession.checkLoggedIn();
+    if (userSession.hasPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization)) {
+      return;
+    }
+    UserDto user = dbClient.userDao().selectByLogin(dbSession, userSession.getLogin());
+    checkState(user != null, "User from session does not exist");
+    if (dbClient.qProfileEditUsersDao().exists(dbSession, profile, user)) {
+      return;
+    }
+    throw insufficientPrivilegesException();
+  }
+
   public void checkNotBuiltInt(QProfileDto profile) {
     checkRequest(!profile.isBuiltIn(), "Operation forbidden for built-in Quality Profile '%s' with language '%s'", profile.getName(), profile.getLanguage());
   }
+
+  public void checkMembership(DbSession dbSession, OrganizationDto organization, UserDto user) {
+    checkArgument(dbClient.organizationMemberDao().select(dbSession, organization.getUuid(), user.getId()).isPresent(),
+      "User '%s' is not member of organization '%s'", user.getLogin(), organization.getKey());
+  }
+
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddUserActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/AddUserActionTest.java
new file mode 100644 (file)
index 0000000..282310e
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.qualityprofile.ws;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.resources.Languages;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.DbTester;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.permission.OrganizationPermission;
+import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.language.LanguageTesting;
+import org.sonar.server.organization.TestDefaultOrganizationProvider;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+
+import static java.lang.String.format;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LOGIN;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE;
+
+public class AddUserActionTest {
+
+  private static final String XOO = "xoo";
+  private static final Languages LANGUAGES = LanguageTesting.newLanguages(XOO);
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+  @Rule
+  public DbTester db = DbTester.create();
+
+  private QProfileWsSupport wsSupport = new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db));
+  private UuidFactory uuidFactory = UuidFactoryFast.getInstance();
+
+  private WsActionTester ws = new WsActionTester(new AddUserAction(db.getDbClient(), uuidFactory, wsSupport, LANGUAGES));
+
+  @Test
+  public void test_definition() {
+    WebService.Action def = ws.getDef();
+    assertThat(def.key()).isEqualTo("add_user");
+    assertThat(def.isPost()).isTrue();
+    assertThat(def.isInternal()).isTrue();
+    assertThat(def.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("organization", "qualityProfile", "language", "login");
+  }
+
+  @Test
+  public void add_user() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO));
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(organization, user);
+    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
+
+    TestResponse response = ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute();
+
+    assertThat(response.getStatus()).isEqualTo(204);
+    assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue();
+  }
+
+  @Test
+  public void does_nothing_when_user_can_already_edit_profile() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO));
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(organization, user);
+    db.qualityProfiles().addUserPermission(profile, user);
+    assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue();
+    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
+
+    ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute();
+
+    assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue();
+  }
+
+  @Test
+  public void qp_administers_can_add_user() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO));
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(organization, user);
+    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
+
+    ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute();
+
+    assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue();
+  }
+
+  @Test
+  public void qp_editors_can_add_user() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO));
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(organization, user);
+    UserDto userAllowedToEditProfile = db.users().insertUser();
+    db.qualityProfiles().addUserPermission(profile, userAllowedToEditProfile);
+    userSession.logIn(userAllowedToEditProfile);
+
+    ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute();
+
+    assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue();
+  }
+
+  @Test
+  public void uses_default_organization_when_no_organization() {
+    OrganizationDto organization = db.getDefaultOrganization();
+    QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO));
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(organization, user);
+    userSession.logIn().addPermission(ADMINISTER_QUALITY_PROFILES, organization);
+
+    ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, user.getLogin() )
+      .execute();
+
+    assertThat(db.getDbClient().qProfileEditUsersDao().exists(db.getSession(), profile, user)).isTrue();
+  }
+
+  @Test
+  public void fail_when_user_does_not_exist() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO));
+    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
+
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("User with login 'unknown' is not found");
+
+    ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, "unknown")
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute();
+  }
+
+  @Test
+  public void fail_when_qprofile_does_not_exist() {
+    OrganizationDto organization = db.organizations().insert();
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(organization, user);
+    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
+
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage(format("Quality Profile for language 'xoo' and name 'unknown' does not exist in organization '%s'", organization.getKey()));
+
+    ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, "unknown")
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute();
+  }
+
+  @Test
+  public void fail_when_qprofile_does_not_belong_to_organization() {
+    OrganizationDto organization = db.organizations().insert();
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(organization, user);
+    OrganizationDto anotherOrganization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(anotherOrganization, p -> p.setLanguage(XOO));
+    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
+
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage(format("Quality Profile for language 'xoo' and name '%s' does not exist in organization '%s'", profile.getName(), organization.getKey()));
+
+    ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute();
+  }
+
+  @Test
+  public void fail_when_wrong_language() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage("unknown"));
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(organization, user);
+    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
+
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage(format("Quality Profile for language 'xoo' and name '%s' does not exist in organization '%s'", profile.getName(), organization.getKey()));
+
+    ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute();
+  }
+
+  @Test
+  public void fail_when_user_is_not_member_of_organization() {
+    OrganizationDto organization = db.organizations().insert();
+    OrganizationDto anotherOrganization = db.organizations().insert();
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(anotherOrganization, user);
+    QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO));
+    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage(format("User '%s' is not member of organization '%s'", user.getLogin(), organization.getKey()));
+
+    ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute();
+  }
+
+  @Test
+  public void fail_when_qp_is_built_in() {
+    OrganizationDto organization = db.organizations().insert();
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(organization, user);
+    QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO).setIsBuiltIn(true));
+    userSession.logIn().addPermission(OrganizationPermission.ADMINISTER_QUALITY_PROFILES, organization);
+
+    expectedException.expect(BadRequestException.class);
+    expectedException.expectMessage(String.format("Operation forbidden for built-in Quality Profile '%s' with language 'xoo'", profile.getName()));
+
+    ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute();
+  }
+
+  @Test
+  public void fail_when_not_enough_permission() {
+    OrganizationDto organization = db.organizations().insert();
+    QProfileDto profile = db.qualityProfiles().insert(organization, p -> p.setLanguage(XOO));
+    UserDto user = db.users().insertUser();
+    db.organizations().addMember(organization, user);
+    userSession.logIn(db.users().insertUser()).addPermission(OrganizationPermission.ADMINISTER_QUALITY_GATES, organization);
+
+    expectedException.expect(ForbiddenException.class);
+
+    ws.newRequest()
+      .setParam(PARAM_QUALITY_PROFILE, profile.getName())
+      .setParam(PARAM_LANGUAGE, XOO)
+      .setParam(PARAM_LOGIN, user.getLogin())
+      .setParam(PARAM_ORGANIZATION, organization.getKey())
+      .execute();
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/AddUserRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualityprofile/AddUserRequest.java
new file mode 100644 (file)
index 0000000..db89c3d
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.sonarqube.ws.client.qualityprofile;
+
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
+public class AddUserRequest {
+
+  private final String organization;
+  private final String language;
+  private final String qualityProfile;
+  private final String userLogin;
+
+  private AddUserRequest(Builder builder) {
+    this.language = builder.language;
+    this.organization = builder.organization;
+    this.qualityProfile = builder.qualityProfile;
+    this.userLogin = builder.userLogin;
+  }
+
+  public String getLanguage() {
+    return language;
+  }
+
+  public String getOrganization() {
+    return organization;
+  }
+
+  public String getQualityProfile() {
+    return qualityProfile;
+  }
+
+  public String getUserLogin() {
+    return userLogin;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+    private String organization;
+    private String qualityProfile;
+    private String language;
+    private String userLogin;
+
+    private Builder() {
+      // enforce factory method use
+    }
+
+    public Builder setLanguage(String language) {
+      this.language = language;
+      return this;
+    }
+
+    public Builder setOrganization(String organization) {
+      this.organization = organization;
+      return this;
+    }
+
+    public Builder setQualityProfile(String qualityProfile) {
+      this.qualityProfile = qualityProfile;
+      return this;
+    }
+
+    public Builder setUserLogin(String userLogin) {
+      this.userLogin = userLogin;
+      return this;
+    }
+
+    public AddUserRequest build() {
+      return new AddUserRequest(this);
+    }
+  }
+}
index ea9ad29636a7f3ec6dc9f01164c2bd4045ea688e..38f5c2ca100a4d7a319732e6edbd9924255cc05d 100644 (file)
@@ -32,6 +32,7 @@ import org.sonarqube.ws.client.WsConnector;
 
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ACTIVATE_RULE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ADD_PROJECT;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ADD_USER;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_CHANGE_PARENT;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_COPY;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_CREATE;
@@ -48,6 +49,7 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_FROM_KEY;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_NAME;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LOGIN;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PARAMS;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PARENT_QUALITY_PROFILE;
@@ -56,6 +58,7 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_KEY;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_UUID;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_RESET;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_RULE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_SEVERITY;
@@ -170,4 +173,12 @@ public class QualityProfilesService extends BaseService {
 
     call(postRequest);
   }
+
+  public void addUser(AddUserRequest request) {
+    call(new PostRequest(path(ACTION_ADD_USER))
+      .setParam(PARAM_ORGANIZATION, request.getOrganization())
+      .setParam(PARAM_QUALITY_PROFILE, request.getQualityProfile())
+      .setParam(PARAM_LANGUAGE, request.getLanguage())
+      .setParam(PARAM_LOGIN, request.getUserLogin()));
+  }
 }
index a3add919ea7432282a08fce40bbfa594beb69258..591d1690abc8edf515839a759a8af21fc01b60fd 100644 (file)
@@ -36,11 +36,13 @@ import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_FROM_KEY;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_NAME;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LOGIN;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PARAMS;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_KEY;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_KEY;
+import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_QUALITY_PROFILE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_RULE;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_SEVERITY;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_TO_NAME;
@@ -195,4 +197,23 @@ public class QualityProfilesServiceTest {
       .hasParam(PARAM_SEVERITY, Severity.INFO.toString())
       .andNoOtherParam();
   }
+
+  @Test
+  public void add_user() {
+    underTest.addUser(AddUserRequest.builder()
+      .setOrganization("O1")
+      .setQualityProfile("P1")
+      .setLanguage("Xoo")
+      .setUserLogin("john")
+      .build());
+    PostRequest request = serviceTester.getPostRequest();
+
+    serviceTester.assertThat(request)
+      .hasPath("add_user")
+      .hasParam(PARAM_ORGANIZATION, "O1")
+      .hasParam(PARAM_QUALITY_PROFILE, "P1")
+      .hasParam(PARAM_LANGUAGE, "Xoo")
+      .hasParam(PARAM_LOGIN, "john")
+      .andNoOtherParam();
+  }
 }