From 429a800e029f2cb57492e1550507b6ba896b8fd5 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Wed, 20 Jun 2018 22:06:18 +0200 Subject: [PATCH] SONARCLOUD-78 add metrics --- .../main/java/org/sonar/db/KeyLongValue.java | 9 ++ .../sonar/db/component/ComponentQuery.java | 32 ++++ .../db/organization/OrganizationDao.java | 9 ++ .../db/organization/OrganizationMapper.java | 5 + .../db/organization/OrganizationQuery.java | 44 +++++- .../main/java/org/sonar/db/user/UserDao.java | 32 ++++ .../java/org/sonar/db/user/UserMapper.java | 15 ++ .../sonar/db/component/ComponentMapper.xml | 24 +++ .../db/organization/OrganizationMapper.xml | 80 ++++++++++ .../org/sonar/db/user/UserMapper.xml | 66 +++++++++ .../sonar/db/component/ComponentDaoTest.java | 140 ++++++++++++------ .../db/organization/OrganizationDaoTest.java | 96 +++++++++++- .../java/org/sonar/db/user/UserDaoTest.java | 89 +++++++++++ 13 files changed, 595 insertions(+), 46 deletions(-) diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/KeyLongValue.java b/server/sonar-db-dao/src/main/java/org/sonar/db/KeyLongValue.java index 88c7f505d12..e1097b0f56c 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/KeyLongValue.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/KeyLongValue.java @@ -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; } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentQuery.java index 487b7b2a252..01fc40ca2cf 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentQuery.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentQuery.java @@ -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 componentUuids; private final Set 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 componentUuids; private Set 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; diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDao.java index f55b9952c6f..58fc0cd8c59 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationDao.java @@ -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 countTeamsByMembers(DbSession dbSession) { + return getMapper(dbSession).countTeamsByMembers(); + } + + public List countTeamsByProjects(DbSession dbSession) { + return getMapper(dbSession).countTeamsByProjects(); + } + public List selectByQuery(DbSession dbSession, OrganizationQuery organizationQuery, Pagination pagination) { requireNonNull(organizationQuery, "organizationQuery can't be null"); return getMapper(dbSession).selectByQuery(organizationQuery, pagination); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationMapper.java index 24763e9a215..e3ce1865389 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationMapper.java @@ -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 countTeamsByMembers(); + + List countTeamsByProjects(); + List selectByQuery(@Param("query") OrganizationQuery organizationQuery, @Param("pagination") Pagination pagination); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationQuery.java index fbd479f86b1..2117660e8e6 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationQuery.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/organization/OrganizationQuery.java @@ -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 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 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); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java index f78aebc4f71..93fe5bab021 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java @@ -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 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)); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java index 2ab7fe93dab..671f8755bd7 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserMapper.java @@ -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 countUsersByIdentityProviders(); + void insert(@Param("user") UserDto userDto); void update(@Param("user") UserDto userDto); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml index d6fd5aa8979..d4c71a6ee39 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml @@ -358,6 +358,30 @@ where pb.project_uuid = p.uuid ) + + 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 >= #{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 >= #{query.analyzedAfter,jdbcType=BIGINT} + ) + ) + + + and p.created_at >= #{query.createdAfter,jdbcType=TIMESTAMP} + + select range as "key", count(1) as "value" + from ( + select case + when nb = 0 then '0' + when nb = 1 then '1' + when nb >= 2 and nb <= 4 then '2-4' + when nb >= 5 and nb <= 9 then '5-9' + when nb >= 10 and nb <= 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 + + + + + + + + + + + + + + + + + + update users set active = ${_false}, diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java index 138ee194017..4d7f7d2b46c 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java @@ -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 query = () -> ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT).setOnProvisionedOnly(true); + Supplier 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 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 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 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 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 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 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 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 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 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 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 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 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 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 selectProjectUuidsByQuery(Consumer 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 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 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 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 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 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 result = underTest.selectByQuery(dbSession, query, 0, 10); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java index 3c6c238a73f..2c3c8f81061 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/organization/OrganizationDaoTest.java @@ -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 result = underTest.selectByQuery(dbSession, OrganizationQuery.newOrganizationQueryBuilder().setMember(user.getId()).build(), forPage(1).andSize(100)); + List 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 result = underTest.selectByQuery(dbSession, OrganizationQuery.newOrganizationQueryBuilder() + List 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 selectUuidsByQuery(Consumer 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"); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java index f54f8923af8..761b5626427 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/user/UserDaoTest.java @@ -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); -- 2.39.5