]> source.dussan.org Git - sonarqube.git/commitdiff
SONARCLOUD-78 add metrics
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Wed, 20 Jun 2018 20:06:18 +0000 (22:06 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 29 Jun 2018 07:10:17 +0000 (09:10 +0200)
13 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/KeyLongValue.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentQuery.java
server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationMapper.java
server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationQuery.java
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
server/sonar-db-dao/src/main/resources/org/sonar/db/organization/OrganizationMapper.xml
server/sonar-db-dao/src/main/resources/org/sonar/db/user/UserMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java

index 88c7f505d1222e7a8effd25180068de188da511c..e1097b0f56c59b09e94da60b76d377c224e00baf 100644 (file)
@@ -29,6 +29,15 @@ public class KeyLongValue {
   private String key;
   private Long value;
 
+  public KeyLongValue() {
+    // for MyBatis
+  }
+
+  public KeyLongValue(String key, Long value) {
+    this.key = key;
+    this.value = value;
+  }
+
   public String getKey() {
     return key;
   }
index 487b7b2a252de62defebb80450f369178bd45cdd..01fc40ca2cf42fe5c66ac7bfb79d48b7fd72737e 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.db.component;
 
+import java.util.Date;
 import java.util.Locale;
 import java.util.Set;
 import java.util.stream.Stream;
@@ -39,6 +40,8 @@ public class ComponentQuery {
   private final Set<String> componentUuids;
   private final Set<String> componentKeys;
   private final Long analyzedBefore;
+  private final Long analyzedAfter;
+  private final Date createdAfter;
   private final boolean onProvisionedOnly;
 
   private ComponentQuery(Builder builder) {
@@ -51,6 +54,8 @@ public class ComponentQuery {
     this.componentKeys = builder.componentKeys;
     this.isPrivate = builder.isPrivate;
     this.analyzedBefore = builder.analyzedBefore;
+    this.analyzedAfter = builder.analyzedAfter;
+    this.createdAfter = builder.createdAfter;
     this.onProvisionedOnly = builder.onProvisionedOnly;
   }
 
@@ -108,6 +113,16 @@ public class ComponentQuery {
     return analyzedBefore;
   }
 
+  @CheckForNull
+  public Long getAnalyzedAfter() {
+    return analyzedAfter;
+  }
+
+  @CheckForNull
+  public Date getCreatedAfter() {
+    return createdAfter;
+  }
+
   public boolean isOnProvisionedOnly() {
     return onProvisionedOnly;
   }
@@ -131,6 +146,8 @@ public class ComponentQuery {
     private Set<String> componentUuids;
     private Set<String> componentKeys;
     private Long analyzedBefore;
+    private Long analyzedAfter;
+    private Date createdAfter;
     private boolean onProvisionedOnly = false;
 
     public Builder setNameOrKeyQuery(@Nullable String nameOrKeyQuery) {
@@ -181,6 +198,21 @@ public class ComponentQuery {
       return this;
     }
 
+    /**
+     * Filter on date of last analysis. On projects, all branches and pull requests are taken into
+     * account. For example the analysis of a short-lived branch is included in the filter
+     * even if the main branch has never been analyzed.
+     */
+    public Builder setAnalyzedAfter(@Nullable Long l) {
+      this.analyzedAfter = l;
+      return this;
+    }
+
+    public Builder setCreatedAfter(@Nullable Date l) {
+      this.createdAfter = l;
+      return this;
+    }
+
     public Builder setOnProvisionedOnly(boolean onProvisionedOnly) {
       this.onProvisionedOnly = onProvisionedOnly;
       return this;
index f55b9952c6fc5116b2469c7c72dfb51a6038c26f..58fc0cd8c59046e7cddae4d277e3e319b355aead 100644 (file)
@@ -25,6 +25,7 @@ import java.util.Set;
 import org.sonar.api.utils.System2;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
+import org.sonar.db.KeyLongValue;
 import org.sonar.db.Pagination;
 import org.sonar.db.qualitygate.QGateWithOrgDto;
 import org.sonar.db.user.GroupDto;
@@ -53,6 +54,14 @@ public class OrganizationDao implements Dao {
     return getMapper(dbSession).countByQuery(organizationQuery);
   }
 
+  public List<KeyLongValue> countTeamsByMembers(DbSession dbSession) {
+    return getMapper(dbSession).countTeamsByMembers();
+  }
+
+  public List<KeyLongValue> countTeamsByProjects(DbSession dbSession) {
+    return getMapper(dbSession).countTeamsByProjects();
+  }
+
   public List<OrganizationDto> selectByQuery(DbSession dbSession, OrganizationQuery organizationQuery, Pagination pagination) {
     requireNonNull(organizationQuery, "organizationQuery can't be null");
     return getMapper(dbSession).selectByQuery(organizationQuery, pagination);
index 24763e9a21535b09bcae6be00fd7e063aecfdfe3..e3ce1865389260c0da33af73a6d5331a10a80151 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.db.organization;
 import java.util.List;
 import javax.annotation.CheckForNull;
 import org.apache.ibatis.annotations.Param;
+import org.sonar.db.KeyLongValue;
 import org.sonar.db.Pagination;
 
 public interface OrganizationMapper {
@@ -29,6 +30,10 @@ public interface OrganizationMapper {
 
   int countByQuery(@Param("query") OrganizationQuery organizationQuery);
 
+  List<KeyLongValue> countTeamsByMembers();
+
+  List<KeyLongValue> countTeamsByProjects();
+
   List<OrganizationDto> selectByQuery(@Param("query") OrganizationQuery organizationQuery,
     @Param("pagination") Pagination pagination);
 
index fbd479f86b1e28e951becb0dfbfa8acccff395e6..2117660e8e6f2de4f25312c59e5ba93cb47cdec8 100644 (file)
@@ -28,13 +28,22 @@ import javax.annotation.Nullable;
 import static org.sonar.core.util.stream.MoreCollectors.toSet;
 
 public class OrganizationQuery {
-  private static final OrganizationQuery NO_QUERY = newOrganizationQueryBuilder().build();
+  private static final OrganizationQuery NO_FILTER = newOrganizationQueryBuilder().build();
   private final Set<String> keys;
   private final Integer userId;
+  private final boolean onlyTeam;
+  private final boolean onlyPersonal;
+  private final boolean withAnalyses;
 
   private OrganizationQuery(Builder builder) {
     this.keys = builder.keys;
     this.userId = builder.member;
+    this.onlyPersonal = builder.onlyPersonal;
+    this.onlyTeam = builder.onlyTeam;
+    if (this.onlyPersonal && this.onlyTeam) {
+      throw new IllegalArgumentException("Only one of onlyPersonal and onlyTeam can be true");
+    }
+    this.withAnalyses = builder.withAnalyses;
   }
 
   @CheckForNull
@@ -47,8 +56,20 @@ public class OrganizationQuery {
     return userId;
   }
 
+  public boolean isOnlyTeam() {
+    return onlyTeam;
+  }
+
+  public boolean isOnlyPersonal() {
+    return onlyPersonal;
+  }
+
+  public boolean isWithAnalyses() {
+    return withAnalyses;
+  }
+
   public static OrganizationQuery returnAll() {
-    return NO_QUERY;
+    return NO_FILTER;
   }
 
   public static Builder newOrganizationQueryBuilder() {
@@ -57,7 +78,11 @@ public class OrganizationQuery {
 
   public static class Builder {
     private Set<String> keys;
+    @Nullable
     private Integer member;
+    private boolean onlyTeam = false;
+    private boolean onlyPersonal = false;
+    private boolean withAnalyses = false;
 
     private Builder() {
       // use static factory method
@@ -77,6 +102,21 @@ public class OrganizationQuery {
       return this;
     }
 
+    public Builder setOnlyTeam() {
+      this.onlyTeam = true;
+      return this;
+    }
+
+    public Builder setOnlyPersonal() {
+      this.onlyPersonal = true;
+      return this;
+    }
+
+    public Builder setWithAnalyses() {
+      this.withAnalyses = true;
+      return this;
+    }
+
     public OrganizationQuery build() {
       return new OrganizationQuery(this);
     }
index f78aebc4f71ef8cfb3bd39b48cda1f47d348da6e..93fe5bab0213852229af73b18a65c7fc3b03daba 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.api.utils.System2;
 import org.sonar.core.util.UuidFactory;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
+import org.sonar.db.KeyLongValue;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.organization.OrganizationDto;
 
@@ -116,6 +117,37 @@ public class UserDao implements Dao {
     return mapper(dbSession).countRootUsersButLogin(login);
   }
 
+  /**
+   * Includes deactivated users
+   */
+  public long countTotalUsers(DbSession dbSession) {
+    return mapper(dbSession).countTotalUsers();
+  }
+
+  public long countTeamUsers(DbSession dbSession) {
+    return mapper(dbSession).countTeamUsers();
+  }
+
+  public long countPersonalUsers(DbSession dbSession) {
+    return mapper(dbSession).countPersonalUsers();
+  }
+
+  public long countPersonalUsersWithZeroProjects(DbSession dbSession) {
+    return mapper(dbSession).countPersonalUsersWithZeroProjects();
+  }
+
+  public long countNewUsersSince(DbSession dbSession, long since) {
+    return mapper(dbSession).countNewUsersSince(since);
+  }
+
+  public long countActiveUsers(DbSession dbSession) {
+    return mapper(dbSession).countActiveUsers();
+  }
+
+  public List<KeyLongValue> countUsersByIdentityProviders(DbSession dbSession) {
+    return mapper(dbSession).countUsersByIdentityProviders();
+  }
+
   public UserDto insert(DbSession session, UserDto dto) {
     long now = system2.now();
     mapper(session).insert(dto.setUuid(uuidFactory.create()).setCreatedAt(now).setUpdatedAt(now));
index 2ab7fe93dabb8f16bc475d3a9c4d022722a26dd3..671f8755bd7ecc6508b1b0110d8f025d34099dad 100644 (file)
@@ -24,6 +24,7 @@ import javax.annotation.CheckForNull;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.session.ResultHandler;
 import org.sonar.api.user.UserQuery;
+import org.sonar.db.KeyLongValue;
 
 public interface UserMapper {
 
@@ -69,6 +70,20 @@ public interface UserMapper {
    */
   long countRootUsersButLogin(@Param("login") String login);
 
+  long countTotalUsers();
+
+  long countTeamUsers();
+
+  long countPersonalUsers();
+
+  long countPersonalUsersWithZeroProjects();
+
+  long countNewUsersSince(@Param("since") long since);
+
+  long countActiveUsers();
+
+  List<KeyLongValue> countUsersByIdentityProviders();
+
   void insert(@Param("user") UserDto userDto);
 
   void update(@Param("user") UserDto userDto);
index d6fd5aa89798fd464ddb99edee08d63101196e07..d4c71a6ee39415156113d853eeb7a3f1eb8d8ab5 100644 (file)
           where pb.project_uuid = p.uuid
         )
       </if>
+      <if test="query.analyzedAfter != null">
+        and (
+          exists(
+            -- branches of projects
+            select 1 from snapshots s
+            inner join project_branches pb on s.component_uuid = pb.uuid
+            where pb.project_uuid = p.uuid
+            and s.status='P'
+            and s.islast = ${_true}
+            and s.created_at &gt;= #{query.analyzedAfter,jdbcType=BIGINT}
+          )
+          or exists (
+            -- applications, portfolios
+            select 1 from snapshots s
+            where s.component_uuid = p.uuid
+            and s.status='P'
+            and s.islast = ${_true}
+            and s.created_at &gt;= #{query.analyzedAfter,jdbcType=BIGINT}
+          )
+        )
+      </if>
+    <if test="query.createdAfter != null">
+      and p.created_at &gt;= #{query.createdAfter,jdbcType=TIMESTAMP}
+    </if>
   </sql>
 
   <select id="selectDescendants" resultType="Component">
index 86231226a8f32290505f6b9eacb2bd849c7519e8..46d8dd1c9e722499c30e17d766ec292197454087 100644 (file)
             #{key, jdbcType=VARCHAR}
           </foreach>
       </if>
+      <if test="query.onlyTeam">
+        and not exists(
+          select 1
+          from users u
+          where u.organization_uuid = org.uuid
+          and u.active = ${_true}
+        )
+      </if>
+      <if test="query.onlyPersonal">
+        and exists(
+          select 1
+          from users u
+          where u.organization_uuid = org.uuid
+          and u.active = ${_true}
+        )
+      </if>
+      <if test="query.withAnalyses">
+        and exists(
+          select 1
+          from snapshots s
+          inner join projects p on p.uuid = s.component_uuid
+          where p.organization_uuid = org.uuid
+          and p.enabled = ${_true}
+          and s.islast = ${_true}
+        )
+      </if>
     </where>
   </sql>
 
+  <select id="countTeamsByMembers" resultType="org.sonar.db.KeyLongValue">
+    select range as "key", count(1) as "value"
+    from (
+      select case
+          when nb = 0 then '0'
+          when nb = 1 then '1'
+          when nb &gt;= 2 and nb &lt;= 4 then '2-4'
+          when nb &gt;= 5 and nb &lt;= 9 then '5-9'
+          when nb &gt;= 10 and nb &lt;= 24 then '10-24'
+          else '25+'
+        end as range
+      from (
+        select o.uuid, count(om.user_id) as nb
+        from organizations o
+        left join organization_members om on om.organization_uuid = o.uuid
+        where not exists(
+          select 1
+          from users u
+          where u.organization_uuid = o.uuid
+          and u.active = ${_true}
+        )
+        group by o.uuid
+      ) alias1
+    ) alias2
+    group by range
+  </select>
+
+  <select id="countTeamsByProjects" resultType="org.sonar.db.KeyLongValue">
+    select range as "key", count(1) as "value"
+    from (
+      select case
+          when nb = 0 then '0'
+          when nb = 1 then '1'
+          when nb &gt;= 2 and nb &lt;= 4 then '2-4'
+          when nb &gt;= 5 and nb &lt;= 9 then '5-9'
+          when nb &gt;= 10 and nb &lt;= 24 then '10-24'
+          else '25+'
+        end as range
+      from (
+        select o.uuid, count(p.uuid) as nb
+        from organizations o
+        left join projects p on p.organization_uuid = o.uuid
+        where not exists(
+          select 1
+          from users u
+          where u.organization_uuid = o.uuid
+          and u.active = ${_true}
+        )
+        group by o.uuid
+      ) alias1
+    ) alias2
+    group by range
+  </select>
+
   <select id="selectByPermission" parameterType="map" resultType="Organization">
     select
       <include refid="selectColumns"/>
index 6a37689fbf2471317557f7bba3248b3e4747ae20..1ae92c9f94195322cb56b34421c1616df0d7878f 100644 (file)
         and u.login &lt;&gt; #{login}
     </select>
 
+    <select id="countTotalUsers" resultType="long">
+        select count(1) from users
+    </select>
+
+    <select id="countTeamUsers" resultType="long">
+        select count(1)
+        from users
+        where active = ${_true}
+        and exists (
+          select 1
+          from organization_members om
+          where om.user_id = users.id
+          and (users.organization_uuid is null or om.organization_uuid != users.organization_uuid)
+        )
+    </select>
+
+    <select id="countPersonalUsers" resultType="long">
+        select count(1)
+        from users
+        where active = ${_true}
+        and organization_uuid is not null
+        and not exists (
+          select 1
+          from organization_members om
+          where om.user_id = users.id
+          and om.organization_uuid != users.organization_uuid
+        )
+    </select>
+
+    <select id="countPersonalUsersWithZeroProjects" resultType="long">
+        select count(1)
+        from users
+        where active = ${_true}
+        and organization_uuid is not null
+        and not exists (
+          select 1
+          from organization_members om
+          where om.user_id = users.id
+          and om.organization_uuid != users.organization_uuid
+        )
+        and not exists (
+          select 1
+          from projects p
+          where p.organization_uuid = users.organization_uuid
+        )
+    </select>
+
+    <select id="countNewUsersSince" parameterType="long" resultType="long">
+        select count(1)
+        from users
+        where created_at &gt; #{since,jdbcType=BIGINT}
+    </select>
+
+    <select id="countActiveUsers" resultType="long">
+        select count(1)
+        from users
+        where active = ${_true}
+    </select>
+
+    <select id="countUsersByIdentityProviders" resultType="org.sonar.db.KeyLongValue">
+        select external_identity_provider as "key", count(1) as "value"
+        from users
+        where active = ${_true}
+        group by external_identity_provider
+    </select>
+
     <update id="deactivateUser" parameterType="map">
         update users set
         active = ${_false},
index 138ee1940175394aec673e78fed41688b4f7fc9f..4d7f7d2b46c1af3c6535db9cf2ef3c1f36f53d97 100644 (file)
@@ -67,6 +67,9 @@ import static org.assertj.core.api.Assertions.entry;
 import static org.assertj.core.api.Assertions.tuple;
 import static org.assertj.guava.api.Assertions.assertThat;
 import static org.sonar.api.resources.Qualifiers.APP;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.api.utils.DateUtils.parseDate;
+import static org.sonar.db.component.ComponentTesting.newBranchDto;
 import static org.sonar.db.component.ComponentTesting.newDirectory;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newModuleDto;
@@ -76,7 +79,6 @@ import static org.sonar.db.component.ComponentTesting.newSubView;
 import static org.sonar.db.component.ComponentTesting.newView;
 import static org.sonar.db.component.ComponentTreeQuery.Strategy.CHILDREN;
 import static org.sonar.db.component.ComponentTreeQuery.Strategy.LEAVES;
-import static org.sonar.db.component.SnapshotTesting.newAnalysis;
 
 @RunWith(DataProviderRunner.class)
 public class ComponentDaoTest {
@@ -694,7 +696,7 @@ public class ComponentDaoTest {
 
   @DataProvider
   public static Object[][] oneOrMoreProjects() {
-    return new Object[][] {
+    return new Object[][]{
       {1},
       {1 + new Random().nextInt(10)}
     };
@@ -923,7 +925,7 @@ public class ComponentDaoTest {
 
   @DataProvider
   public static Object[][] portfolioOrApplicationRootViewQualifier() {
-    return new Object[][] {
+    return new Object[][]{
       {Qualifiers.VIEW},
       {Qualifiers.APP},
     };
@@ -1045,7 +1047,7 @@ public class ComponentDaoTest {
       .setEnabled(false));
     SnapshotDto analyzedPortfolio = db.components().insertProjectAndSnapshot(ComponentTesting.newView(organization));
 
-    Supplier<ComponentQuery.Builder> query = () -> ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT).setOnProvisionedOnly(true);
+    Supplier<ComponentQuery.Builder> query = () -> ComponentQuery.builder().setQualifiers(PROJECT).setOnProvisionedOnly(true);
     assertThat(underTest.selectByQuery(dbSession, organization.getUuid(), query.get().build(), 0, 10))
       .extracting(ComponentDto::uuid)
       .containsOnly(provisionedProject.uuid());
@@ -1055,10 +1057,10 @@ public class ComponentDaoTest {
 
     // filter on qualifiers
     assertThat(underTest.selectByQuery(dbSession, organization.getUuid(), query.get().setQualifiers("XXX").build(), 0, 10)).isEmpty();
-    assertThat(underTest.selectByQuery(dbSession, organization.getUuid(), query.get().setQualifiers(Qualifiers.PROJECT, "XXX").build(), 0, 10))
+    assertThat(underTest.selectByQuery(dbSession, organization.getUuid(), query.get().setQualifiers(PROJECT, "XXX").build(), 0, 10))
       .extracting(ComponentDto::uuid)
       .containsOnly(provisionedProject.uuid());
-    assertThat(underTest.selectByQuery(dbSession, organization.getUuid(), query.get().setQualifiers(Qualifiers.PROJECT, Qualifiers.VIEW).build(), 0, 10))
+    assertThat(underTest.selectByQuery(dbSession, organization.getUuid(), query.get().setQualifiers(PROJECT, Qualifiers.VIEW).build(), 0, 10))
       .extracting(ComponentDto::uuid)
       .containsOnly(provisionedProject.uuid(), provisionedPortfolio.uuid());
 
@@ -1082,7 +1084,7 @@ public class ComponentDaoTest {
   @Test
   public void selectByQuery_onProvisionedOnly_filters_projects_with_analysis_on_branch() {
     Supplier<ComponentQuery.Builder> query = () -> ComponentQuery.builder()
-      .setQualifiers(Qualifiers.PROJECT)
+      .setQualifiers(PROJECT)
       .setOnProvisionedOnly(true);
 
     // the project does not have any analysis
@@ -1109,9 +1111,9 @@ public class ComponentDaoTest {
     db.components().insertProjectAndSnapshot(ComponentTesting.newView(organization));
     Supplier<ComponentQuery.Builder> query = () -> ComponentQuery.builder().setOnProvisionedOnly(true);
 
-    assertThat(underTest.countByQuery(dbSession, organization.getUuid(), query.get().setQualifiers(Qualifiers.PROJECT).build())).isEqualTo(1);
+    assertThat(underTest.countByQuery(dbSession, organization.getUuid(), query.get().setQualifiers(PROJECT).build())).isEqualTo(1);
     assertThat(underTest.countByQuery(dbSession, organization.getUuid(), query.get().setQualifiers(Qualifiers.VIEW).build())).isEqualTo(0);
-    assertThat(underTest.countByQuery(dbSession, organization.getUuid(), query.get().setQualifiers(Qualifiers.PROJECT, Qualifiers.VIEW).build())).isEqualTo(1);
+    assertThat(underTest.countByQuery(dbSession, organization.getUuid(), query.get().setQualifiers(PROJECT, Qualifiers.VIEW).build())).isEqualTo(1);
   }
 
   @Test
@@ -1126,7 +1128,7 @@ public class ComponentDaoTest {
   public void countByQuery_throws_IAE_if_too_many_component_ids() {
     Set<Long> ids = LongStream.range(0L, 1_010L).boxed().collect(toSet());
     ComponentQuery.Builder query = ComponentQuery.builder()
-      .setQualifiers(Qualifiers.PROJECT)
+      .setQualifiers(PROJECT)
       .setComponentIds(ids);
 
     assertThatCountByQueryThrowsIAE(query, "Too many component ids in query");
@@ -1136,7 +1138,7 @@ public class ComponentDaoTest {
   public void countByQuery_throws_IAE_if_too_many_component_keys() {
     Set<String> keys = IntStream.range(0, 1_010).mapToObj(String::valueOf).collect(toSet());
     ComponentQuery.Builder query = ComponentQuery.builder()
-      .setQualifiers(Qualifiers.PROJECT)
+      .setQualifiers(PROJECT)
       .setComponentKeys(keys);
 
     assertThatCountByQueryThrowsIAE(query, "Too many component keys in query");
@@ -1146,7 +1148,7 @@ public class ComponentDaoTest {
   public void countByQuery_throws_IAE_if_too_many_component_uuids() {
     Set<String> uuids = IntStream.range(0, 1_010).mapToObj(String::valueOf).collect(toSet());
     ComponentQuery.Builder query = ComponentQuery.builder()
-      .setQualifiers(Qualifiers.PROJECT)
+      .setQualifiers(PROJECT)
       .setComponentUuids(uuids);
 
     assertThatCountByQueryThrowsIAE(query, "Too many component UUIDs in query");
@@ -1392,7 +1394,7 @@ public class ComponentDaoTest {
   public void selectByQuery_throws_IAE_if_too_many_component_ids() {
     Set<Long> ids = LongStream.range(0L, 1_010L).boxed().collect(toSet());
     ComponentQuery.Builder query = ComponentQuery.builder()
-      .setQualifiers(Qualifiers.PROJECT)
+      .setQualifiers(PROJECT)
       .setComponentIds(ids);
 
     assertThatSelectByQueryThrowsIAE(query, "Too many component ids in query");
@@ -1402,7 +1404,7 @@ public class ComponentDaoTest {
   public void selectByQuery_throws_IAE_if_too_many_component_keys() {
     Set<String> keys = IntStream.range(0, 1_010).mapToObj(String::valueOf).collect(toSet());
     ComponentQuery.Builder query = ComponentQuery.builder()
-      .setQualifiers(Qualifiers.PROJECT)
+      .setQualifiers(PROJECT)
       .setComponentKeys(keys);
 
     assertThatSelectByQueryThrowsIAE(query, "Too many component keys in query");
@@ -1412,7 +1414,7 @@ public class ComponentDaoTest {
   public void selectByQuery_throws_IAE_if_too_many_component_uuids() {
     Set<String> uuids = IntStream.range(0, 1_010).mapToObj(String::valueOf).collect(toSet());
     ComponentQuery.Builder query = ComponentQuery.builder()
-      .setQualifiers(Qualifiers.PROJECT)
+      .setQualifiers(PROJECT)
       .setComponentUuids(uuids);
 
     assertThatSelectByQueryThrowsIAE(query, "Too many component UUIDs in query");
@@ -1434,7 +1436,7 @@ public class ComponentDaoTest {
       db.components().insertProjectAndSnapshot(newPrivateProjectDto(organizationDto).setName("project-" + i));
     }
 
-    ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("oJect").setQualifiers(Qualifiers.PROJECT).build();
+    ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("oJect").setQualifiers(PROJECT).build();
     List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 1, 3);
     int count = underTest.countByQuery(dbSession, query);
 
@@ -1502,7 +1504,7 @@ public class ComponentDaoTest {
   public void selectByQuery_name_with_special_characters() {
     db.components().insertProjectAndSnapshot(newPrivateProjectDto(db.getDefaultOrganization()).setName("project-\\_%/-name"));
 
-    ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("-\\_%/-").setQualifiers(Qualifiers.PROJECT).build();
+    ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("-\\_%/-").setQualifiers(PROJECT).build();
     List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
 
     assertThat(result).hasSize(1);
@@ -1514,7 +1516,7 @@ public class ComponentDaoTest {
     db.components().insertProjectAndSnapshot(newPrivateProjectDto(db.organizations().insert()).setDbKey("project-_%-key"));
     db.components().insertProjectAndSnapshot(newPrivateProjectDto(db.organizations().insert()).setDbKey("project-key-that-does-not-match"));
 
-    ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("project-_%-key").setQualifiers(Qualifiers.PROJECT).build();
+    ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("project-_%-key").setQualifiers(PROJECT).build();
     List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
 
     assertThat(result).hasSize(1);
@@ -1528,7 +1530,7 @@ public class ComponentDaoTest {
     ComponentQuery query = ComponentQuery.builder()
       .setNameOrKeyQuery("JECT-K")
       .setPartialMatchOnKey(true)
-      .setQualifiers(Qualifiers.PROJECT).build();
+      .setQualifiers(PROJECT).build();
     List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
 
     assertThat(result).hasSize(1);
@@ -1540,7 +1542,7 @@ public class ComponentDaoTest {
     db.components().insertComponent(newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("java-project-key").setLanguage("java"));
     db.components().insertComponent(newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("cpp-project-key").setLanguage("cpp"));
 
-    ComponentQuery query = ComponentQuery.builder().setLanguage("java").setQualifiers(Qualifiers.PROJECT).build();
+    ComponentQuery query = ComponentQuery.builder().setLanguage("java").setQualifiers(PROJECT).build();
     List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
 
     assertThat(result).hasSize(1);
@@ -1548,22 +1550,76 @@ public class ComponentDaoTest {
   }
 
   @Test
-  public void selectByQuery_filter_on_last_analysis_date() {
+  public void selectByQuery_filter_last_analysis_date() {
     long aLongTimeAgo = 1_000_000_000L;
     long recentTime = 3_000_000_000L;
-    ComponentDto oldProject = db.components().insertPublicProject();
-    db.getDbClient().snapshotDao().insert(dbSession, newAnalysis(oldProject).setCreatedAt(aLongTimeAgo));
-    ComponentDto recentProject = db.components().insertPublicProject();
-    db.getDbClient().snapshotDao().insert(dbSession, newAnalysis(recentProject).setCreatedAt(recentTime));
-    db.getDbClient().snapshotDao().insert(dbSession, newAnalysis(recentProject).setCreatedAt(aLongTimeAgo).setLast(false));
-    ComponentQuery.Builder query = ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT);
-
-    assertThat(underTest.selectByQuery(dbSession, query.setAnalyzedBefore(recentTime).build(), 0, 10)).extracting(ComponentDto::getKey)
-      .containsExactlyInAnyOrder(oldProject.getKey());
-    assertThat(underTest.selectByQuery(dbSession, query.setAnalyzedBefore(aLongTimeAgo).build(), 0, 10)).extracting(ComponentDto::getKey)
+    ComponentDto oldProject = db.components().insertPrivateProject();
+    db.components().insertSnapshot(oldProject, s -> s.setCreatedAt(aLongTimeAgo));
+    ComponentDto recentProject = db.components().insertPrivateProject();
+    db.components().insertSnapshot(recentProject, s -> s.setCreatedAt(recentTime).setLast(true));
+    db.components().insertSnapshot(recentProject, s -> s.setCreatedAt(aLongTimeAgo).setLast(false));
+
+    // before date
+    assertThat(selectProjectUuidsByQuery(q -> q.setAnalyzedBefore(recentTime), 0, 10))
+      .containsExactlyInAnyOrder(oldProject.uuid());
+    assertThat(selectProjectUuidsByQuery(q -> q.setAnalyzedBefore(aLongTimeAgo), 0, 10))
       .isEmpty();
-    assertThat(underTest.selectByQuery(dbSession, query.setAnalyzedBefore(recentTime + 1_000L).build(), 0, 10)).extracting(ComponentDto::getKey)
-      .containsExactlyInAnyOrder(oldProject.getKey(), recentProject.getKey());
+    assertThat(selectProjectUuidsByQuery(q -> q.setAnalyzedBefore(recentTime + 1_000L), 0, 10))
+      .containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid());
+
+    // after date
+    assertThat(selectProjectUuidsByQuery(q -> q.setAnalyzedAfter(recentTime - 1_000L), 0, 10))
+      .containsExactlyInAnyOrder(recentProject.uuid());
+    assertThat(selectProjectUuidsByQuery(q -> q.setAnalyzedAfter(recentTime + 1_000L), 0, 10))
+      .isEmpty();
+    assertThat(selectProjectUuidsByQuery(q -> q.setAnalyzedAfter(aLongTimeAgo), 0, 10))
+      .containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid());
+  }
+
+  @Test
+  public void selectByQuery_filter_last_analysis_date_on_non_main_branches() {
+    long aLongTimeAgo = 1_000_000_000L;
+    long recentTime = 3_000_000_000L;
+    // project with only a non-main and old analyzed branch
+    ComponentDto oldProject = db.components().insertMainBranch();
+    ComponentDto oldProjectBranch = db.components().insertProjectBranch(oldProject, newBranchDto(oldProject).setBranchType(BranchType.SHORT));
+    db.components().insertSnapshot(oldProjectBranch, s -> s.setLast(true).setCreatedAt(aLongTimeAgo));
+
+    // project with only a old main branch and a recent non-main branch
+    ComponentDto recentProject = db.components().insertMainBranch();
+    ComponentDto recentProjectBranch = db.components().insertProjectBranch(recentProject, newBranchDto(recentProject).setBranchType(BranchType.SHORT));
+    db.components().insertSnapshot(recentProjectBranch, s -> s.setCreatedAt(recentTime).setLast(true));
+    db.components().insertSnapshot(recentProjectBranch, s -> s.setCreatedAt(aLongTimeAgo).setLast(false));
+
+    // after date
+    assertThat(selectProjectUuidsByQuery(q -> q.setAnalyzedAfter(recentTime - 1_000L), 0, 10))
+      .containsExactlyInAnyOrder(recentProject.uuid());
+    assertThat(selectProjectUuidsByQuery(q -> q.setAnalyzedAfter(recentTime + 1_000L), 0, 10))
+      .isEmpty();
+    assertThat(selectProjectUuidsByQuery(q -> q.setAnalyzedAfter(aLongTimeAgo), 0, 10))
+      .containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid());
+  }
+
+  @Test
+  public void selectByQuery_filter_created_at() {
+    ComponentDto project1 = db.components().insertPrivateProject(p -> p.setCreatedAt(parseDate("2018-02-01")));
+    ComponentDto project2 = db.components().insertPrivateProject(p -> p.setCreatedAt(parseDate("2018-06-01")));
+
+    assertThat(selectProjectUuidsByQuery(q -> q.setCreatedAfter(parseDate("2017-12-01")), 0, 10))
+      .containsExactlyInAnyOrder(project1.uuid(), project2.uuid());
+    assertThat(selectProjectUuidsByQuery(q -> q.setCreatedAfter(parseDate("2018-02-20")), 0, 10))
+      .containsExactlyInAnyOrder(project2.uuid());
+    assertThat(selectProjectUuidsByQuery(q -> q.setCreatedAfter(parseDate("2019-01-01")), 0, 10))
+      .isEmpty();
+  }
+
+  private List<String> selectProjectUuidsByQuery(Consumer<ComponentQuery.Builder> query, int offset, int limit) {
+    ComponentQuery.Builder builder = ComponentQuery.builder().setQualifiers(PROJECT);
+    query.accept(builder);
+    return underTest.selectByQuery(dbSession, builder.build(), offset, limit)
+      .stream()
+      .map(ComponentDto::uuid)
+      .collect(Collectors.toList());
   }
 
   @Test
@@ -1571,9 +1627,9 @@ public class ComponentDaoTest {
     db.components().insertComponent(newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("private-key"));
     db.components().insertComponent(ComponentTesting.newPublicProjectDto(db.getDefaultOrganization()).setDbKey("public-key"));
 
-    ComponentQuery privateProjectsQuery = ComponentQuery.builder().setPrivate(true).setQualifiers(Qualifiers.PROJECT).build();
-    ComponentQuery publicProjectsQuery = ComponentQuery.builder().setPrivate(false).setQualifiers(Qualifiers.PROJECT).build();
-    ComponentQuery allProjectsQuery = ComponentQuery.builder().setPrivate(null).setQualifiers(Qualifiers.PROJECT).build();
+    ComponentQuery privateProjectsQuery = ComponentQuery.builder().setPrivate(true).setQualifiers(PROJECT).build();
+    ComponentQuery publicProjectsQuery = ComponentQuery.builder().setPrivate(false).setQualifiers(PROJECT).build();
+    ComponentQuery allProjectsQuery = ComponentQuery.builder().setPrivate(null).setQualifiers(PROJECT).build();
 
     assertThat(underTest.selectByQuery(dbSession, privateProjectsQuery, 0, 10)).extracting(ComponentDto::getDbKey).containsExactly("private-key");
     assertThat(underTest.selectByQuery(dbSession, publicProjectsQuery, 0, 10)).extracting(ComponentDto::getDbKey).containsExactly("public-key");
@@ -1583,7 +1639,7 @@ public class ComponentDaoTest {
   @Test
   public void selectByQuery_on_empty_list_of_component_id() {
     db.components().insertPrivateProject();
-    ComponentQuery dbQuery = ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT).setComponentIds(emptySet()).build();
+    ComponentQuery dbQuery = ComponentQuery.builder().setQualifiers(PROJECT).setComponentIds(emptySet()).build();
 
     List<ComponentDto> result = underTest.selectByQuery(dbSession, dbQuery, 0, 10);
     int count = underTest.countByQuery(dbSession, dbQuery);
@@ -1599,7 +1655,7 @@ public class ComponentDaoTest {
     ComponentDto jdk8 = db.components().insertComponent(newPrivateProjectDto(organizationDto));
     ComponentDto cLang = db.components().insertComponent(newPrivateProjectDto(organizationDto));
 
-    ComponentQuery query = ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT)
+    ComponentQuery query = ComponentQuery.builder().setQualifiers(PROJECT)
       .setComponentIds(newHashSet(sonarqube.getId(), jdk8.getId())).build();
     List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
 
@@ -1611,7 +1667,7 @@ public class ComponentDaoTest {
   @Test
   public void selectByQuery_on_empty_list_of_component_key() {
     db.components().insertPrivateProject();
-    ComponentQuery dbQuery = ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT).setComponentKeys(emptySet()).build();
+    ComponentQuery dbQuery = ComponentQuery.builder().setQualifiers(PROJECT).setComponentKeys(emptySet()).build();
 
     List<ComponentDto> result = underTest.selectByQuery(dbSession, dbQuery, 0, 10);
     int count = underTest.countByQuery(dbSession, dbQuery);
@@ -1626,7 +1682,7 @@ public class ComponentDaoTest {
     ComponentDto sonarqube = db.components().insertComponent(newPrivateProjectDto(organizationDto));
     ComponentDto jdk8 = db.components().insertComponent(newPrivateProjectDto(organizationDto));
     ComponentDto cLang = db.components().insertComponent(newPrivateProjectDto(organizationDto));
-    ComponentQuery query = ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT)
+    ComponentQuery query = ComponentQuery.builder().setQualifiers(PROJECT)
       .setComponentKeys(newHashSet(sonarqube.getDbKey(), jdk8.getDbKey())).build();
 
     List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
@@ -1639,7 +1695,7 @@ public class ComponentDaoTest {
   @Test
   public void selectByQuery_on_empty_list_of_component_uuids() {
     db.components().insertPrivateProject();
-    ComponentQuery dbQuery = ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT).setComponentUuids(emptySet()).build();
+    ComponentQuery dbQuery = ComponentQuery.builder().setQualifiers(PROJECT).setComponentUuids(emptySet()).build();
 
     List<ComponentDto> result = underTest.selectByQuery(dbSession, dbQuery, 0, 10);
     int count = underTest.countByQuery(dbSession, dbQuery);
@@ -1654,7 +1710,7 @@ public class ComponentDaoTest {
     ComponentDto sonarqube = db.components().insertComponent(newPrivateProjectDto(organizationDto));
     ComponentDto jdk8 = db.components().insertComponent(newPrivateProjectDto(organizationDto));
     ComponentDto cLang = db.components().insertComponent(newPrivateProjectDto(organizationDto));
-    ComponentQuery query = ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT)
+    ComponentQuery query = ComponentQuery.builder().setQualifiers(PROJECT)
       .setComponentUuids(newHashSet(sonarqube.uuid(), jdk8.uuid())).build();
 
     List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
index 3c6c238a73fb6d6a4483a9f6250f31998952c465..2c3c8f810611c3fe73e41228a4be896bbf4c3de3 100644 (file)
@@ -29,8 +29,11 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Random;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 import org.apache.ibatis.exceptions.PersistenceException;
+import org.assertj.core.groups.Tuple;
 import org.assertj.core.util.Lists;
 import org.junit.Rule;
 import org.junit.Test;
@@ -39,6 +42,9 @@ import org.sonar.api.utils.System2;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.KeyLongValue;
+import org.sonar.db.Pagination;
+import org.sonar.db.component.ComponentDto;
 import org.sonar.db.dialect.Dialect;
 import org.sonar.db.dialect.Oracle;
 import org.sonar.db.qualitygate.QGateWithOrgDto;
@@ -53,6 +59,7 @@ import static org.assertj.core.api.Assertions.tuple;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.sonar.db.Pagination.forPage;
+import static org.sonar.db.organization.OrganizationQuery.Builder;
 import static org.sonar.db.organization.OrganizationQuery.newOrganizationQueryBuilder;
 import static org.sonar.db.organization.OrganizationQuery.returnAll;
 import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
@@ -509,7 +516,7 @@ public class OrganizationDaoTest {
     db.organizations().addMember(organization, user);
     db.organizations().addMember(anotherOrganization, user);
 
-    List<OrganizationDto> result = underTest.selectByQuery(dbSession, OrganizationQuery.newOrganizationQueryBuilder().setMember(user.getId()).build(), forPage(1).andSize(100));
+    List<OrganizationDto> result = underTest.selectByQuery(dbSession, newOrganizationQueryBuilder().setMember(user.getId()).build(), forPage(1).andSize(100));
 
     assertThat(result).extracting(OrganizationDto::getUuid)
       .containsExactlyInAnyOrder(organization.getUuid(), anotherOrganization.getUuid())
@@ -527,7 +534,7 @@ public class OrganizationDaoTest {
     db.organizations().addMember(anotherOrganization, user);
     db.organizations().addMember(organizationWithoutKeyProvided, user);
 
-    List<OrganizationDto> result = underTest.selectByQuery(dbSession, OrganizationQuery.newOrganizationQueryBuilder()
+    List<OrganizationDto> result = underTest.selectByQuery(dbSession, newOrganizationQueryBuilder()
       .setKeys(Arrays.asList(organization.getKey(), anotherOrganization.getKey(), organizationWithoutMember.getKey()))
       .setMember(user.getId()).build(), forPage(1).andSize(100));
 
@@ -536,6 +543,51 @@ public class OrganizationDaoTest {
       .doesNotContain(organizationWithoutKeyProvided.getUuid(), organizationWithoutMember.getUuid());
   }
 
+  @Test
+  public void selectByQuery_filter_on_type() {
+    OrganizationDto personalOrg1 = db.organizations().insert();
+    db.users().insertUser(u -> u.setOrganizationUuid(personalOrg1.getUuid()));
+    OrganizationDto personalOrg2 = db.organizations().insert();
+    db.users().insertUser(u -> u.setOrganizationUuid(personalOrg2.getUuid()));
+    OrganizationDto teamOrg1 = db.organizations().insert();
+
+    assertThat(selectUuidsByQuery(q -> q.setOnlyPersonal(), forPage(1).andSize(100)))
+      .containsExactlyInAnyOrder(personalOrg1.getUuid(), personalOrg2.getUuid());
+    assertThat(selectUuidsByQuery(q -> q.setOnlyTeam(), forPage(1).andSize(100)))
+      .containsExactlyInAnyOrder(teamOrg1.getUuid());
+  }
+
+  @Test
+  public void selectByQuery_filter_on_withAnalyses() {
+    assertThat(selectUuidsByQuery(q -> q.setWithAnalyses(), forPage(1).andSize(100)))
+      .isEmpty();
+
+    // has projects and analyses
+    OrganizationDto orgWithAnalyses = db.organizations().insert();
+    ComponentDto analyzedProject = db.components().insertPrivateProject(orgWithAnalyses);
+    db.components().insertSnapshot(analyzedProject, s -> s.setLast(true));
+    // has projects but no analyses
+    OrganizationDto orgWithProjects = db.organizations().insert();
+    db.components().insertPrivateProject(orgWithProjects);
+    db.components().insertPrivateProject(orgWithProjects, p -> p.setEnabled(false));
+    // has no projects
+    db.organizations().insert();
+    // has only disabled projects
+    OrganizationDto orgWithOnlyDisabledProjects = db.organizations().insert();
+    db.components().insertPrivateProject(orgWithOnlyDisabledProjects, p -> p.setEnabled(false));
+
+    assertThat(selectUuidsByQuery(q -> q.setWithAnalyses(), forPage(1).andSize(100)))
+      .containsExactlyInAnyOrder(orgWithAnalyses.getUuid());
+  }
+
+  private List<String> selectUuidsByQuery(Consumer<Builder> query, Pagination pagination) {
+    Builder builder = newOrganizationQueryBuilder();
+    query.accept(builder);
+    return underTest.selectByQuery(dbSession, builder.build(), pagination).stream()
+      .map(OrganizationDto::getUuid)
+      .collect(Collectors.toList());
+  }
+
   @Test
   public void getDefaultTemplates_returns_empty_when_table_is_empty() {
     assertThat(underTest.getDefaultTemplates(dbSession, ORGANIZATION_DTO_1.getUuid())).isEmpty();
@@ -923,6 +975,46 @@ public class OrganizationDaoTest {
       .containsOnlyOnce(organization.getUuid());
   }
 
+  @Test
+  public void countTeamsByMembers() {
+    assertThat(underTest.countTeamsByMembers(dbSession)).isEmpty();
+
+    UserDto user1 = db.users().insertUser();
+    UserDto user2 = db.users().insertUser();
+    UserDto user3 = db.users().insertUser();
+    OrganizationDto org1 = db.organizations().insert();
+    db.organizations().addMember(org1, user1, user2, user3);
+    OrganizationDto org2 = db.organizations().insert();
+    db.organizations().addMember(org2, user1);
+    OrganizationDto org3 = db.organizations().insert();
+    db.organizations().addMember(org3, user1, user2, user3);
+
+    assertThat(underTest.countTeamsByMembers(dbSession))
+      .extracting(KeyLongValue::getKey, KeyLongValue::getValue)
+      .containsExactlyInAnyOrder(Tuple.tuple("1", 1L), Tuple.tuple("2-4", 2L));
+
+  }
+
+  @Test
+  public void countTeamsByProjects() {
+    assertThat(underTest.countTeamsByProjects(dbSession)).isEmpty();
+
+    OrganizationDto org1 = db.organizations().insert();
+    db.components().insertPrivateProject(org1);
+    OrganizationDto org2 = db.organizations().insert();
+    db.components().insertPrivateProject(org2);
+    db.components().insertPrivateProject(org2);
+    OrganizationDto org3 = db.organizations().insert();
+    db.components().insertPrivateProject(org3);
+    db.components().insertPrivateProject(org3);
+    db.components().insertPrivateProject(org3);
+
+    assertThat(underTest.countTeamsByProjects(dbSession))
+      .extracting(KeyLongValue::getKey, KeyLongValue::getValue)
+      .containsExactlyInAnyOrder(Tuple.tuple("1", 1L), Tuple.tuple("2-4", 2L));
+
+  }
+
   private void expectDtoCanNotBeNull() {
     expectedException.expect(NullPointerException.class);
     expectedException.expectMessage("OrganizationDto can't be null");
index f54f8923af80eb2430c5daf39ae2bd291de7171c..761b56264277bffd2e57e8e205efc69887418c9e 100644 (file)
@@ -33,6 +33,7 @@ import org.sonar.db.DatabaseUtils;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.KeyLongValue;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.organization.OrganizationDto;
 
@@ -301,6 +302,94 @@ public class UserDaoTest {
     assertThat(underTest.countRootUsersButLogin(session, rootLogin)).isEqualTo(2);
   }
 
+  @Test
+  public void countTotalUsers() {
+    assertThat(underTest.countTotalUsers(session)).isEqualTo(0);
+
+    db.users().insertUser(u -> u.setActive(false));
+    db.users().insertUser(u -> u.setActive(true));
+
+    assertThat(underTest.countTotalUsers(session)).isEqualTo(2);
+  }
+
+  @Test
+  public void countTeamUsers() {
+    assertThat(underTest.countTeamUsers(session)).isEqualTo(0);
+
+    // user 1: with only personal organization
+    OrganizationDto user1Org = db.organizations().insert();
+    insertNonRootUser(newUserDto().setOrganizationUuid(user1Org.getUuid()));
+    assertThat(underTest.countTeamUsers(session)).isEqualTo(0);
+
+    // user 2: with no organizations at all
+    insertNonRootUser(newUserDto().setOrganizationUuid(null));
+    assertThat(underTest.countTeamUsers(session)).isEqualTo(0);
+
+    // user 3: with personal and team organizations
+    OrganizationDto user3Org = db.organizations().insert();
+    OrganizationDto teamOrg = db.organizations().insert();
+    UserDto user3 = insertNonRootUser(newUserDto().setOrganizationUuid(user3Org.getUuid()));
+    db.organizations().addMember(teamOrg, user3);
+    assertThat(underTest.countTeamUsers(session)).isEqualTo(1);
+  }
+
+  @Test
+  public void countPersonalUsers() {
+    assertThat(underTest.countPersonalUsers(session)).isEqualTo(0);
+
+    // user 1: with only personal organization
+    OrganizationDto user1Org = db.organizations().insert();
+    insertNonRootUser(newUserDto().setOrganizationUuid(user1Org.getUuid()));
+    assertThat(underTest.countPersonalUsers(session)).isEqualTo(1);
+
+    // user 2: with no organizations at all
+    insertNonRootUser(newUserDto().setOrganizationUuid(null));
+    assertThat(underTest.countPersonalUsers(session)).isEqualTo(1);
+
+    // user 3: with personal and team organizations
+    OrganizationDto user3Org = db.organizations().insert();
+    OrganizationDto teamOrg = db.organizations().insert();
+    UserDto user3 = insertNonRootUser(newUserDto().setOrganizationUuid(user3Org.getUuid()));
+    db.organizations().addMember(teamOrg, user3);
+    assertThat(underTest.countPersonalUsers(session)).isEqualTo(1);
+
+    // user 4: excluded because deactivated
+    OrganizationDto user4Org = db.organizations().insert();
+    insertNonRootUser(newUserDto().setOrganizationUuid(user4Org.getUuid()).setActive(false));
+    assertThat(underTest.countPersonalUsers(session)).isEqualTo(1);
+  }
+
+  @Test
+  public void countNewUsersSince() {
+    assertThat(underTest.countNewUsersSince(session, 400L)).isEqualTo(0);
+
+    when(system2.now()).thenReturn(100L);
+    insertNonRootUser(newUserDto());
+    when(system2.now()).thenReturn(200L);
+    insertNonRootUser(newUserDto());
+    when(system2.now()).thenReturn(300L);
+    insertNonRootUser(newUserDto());
+
+    assertThat(underTest.countNewUsersSince(session, 50L)).isEqualTo(3);
+    assertThat(underTest.countNewUsersSince(session, 190L)).isEqualTo(2);
+    assertThat(underTest.countNewUsersSince(session, 400L)).isEqualTo(0);
+  }
+
+  @Test
+  public void countUsersByIdentityProviders() {
+    assertThat(underTest.countUsersByIdentityProviders(session)).isEmpty();
+
+    db.users().insertUser(u -> u.setActive(true).setExternalIdentityProvider("bitbucket"));
+    db.users().insertUser(u -> u.setActive(true).setExternalIdentityProvider("github"));
+    db.users().insertUser(u -> u.setActive(true).setExternalIdentityProvider("github"));
+    // this used is excluded because deactivated
+    db.users().insertUser(u -> u.setActive(false).setExternalIdentityProvider("github"));
+
+    assertThat(underTest.countUsersByIdentityProviders(session))
+      .extracting(KeyLongValue::getKey, KeyLongValue::getValue)
+      .containsExactlyInAnyOrder(tuple("bitbucket", 1L), tuple("github", 2L));
+  }
+
   private UserDto insertInactiveRootUser(UserDto dto) {
     insertRootUser(dto);
     dto.setActive(false);