aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorBenoƮt Gianinetti <benoit.gianinetti@sonarsource.com>2019-05-06 14:15:46 +0200
committerSonarTech <sonartech@sonarsource.com>2019-05-15 20:21:11 +0200
commit5e41f4d0ce6ffac895a9dd5126e2066f261ab11e (patch)
treef33aca461594c7e233429bb811651f0f149fd97b /server
parent925daa3fdb2b5936553482338a8ab869ccfda639 (diff)
downloadsonarqube-5e41f4d0ce6ffac895a9dd5126e2066f261ab11e.tar.gz
sonarqube-5e41f4d0ce6ffac895a9dd5126e2066f261ab11e.zip
SONAR-12061 Create 'INTERNAL_COMPONENT_PROPS' table
Diffstat (limited to 'server')
-rw-r--r--server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java2
-rw-r--r--server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl10
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java7
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java4
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertiesDao.java63
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertiesMapper.java35
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertyDto.java108
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/property/InternalComponentPropertiesMapper.xml62
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java2
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/property/InternalComponentPropertiesDaoTest.java223
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/property/InternalComponentPropertyDtoTest.java104
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v78/CreateInternalComponentPropertiesTable.java106
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v78/DbVersion78.java3
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v78/CreateInternalComponentPropertiesTableTest.java62
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v78/DbVersion78Test.java2
16 files changed, 791 insertions, 4 deletions
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
index 0003b7c7cf1..3494bc34fc6 100644
--- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
+++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
@@ -122,7 +122,7 @@ public class ComputeEngineContainerImplTest {
assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize(
COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION
+ 26 // level 1
- + 60 // content of DaoModule
+ + 61 // content of DaoModule
+ 3 // content of EsModule
+ 52 // content of CorePropertyDefinitions
+ 1 // StopFlagContainer
diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
index f80f1a62bcd..786e100f102 100644
--- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
+++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
@@ -451,6 +451,16 @@ CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
+CREATE TABLE "INTERNAL_COMPONENT_PROPS" (
+ "UUID" VARCHAR(40) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(50) NOT NULL,
+ "KEE" VARCHAR(512) NOT NULL,
+ "VALUE" VARCHAR(4000),
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ CONSTRAINT "INTERNAL_COMPONENT_PROPS_UUID" PRIMARY KEY ("UUID")
+);
+CREATE UNIQUE INDEX "UNIQUE_COMPONENT_UUID_KEE" ON "INTERNAL_COMPONENT_PROPS" ("COMPONENT_UUID", "KEE");
CREATE TABLE "MANUAL_MEASURES" (
"ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
index 6958da9881d..3f833346883 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
@@ -58,6 +58,7 @@ import org.sonar.db.permission.UserPermissionDao;
import org.sonar.db.permission.template.PermissionTemplateCharacteristicDao;
import org.sonar.db.permission.template.PermissionTemplateDao;
import org.sonar.db.plugin.PluginDao;
+import org.sonar.db.property.InternalComponentPropertiesDao;
import org.sonar.db.property.InternalPropertiesDao;
import org.sonar.db.property.PropertiesDao;
import org.sonar.db.purge.PurgeDao;
@@ -113,6 +114,7 @@ public class DaoModule extends Module {
GroupPermissionDao.class,
AlmAppInstallDao.class,
ProjectAlmBindingDao.class,
+ InternalComponentPropertiesDao.class,
InternalPropertiesDao.class,
IssueChangeDao.class,
IssueDao.class,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
index 483e5b56bb2..eb220ed54c0 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
@@ -56,6 +56,7 @@ import org.sonar.db.permission.UserPermissionDao;
import org.sonar.db.permission.template.PermissionTemplateCharacteristicDao;
import org.sonar.db.permission.template.PermissionTemplateDao;
import org.sonar.db.plugin.PluginDao;
+import org.sonar.db.property.InternalComponentPropertiesDao;
import org.sonar.db.property.InternalPropertiesDao;
import org.sonar.db.property.PropertiesDao;
import org.sonar.db.purge.PurgeDao;
@@ -96,6 +97,7 @@ public class DbClient {
private final PropertiesDao propertiesDao;
private final AlmAppInstallDao almAppInstallDao;
private final ProjectAlmBindingDao projectAlmBindingDao;
+ private final InternalComponentPropertiesDao internalComponentPropertiesDao;
private final InternalPropertiesDao internalPropertiesDao;
private final SnapshotDao snapshotDao;
private final ComponentDao componentDao;
@@ -218,6 +220,7 @@ public class DbClient {
webhookDeliveryDao = getDao(map, WebhookDeliveryDao.class);
projectMappingsDao = getDao(map, ProjectMappingsDao.class);
organizationAlmBindingDao = getDao(map, OrganizationAlmBindingDao.class);
+ internalComponentPropertiesDao = getDao(map, InternalComponentPropertiesDao.class);
}
public DbSession openSession(boolean batch) {
@@ -477,4 +480,8 @@ public class DbClient {
public OrganizationAlmBindingDao organizationAlmBindingDao() {
return organizationAlmBindingDao;
}
+
+ public InternalComponentPropertiesDao internalComponentPropertiesDao() {
+ return internalComponentPropertiesDao;
+ }
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
index 5ce01ea804a..4ac08ada7a2 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
@@ -99,6 +99,8 @@ import org.sonar.db.permission.template.PermissionTemplateMapper;
import org.sonar.db.permission.template.PermissionTemplateUserDto;
import org.sonar.db.plugin.PluginDto;
import org.sonar.db.plugin.PluginMapper;
+import org.sonar.db.property.InternalComponentPropertiesMapper;
+import org.sonar.db.property.InternalComponentPropertyDto;
import org.sonar.db.property.InternalPropertiesMapper;
import org.sonar.db.property.InternalPropertyDto;
import org.sonar.db.property.PropertiesMapper;
@@ -180,6 +182,7 @@ public class MyBatis implements Startable {
confBuilder.loadAlias("GroupPermission", GroupPermissionDto.class);
confBuilder.loadAlias("IdUuidPair", IdUuidPair.class);
confBuilder.loadAlias("InternalProperty", InternalPropertyDto.class);
+ confBuilder.loadAlias("InternalComponentProperty", InternalComponentPropertyDto.class);
confBuilder.loadAlias("IssueChange", IssueChangeDto.class);
confBuilder.loadAlias("KeyLongValue", KeyLongValue.class);
confBuilder.loadAlias("Issue", IssueDto.class);
@@ -241,6 +244,7 @@ public class MyBatis implements Startable {
GroupMapper.class,
GroupMembershipMapper.class,
GroupPermissionMapper.class,
+ InternalComponentPropertiesMapper.class,
InternalPropertiesMapper.class,
IsAliveMapper.class,
IssueChangeMapper.class,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertiesDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertiesDao.java
new file mode 100644
index 00000000000..b9ae5bfa306
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertiesDao.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.property;
+
+import java.util.Optional;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.Dao;
+import org.sonar.db.DbSession;
+
+public class InternalComponentPropertiesDao implements Dao {
+
+ private final System2 system2;
+ private final UuidFactory uuidFactory;
+
+ public InternalComponentPropertiesDao(System2 system2, UuidFactory uuidFactory) {
+ this.system2 = system2;
+ this.uuidFactory = uuidFactory;
+ }
+
+ public void insertOrUpdate(DbSession dbSession, InternalComponentPropertyDto dto) {
+ InternalComponentPropertiesMapper mapper = getMapper(dbSession);
+
+ dto.setUpdatedAt(system2.now());
+
+ if (mapper.update(dto) == 1) {
+ return;
+ }
+
+ dto.setUuid(uuidFactory.create());
+ dto.setCreatedAt(system2.now());
+ mapper.insert(dto);
+ }
+
+ public Optional<InternalComponentPropertyDto> selectByComponentUuidAndKey(DbSession dbSession, String componentUuid, String key) {
+ return getMapper(dbSession).selectByComponentUuidAndKey(componentUuid, key);
+ }
+
+ public int deleteByComponentUuidAndKey(DbSession dbSession, String componentUuid, String key) {
+ return getMapper(dbSession).deleteByComponentUuidAndKey(componentUuid, key);
+ }
+
+ private static InternalComponentPropertiesMapper getMapper(DbSession dbSession) {
+ return dbSession.getMapper(InternalComponentPropertiesMapper.class);
+ }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertiesMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertiesMapper.java
new file mode 100644
index 00000000000..3ddc22c6634
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertiesMapper.java
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.property;
+
+import java.util.Optional;
+import org.apache.ibatis.annotations.Param;
+
+public interface InternalComponentPropertiesMapper {
+
+ Optional<InternalComponentPropertyDto> selectByComponentUuidAndKey(@Param("componentUuid") String componentUuid, @Param("key") String key);
+
+ void insert(@Param("dto") InternalComponentPropertyDto dto);
+
+ int update(@Param("dto") InternalComponentPropertyDto dto);
+
+ int deleteByComponentUuidAndKey(@Param("componentUuid") String componentUuid, @Param("key") String key);
+
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertyDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertyDto.java
new file mode 100644
index 00000000000..c30e13df573
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/property/InternalComponentPropertyDto.java
@@ -0,0 +1,108 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.property;
+
+import com.google.common.base.MoreObjects;
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class InternalComponentPropertyDto {
+ private static final int MAX_KEY_LENGTH = 512;
+ private static final int MAX_VALUE_LENGTH = 4000;
+
+ private String uuid;
+ private String key;
+ private String value;
+ private String componentUuid;
+ private Long createdAt;
+ private Long updatedAt;
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public InternalComponentPropertyDto setUuid(String uuid) {
+ this.uuid = uuid;
+ return this;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public InternalComponentPropertyDto setKey(String key) {
+ checkArgument(key != null && !key.isEmpty(), "key can't be null nor empty");
+ checkArgument(key.length() <= MAX_KEY_LENGTH, "key length (%s) is longer than the maximum authorized (%s). '%s' was provided", key.length(), MAX_KEY_LENGTH, key);
+ this.key = key;
+ return this;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public InternalComponentPropertyDto setValue(@Nullable String value) {
+ if (value != null) {
+ checkArgument(value.length() <= MAX_VALUE_LENGTH, "value length (%s) is longer than the maximum authorized (%s). '%s' was provided", value.length(), MAX_VALUE_LENGTH, value);
+ }
+ this.value = value;
+ return this;
+ }
+
+ public String getComponentUuid() {
+ return componentUuid;
+ }
+
+ public InternalComponentPropertyDto setComponentUuid(String componentUuid) {
+ this.componentUuid = componentUuid;
+ return this;
+ }
+
+ public Long getCreatedAt() {
+ return createdAt;
+ }
+
+ public InternalComponentPropertyDto setCreatedAt(Long createdAt) {
+ this.createdAt = createdAt;
+ return this;
+ }
+
+ public Long getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public InternalComponentPropertyDto setUpdatedAt(Long updatedAt) {
+ this.updatedAt = updatedAt;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper("InternalComponentPropertyDto")
+ .add("uuid", this.uuid)
+ .add("key", this.key)
+ .add("value", this.value)
+ .add("componentUuid", this.componentUuid)
+ .add("updatedAt", this.updatedAt)
+ .add("createdAt", this.createdAt)
+ .toString();
+ }
+}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/property/InternalComponentPropertiesMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/property/InternalComponentPropertiesMapper.xml
new file mode 100644
index 00000000000..1ad615315e7
--- /dev/null
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/property/InternalComponentPropertiesMapper.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.db.property.InternalComponentPropertiesMapper">
+
+ <select id="selectByComponentUuidAndKey" parameterType="map" resultType="org.sonar.db.property.InternalComponentPropertyDto">
+ SELECT
+ p.uuid as uuid,
+ p.component_uuid as componentUuid,
+ p.kee as "key",
+ p.value as value,
+ p.updated_at as updatedAt,
+ p.created_at as createdAt
+ FROM
+ internal_component_props p
+ <where>
+ p.component_uuid = #{componentUuid, jdbcType=VARCHAR}
+ AND p.kee = #{key, jdbcType=VARCHAR}
+ </where>
+ </select>
+
+ <insert id="insert" parameterType="Map">
+ insert into internal_component_props
+ (
+ uuid,
+ component_uuid,
+ kee,
+ value,
+ updated_at,
+ created_at
+ )
+ values (
+ #{dto.uuid},
+ #{dto.componentUuid},
+ #{dto.key},
+ #{dto.value},
+ #{dto.updatedAt},
+ #{dto.createdAt}
+ )
+ </insert>
+
+ <update id="update" parameterType="map">
+ update internal_component_props
+ <set>
+ value = #{dto.value, jdbcType=VARCHAR},
+ updated_at = #{dto.updatedAt, jdbcType=BIGINT}
+ </set>
+ <where>
+ component_uuid = #{dto.componentUuid, jdbcType=VARCHAR}
+ AND kee = #{dto.key}
+ </where>
+ </update>
+
+ <delete id="deleteByComponentUuidAndKey" parameterType="map">
+ DELETE FROM internal_component_props
+ <where>
+ component_uuid = #{componentUuid, jdbcType=VARCHAR}
+ AND kee = #{key,jdbcType=VARCHAR}
+ </where>
+ </delete>
+
+</mapper>
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java
index 1f9ac9b80cc..184f56ad80d 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java
@@ -30,6 +30,6 @@ public class DaoModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new DaoModule().configure(container);
- assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 60);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 61);
}
}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/property/InternalComponentPropertiesDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/property/InternalComponentPropertiesDaoTest.java
new file mode 100644
index 00000000000..b7eecb8b1dc
--- /dev/null
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/property/InternalComponentPropertiesDaoTest.java
@@ -0,0 +1,223 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.property;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import org.apache.commons.lang.math.RandomUtils;
+import org.assertj.core.api.AbstractAssert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class InternalComponentPropertiesDaoTest {
+
+ private static final String SOME_KEY = "key1";
+ private static final String SOME_COMPONENT = "component1";
+ private static final String SOME_VALUE = "value";
+
+ private System2 system2 = mock(System2.class);
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public DbTester dbTester = DbTester.create(system2);
+ private DbSession dbSession = dbTester.getSession();
+ private UuidFactory uuidFactory = UuidFactoryFast.getInstance();
+ private InternalComponentPropertiesDao underTest = new InternalComponentPropertiesDao(system2, uuidFactory);
+
+ @Test
+ public void insertOrUpdate_insert_property_if_it_doesnt_already_exist() {
+ InternalComponentPropertyDto dto = new InternalComponentPropertyDto()
+ .setKey(SOME_KEY)
+ .setComponentUuid(SOME_COMPONENT)
+ .setValue(SOME_VALUE);
+
+ long now = RandomUtils.nextLong();
+ when(system2.now()).thenReturn(now);
+
+ underTest.insertOrUpdate(dbSession, dto);
+
+ assertThatInternalProperty(dto.getUuid())
+ .hasComponentUuid(SOME_COMPONENT)
+ .hasKey(SOME_KEY)
+ .hasValue(SOME_VALUE)
+ .hasUpdatedAt(now)
+ .hasCreatedAt(now);
+ }
+
+ @Test
+ public void insertOrUpdate_update_property_if_it_already_exists() {
+ long creationDate = 10L;
+ when(system2.now()).thenReturn(creationDate);
+
+ InternalComponentPropertyDto dto = saveDto();
+
+ long updateDate = 20L;
+ when(system2.now()).thenReturn(updateDate);
+
+ dto.setValue("other value");
+
+ underTest.insertOrUpdate(dbSession, dto);
+
+ assertThatInternalProperty(dto.getUuid())
+ .hasUpdatedAt(updateDate)
+ .hasValue("other value")
+ .hasCreatedAt(creationDate);
+ }
+
+ @Test
+ public void select_by_component_uuid_and_key_returns_property() {
+ saveDto();
+
+ Optional<InternalComponentPropertyDto> result = underTest.selectByComponentUuidAndKey(dbSession, SOME_COMPONENT, SOME_KEY);
+ assertThat(result.get())
+ .extracting("componentUuid", "key", "value")
+ .contains(SOME_COMPONENT, SOME_KEY, SOME_VALUE);
+ }
+
+ @Test
+ public void select_by_component_uuid_and_key_returns_empty_when_it_doesnt_exist() {
+ saveDto();
+
+ assertThat(underTest.selectByComponentUuidAndKey(dbSession, "other_component", SOME_KEY)).isEmpty();
+ assertThat(underTest.selectByComponentUuidAndKey(dbSession, SOME_COMPONENT, "other_key")).isEmpty();
+ }
+
+ @Test
+ public void delete_by_component_uuid_and_key_deletes_property() {
+ saveDto();
+
+ assertThat(underTest.deleteByComponentUuidAndKey(dbSession, SOME_COMPONENT, SOME_KEY)).isEqualTo(1);
+ assertThat(underTest.selectByComponentUuidAndKey(dbSession, SOME_COMPONENT, SOME_KEY)).isEmpty();
+ }
+
+ @Test
+ public void delete_by_component_uuid_and_key_does_nothing_if_property_doesnt_exist() {
+ saveDto();
+
+ assertThat(underTest.deleteByComponentUuidAndKey(dbSession, SOME_COMPONENT, "other_key")).isEqualTo(0);
+ assertThat(underTest.deleteByComponentUuidAndKey(dbSession, "other_component", SOME_KEY)).isEqualTo(0);
+ assertThat(underTest.selectByComponentUuidAndKey(dbSession, SOME_COMPONENT, SOME_KEY)).isNotEmpty();
+ }
+
+ private InternalComponentPropertyDto saveDto() {
+ InternalComponentPropertyDto dto = new InternalComponentPropertyDto()
+ .setKey(SOME_KEY)
+ .setComponentUuid(SOME_COMPONENT)
+ .setValue(SOME_VALUE);
+
+ underTest.insertOrUpdate(dbSession, dto);
+ return dto;
+ }
+
+ private InternalComponentPropertyAssert assertThatInternalProperty(String uuid) {
+ return new InternalComponentPropertiesDaoTest.InternalComponentPropertyAssert(dbTester, dbSession, uuid);
+ }
+
+ private static class InternalComponentPropertyAssert extends AbstractAssert<InternalComponentPropertiesDaoTest.InternalComponentPropertyAssert, InternalComponentPropertyDto> {
+
+ public InternalComponentPropertyAssert(DbTester dbTester, DbSession dbSession, String uuid) {
+ super(asInternalProperty(dbTester, dbSession, uuid), InternalComponentPropertyAssert.class);
+ }
+
+ private static InternalComponentPropertyDto asInternalProperty(DbTester dbTester, DbSession dbSession, String uuid) {
+ Map<String, Object> row = dbTester.selectFirst(
+ dbSession,
+ "select" +
+ " uuid as \"uuid\", component_uuid as \"componentUuid\", kee as \"key\", value as \"value\", updated_at as \"updatedAt\", created_at as \"createdAt\"" +
+ " from internal_component_props" +
+ " where uuid='" + uuid+ "'");
+ return new InternalComponentPropertyDto()
+ .setUuid((String) row.get("uuid"))
+ .setComponentUuid((String) row.get("componentUuid"))
+ .setKey((String) row.get("key"))
+ .setValue((String) row.get("value"))
+ .setUpdatedAt((Long) row.get("updatedAt"))
+ .setCreatedAt((Long) row.get("createdAt"));
+ }
+
+ public void doesNotExist() {
+ isNull();
+ }
+
+ public InternalComponentPropertiesDaoTest.InternalComponentPropertyAssert hasKey(String expected) {
+ isNotNull();
+
+ if (!Objects.equals(actual.getKey(), expected)) {
+ failWithMessage("Expected Internal property to have column KEY to be <%s> but was <%s>", true, actual.getKey());
+ }
+
+ return this;
+ }
+
+ public InternalComponentPropertiesDaoTest.InternalComponentPropertyAssert hasComponentUuid(String expected) {
+ isNotNull();
+
+ if (!Objects.equals(actual.getComponentUuid(), expected)) {
+ failWithMessage("Expected Internal property to have column COMPONENT_UUID to be <%s> but was <%s>", true, actual.getComponentUuid());
+ }
+
+ return this;
+ }
+
+ public InternalComponentPropertiesDaoTest.InternalComponentPropertyAssert hasValue(String expected) {
+ isNotNull();
+
+ if (!Objects.equals(actual.getValue(), expected)) {
+ failWithMessage("Expected Internal property to have column VALUE to be <%s> but was <%s>", true, actual.getValue());
+ }
+
+ return this;
+ }
+
+ public InternalComponentPropertiesDaoTest.InternalComponentPropertyAssert hasCreatedAt(long expected) {
+ isNotNull();
+
+ if (!Objects.equals(actual.getCreatedAt(), expected)) {
+ failWithMessage("Expected Internal property to have column CREATED_AT to be <%s> but was <%s>", expected, actual.getCreatedAt());
+ }
+
+ return this;
+ }
+
+ public InternalComponentPropertiesDaoTest.InternalComponentPropertyAssert hasUpdatedAt(long expected) {
+ isNotNull();
+
+ if (!Objects.equals(actual.getUpdatedAt(), expected)) {
+ failWithMessage("Expected Internal property to have column UPDATED_AT to be <%s> but was <%s>", expected, actual.getUpdatedAt());
+ }
+
+ return this;
+ }
+
+ }
+
+}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/property/InternalComponentPropertyDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/property/InternalComponentPropertyDtoTest.java
new file mode 100644
index 00000000000..5188fdab7ce
--- /dev/null
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/property/InternalComponentPropertyDtoTest.java
@@ -0,0 +1,104 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.property;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import org.apache.commons.lang.StringUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@RunWith(DataProviderRunner.class)
+public class InternalComponentPropertyDtoTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void setter_and_getter() {
+ InternalComponentPropertyDto underTest = new InternalComponentPropertyDto()
+ .setComponentUuid("component1")
+ .setKey("key1")
+ .setValue("value1")
+ .setUuid("uuid1")
+ .setCreatedAt(10L)
+ .setUpdatedAt(15L);
+
+ assertThat(underTest.getComponentUuid()).isEqualTo("component1");
+ assertThat(underTest.getKey()).isEqualTo("key1");
+ assertThat(underTest.getValue()).isEqualTo("value1");
+ assertThat(underTest.getUuid()).isEqualTo("uuid1");
+ assertThat(underTest.getCreatedAt()).isEqualTo(10L);
+ assertThat(underTest.getUpdatedAt()).isEqualTo(15L);
+ }
+
+ @Test
+ @DataProvider({"null", ""})
+ public void setKey_throws_IAE_if_key_is_null_or_empty(String key) {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("key can't be null nor empty");
+
+ new InternalComponentPropertyDto().setKey(key);
+ }
+
+ @Test
+ public void setKey_throws_IAE_if_key_is_too_long() {
+ String veryLongKey = StringUtils.repeat("a", 513);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage(String.format("key length (513) is longer than the maximum authorized (512). '%s' was provided", veryLongKey));
+
+ new InternalComponentPropertyDto().setKey(veryLongKey);
+ }
+
+ @Test
+ public void setValue_throws_IAE_if_value_is_too_long() {
+ String veryLongValue = StringUtils.repeat("a", 4001);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage(String.format("value length (4001) is longer than the maximum authorized (4000). '%s' was provided", veryLongValue));
+
+ new InternalComponentPropertyDto().setValue(veryLongValue);
+ }
+
+ @Test
+ public void setValue_accept_null_value() {
+ InternalComponentPropertyDto underTest = new InternalComponentPropertyDto().setValue(null);
+
+ assertThat(underTest.getValue()).isNull();
+ }
+
+ @Test
+ public void test_toString() {
+ InternalComponentPropertyDto underTest = new InternalComponentPropertyDto()
+ .setUuid("uuid1")
+ .setComponentUuid("component1")
+ .setKey("key1")
+ .setValue("value1")
+ .setCreatedAt(10L)
+ .setUpdatedAt(15L);
+
+ assertThat(underTest.toString()).isEqualTo("InternalComponentPropertyDto{uuid=uuid1, key=key1, value=value1, componentUuid=component1, updatedAt=15, createdAt=10}");
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v78/CreateInternalComponentPropertiesTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v78/CreateInternalComponentPropertiesTable.java
new file mode 100644
index 00000000000..872ede4d5f0
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v78/CreateInternalComponentPropertiesTable.java
@@ -0,0 +1,106 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v78;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
+import org.sonar.server.platform.db.migration.SupportsBlueGreen;
+import org.sonar.server.platform.db.migration.def.BigIntegerColumnDef;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.sql.CreateTableBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+@SupportsBlueGreen
+public class CreateInternalComponentPropertiesTable extends DdlChange {
+
+ private static final String TABLE_NAME = "internal_component_props";
+
+ private static final VarcharColumnDef UUID_COLUMN = newVarcharColumnDefBuilder()
+ .setColumnName("uuid")
+ .setIsNullable(false)
+ .setLimit(VarcharColumnDef.UUID_SIZE)
+ .build();
+
+ private static final VarcharColumnDef COMPONENT_UUID_COLUMN = newVarcharColumnDefBuilder()
+ .setColumnName("component_uuid")
+ .setIsNullable(false)
+ .setLimit(50)
+ .build();
+
+ private static final VarcharColumnDef KEE_COLUMN = newVarcharColumnDefBuilder()
+ .setColumnName("kee")
+ .setIsNullable(false)
+ .setLimit(512)
+ .build();
+
+ private static final VarcharColumnDef VALUE_COLUMN = newVarcharColumnDefBuilder()
+ .setColumnName("value")
+ .setIsNullable(true)
+ .setLimit(4000)
+ .build();
+
+ private static final BigIntegerColumnDef UPDATED_AT_COLUMN = newBigIntegerColumnDefBuilder()
+ .setColumnName("updated_at")
+ .setIsNullable(false)
+ .build();
+
+ private static final BigIntegerColumnDef CREATED_AT_COLUMN = newBigIntegerColumnDefBuilder()
+ .setColumnName("created_at")
+ .setIsNullable(false)
+ .build();
+
+ public CreateInternalComponentPropertiesTable(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ if (!tableExists()) {
+ context.execute(new CreateTableBuilder(getDialect(), TABLE_NAME)
+ .addPkColumn(UUID_COLUMN)
+ .addColumn(COMPONENT_UUID_COLUMN)
+ .addColumn(KEE_COLUMN)
+ .addColumn(VALUE_COLUMN)
+ .addColumn(UPDATED_AT_COLUMN)
+ .addColumn(CREATED_AT_COLUMN)
+ .build());
+
+ context.execute(new CreateIndexBuilder(getDialect())
+ .addColumn(COMPONENT_UUID_COLUMN)
+ .addColumn(KEE_COLUMN)
+ .setUnique(true)
+ .setTable(TABLE_NAME)
+ .setName("unique_component_uuid_kee")
+ .build());
+ }
+ }
+
+ private boolean tableExists() throws SQLException {
+ try (Connection connection = getDatabase().getDataSource().getConnection()) {
+ return DatabaseUtils.tableExists(TABLE_NAME, connection);
+ }
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v78/DbVersion78.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v78/DbVersion78.java
index 758f1ea69cd..9adeaa827d6 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v78/DbVersion78.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v78/DbVersion78.java
@@ -30,6 +30,7 @@ public class DbVersion78 implements DbVersion {
.add(2700, "Drop overall subscriptions on notifications about new and resolved issues", DeleteOverallSubscriptionsOnNewAndResolvedIssuesNotifications.class)
.add(2701, "Add index to org_qprofile.parent_uuid", AddIndexToOrgQProfileParentUuid.class)
.add(2702, "Add column webhooks.secret", AddWebhooksSecret.class)
- .add(2703, "Add security fields to Elasticsearch indices", AddSecurityFieldsToElasticsearchIndices.class);
+ .add(2703, "Add security fields to Elasticsearch indices", AddSecurityFieldsToElasticsearchIndices.class)
+ .add(2704, "Add InternalComponentProperties table", CreateInternalComponentPropertiesTable.class);
}
}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v78/CreateInternalComponentPropertiesTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v78/CreateInternalComponentPropertiesTableTest.java
new file mode 100644
index 00000000000..d42fbb28d19
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v78/CreateInternalComponentPropertiesTableTest.java
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v78;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+public class CreateInternalComponentPropertiesTableTest {
+
+ private static final String TABLE = "internal_component_props";
+
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createEmpty();
+
+ private CreateInternalComponentPropertiesTable underTest = new CreateInternalComponentPropertiesTable(db.database());
+
+ @Test
+ public void creates_table() throws SQLException {
+ underTest.execute();
+
+ checkTable();
+ }
+
+ @Test
+ public void migration_is_reentrant() throws SQLException {
+ underTest.execute();
+ underTest.execute();
+
+ checkTable();
+ }
+
+ private void checkTable() {
+ db.assertColumnDefinition(TABLE, "uuid", Types.VARCHAR, 40, false);
+ db.assertColumnDefinition(TABLE, "component_uuid", Types.VARCHAR, 50, false);
+ db.assertColumnDefinition(TABLE, "kee", Types.VARCHAR, 512, false);
+ db.assertColumnDefinition(TABLE, "value", Types.VARCHAR, 4000, true);
+ db.assertColumnDefinition(TABLE, "updated_at", Types.BIGINT, null, false);
+ db.assertColumnDefinition(TABLE, "created_at", Types.BIGINT, null, false);
+
+ db.assertUniqueIndex(TABLE, "unique_component_uuid_kee", "component_uuid", "kee");
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v78/DbVersion78Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v78/DbVersion78Test.java
index 0803ca29179..5d44948e8a1 100644
--- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v78/DbVersion78Test.java
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v78/DbVersion78Test.java
@@ -35,7 +35,7 @@ public class DbVersion78Test {
@Test
public void verify_migration_count() {
- verifyMigrationCount(underTest, 4);
+ verifyMigrationCount(underTest, 5);
}
}