@@ -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 |
@@ -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), |
@@ -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, |
@@ -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; | |||
} | |||
} |
@@ -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, |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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> |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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}"); | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -35,7 +35,7 @@ public class DbVersion78Test { | |||
@Test | |||
public void verify_migration_count() { | |||
verifyMigrationCount(underTest, 4); | |||
verifyMigrationCount(underTest, 5); | |||
} | |||
} |