From: BenoƮt Gianinetti Date: Mon, 6 May 2019 12:15:46 +0000 (+0200) Subject: SONAR-12061 Create 'INTERNAL_COMPONENT_PROPS' table X-Git-Tag: 7.8~229 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=5e41f4d0ce6ffac895a9dd5126e2066f261ab11e;p=sonarqube.git SONAR-12061 Create 'INTERNAL_COMPONENT_PROPS' table --- 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 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 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 @@ + + + + + + + + + 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} + ) + + + + update internal_component_props + + value = #{dto.value, jdbcType=VARCHAR}, + updated_at = #{dto.updatedAt, jdbcType=BIGINT} + + + component_uuid = #{dto.componentUuid, jdbcType=VARCHAR} + AND kee = #{dto.key} + + + + + DELETE FROM internal_component_props + + component_uuid = #{componentUuid, jdbcType=VARCHAR} + AND kee = #{key,jdbcType=VARCHAR} + + + + 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 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 { + + 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 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); } }