@@ -26,6 +26,7 @@ public class AuditDto { | |||
private String uuid; | |||
private String userUuid; | |||
private String userLogin; | |||
private boolean userTriggered; | |||
private String category; | |||
private String operation; | |||
private String newValue; | |||
@@ -55,6 +56,14 @@ public class AuditDto { | |||
this.userLogin = userLogin; | |||
} | |||
public boolean isUserTriggered() { | |||
return userTriggered; | |||
} | |||
public void setUserTriggered(boolean userTriggered) { | |||
this.userTriggered = userTriggered; | |||
} | |||
public String getCategory() { | |||
return category; | |||
} |
@@ -7,6 +7,7 @@ | |||
a.uuid as "uuid", | |||
a.user_uuid as "userUuid", | |||
a.user_login as "userLogin", | |||
a.user_triggered as "userTriggered", | |||
a.category as "category", | |||
a.operation as "operation", | |||
a.new_value as "newValue", | |||
@@ -17,6 +18,7 @@ | |||
uuid, | |||
user_uuid, | |||
user_login, | |||
user_triggered, | |||
category, | |||
operation, | |||
new_value, | |||
@@ -65,6 +67,7 @@ | |||
uuid, | |||
user_uuid, | |||
user_login, | |||
user_triggered, | |||
category, | |||
operation, | |||
new_value, | |||
@@ -74,6 +77,7 @@ | |||
#{dto.uuid, jdbcType=VARCHAR}, | |||
#{dto.userUuid, jdbcType=VARCHAR}, | |||
#{dto.userLogin, jdbcType=VARCHAR}, | |||
#{dto.userTriggered, jdbcType=BOOLEAN}, | |||
#{dto.category, jdbcType=VARCHAR}, | |||
#{dto.operation, jdbcType=VARCHAR}, | |||
#{dto.newValue, jdbcType=VARCHAR}, |
@@ -107,7 +107,8 @@ CREATE TABLE "AUDITS"( | |||
"CATEGORY" CHARACTER VARYING(25) NOT NULL, | |||
"OPERATION" CHARACTER VARYING(50) NOT NULL, | |||
"NEW_VALUE" CHARACTER VARYING(4000), | |||
"CREATED_AT" BIGINT NOT NULL | |||
"CREATED_AT" BIGINT NOT NULL, | |||
"USER_TRIGGERED" BOOLEAN DEFAULT TRUE NOT NULL | |||
); | |||
ALTER TABLE "AUDITS" ADD CONSTRAINT "PK_AUDITS" PRIMARY KEY("UUID"); | |||
CREATE INDEX "AUDITS_CREATED_AT" ON "AUDITS"("CREATED_AT" NULLS FIRST); |
@@ -0,0 +1,48 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2022 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.v95; | |||
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.def.BooleanColumnDef; | |||
import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder; | |||
import org.sonar.server.platform.db.migration.step.DdlChange; | |||
public class AddUserTriggeredColumnToAudits extends DdlChange { | |||
private static final String TABLE_NAME = "audits"; | |||
private static final String COLUMN_NAME = "user_triggered"; | |||
public AddUserTriggeredColumnToAudits(Database db) { | |||
super(db); | |||
} | |||
@Override | |||
public void execute(Context context) throws SQLException { | |||
try (Connection c = getDatabase().getDataSource().getConnection()) { | |||
if (!DatabaseUtils.tableColumnExists(c, TABLE_NAME, COLUMN_NAME)) { | |||
context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME) | |||
.addColumn(BooleanColumnDef.newBooleanColumnDefBuilder().setColumnName(COLUMN_NAME).setIsNullable(false).setDefaultValue(true).build()) | |||
.build()); | |||
} | |||
} | |||
} | |||
} |
@@ -47,6 +47,9 @@ public class DbVersion95 implements DbVersion { | |||
.add(6415, "Migrate hotspot rule descriptions", MigrateHotspotRuleDescriptions.class) | |||
.add(6416, "Remove onboarded column from User table", DropOnboardedColumnFromUserTable.class) | |||
.add(6417, "Add column 'user_triggered' to 'audits'", AddUserTriggeredColumnToAudits.class) | |||
.add(6418, "Upsert value of 'user_triggered' in 'audits'", UpsertAuditsUserTriggeredValue.class) | |||
; | |||
} | |||
} |
@@ -0,0 +1,41 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2022 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.v95; | |||
import java.sql.SQLException; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.step.DataChange; | |||
import org.sonar.server.platform.db.migration.step.Upsert; | |||
public class UpsertAuditsUserTriggeredValue extends DataChange { | |||
public UpsertAuditsUserTriggeredValue(Database db) { | |||
super(db); | |||
} | |||
@Override | |||
protected void execute(Context context) throws SQLException { | |||
Upsert upsert = context.prepareUpsert("update audits set user_triggered = ? where user_uuid = '-'"); | |||
upsert | |||
.setBoolean(1, false); | |||
upsert.execute(); | |||
upsert.commit(); | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2022 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.v95; | |||
import java.sql.SQLException; | |||
import java.sql.Types; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.db.CoreDbTester; | |||
public class AddUserTriggeredColumnToAuditsTest { | |||
private static final String TABLE_NAME = "audits"; | |||
private static final String COLUMN_NAME = "user_triggered"; | |||
@Rule | |||
public final CoreDbTester db = CoreDbTester.createForSchema(AddUserTriggeredColumnToAuditsTest.class, "schema.sql"); | |||
private final AddUserTriggeredColumnToAudits underTest = new AddUserTriggeredColumnToAudits(db.database()); | |||
@Test | |||
public void migration_should_add_column() throws SQLException { | |||
db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); | |||
underTest.execute(); | |||
db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BOOLEAN, null, false); | |||
} | |||
@Test | |||
public void migration_should_be_reentrant() throws SQLException { | |||
db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); | |||
underTest.execute(); | |||
underTest.execute(); | |||
db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BOOLEAN, null, false); | |||
} | |||
} |
@@ -0,0 +1,114 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2022 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.v95; | |||
import java.sql.SQLException; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.stream.Collectors; | |||
import org.jetbrains.annotations.NotNull; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.core.util.UuidFactory; | |||
import org.sonar.core.util.UuidFactoryFast; | |||
import org.sonar.db.CoreDbTester; | |||
import org.sonar.server.platform.db.migration.step.DataChange; | |||
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class UpsertAuditsUserTriggeredValueTest { | |||
private final UuidFactory uuidFactory = UuidFactoryFast.getInstance(); | |||
@Rule | |||
public CoreDbTester db = CoreDbTester.createForSchema(UpsertAuditsUserTriggeredValueTest.class, "schema.sql"); | |||
private final DataChange underTest = new UpsertAuditsUserTriggeredValue(db.database()); | |||
@Test | |||
public void migration_populates_audits_user_triggered() throws SQLException { | |||
String auditUuid1 = insertUserTriggeredLog(); | |||
String auditUuid2 = insertSystemTriggeredLog(); | |||
underTest.execute(); | |||
assertUserTriggeredIsTrue(auditUuid1); | |||
assertUserTriggeredIsFalse(auditUuid2); | |||
} | |||
@Test | |||
public void migration_should_be_reentrant() throws SQLException { | |||
String auditUuid1 = insertUserTriggeredLog(); | |||
String auditUuid2 = insertSystemTriggeredLog(); | |||
underTest.execute(); | |||
// re-entrant | |||
underTest.execute(); | |||
assertUserTriggeredIsTrue(auditUuid1); | |||
assertUserTriggeredIsFalse(auditUuid2); | |||
} | |||
private void assertUserTriggeredIsTrue(String userUuid) { | |||
assertUserTriggered(userUuid,true); | |||
} | |||
private void assertUserTriggeredIsFalse(String userUuid) { | |||
assertUserTriggered(userUuid,false); | |||
} | |||
private void assertUserTriggered(String userUuid, boolean userTriggered) { | |||
String selectSql = String.format("select USER_TRIGGERED from audits where uuid='%s'", userUuid); | |||
assertThat(db.select(selectSql).stream().map(row -> row.get("USER_TRIGGERED")).collect(Collectors.toList())) | |||
.containsExactlyInAnyOrder(userTriggered); | |||
} | |||
private String insertUserTriggeredLog() { | |||
Map<String, Object> map = getAuditsValueMap(uuidFactory.create(), randomAlphabetic(20)); | |||
return insertAuditsValueMap(map); | |||
} | |||
private String insertSystemTriggeredLog() { | |||
Map<String, Object> map = getAuditsValueMap("-", "System"); | |||
return insertAuditsValueMap(map); | |||
} | |||
@NotNull | |||
private Map<String, Object> getAuditsValueMap(String userUuid, String userLogin) { | |||
Map<String, Object> map = new HashMap<>(); | |||
String uuid = uuidFactory.create(); | |||
map.put("UUID", uuid); | |||
map.put("USER_UUID", userUuid); | |||
map.put("USER_LOGIN", userLogin); | |||
map.put("CATEGORY", "USER"); | |||
map.put("OPERATION", "UPDATE"); | |||
map.put("CREATED_AT", System.currentTimeMillis()); | |||
map.put("NEW_VALUE", "Some Value"); | |||
map.put("USER_TRIGGERED", true); | |||
return map; | |||
} | |||
private String insertAuditsValueMap(Map<String, Object> map) { | |||
db.executeInsert("audits", map); | |||
return (String) map.get("UUID"); | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
CREATE TABLE "AUDITS"( | |||
"UUID" CHARACTER VARYING(40) NOT NULL, | |||
"USER_UUID" CHARACTER VARYING(40) NOT NULL, | |||
"USER_LOGIN" CHARACTER VARYING(255) NOT NULL, | |||
"CATEGORY" CHARACTER VARYING(25) NOT NULL, | |||
"OPERATION" CHARACTER VARYING(50) NOT NULL, | |||
"NEW_VALUE" CHARACTER VARYING(4000), | |||
"CREATED_AT" BIGINT NOT NULL | |||
); | |||
ALTER TABLE "AUDITS" ADD CONSTRAINT "PK_AUDITS" PRIMARY KEY("UUID"); | |||
CREATE INDEX "AUDITS_CREATED_AT" ON "AUDITS"("CREATED_AT" NULLS FIRST); |
@@ -0,0 +1,12 @@ | |||
CREATE TABLE "AUDITS"( | |||
"UUID" CHARACTER VARYING(40) NOT NULL, | |||
"USER_UUID" CHARACTER VARYING(40) NOT NULL, | |||
"USER_LOGIN" CHARACTER VARYING(255) NOT NULL, | |||
"CATEGORY" CHARACTER VARYING(25) NOT NULL, | |||
"OPERATION" CHARACTER VARYING(50) NOT NULL, | |||
"NEW_VALUE" CHARACTER VARYING(4000), | |||
"CREATED_AT" BIGINT NOT NULL, | |||
"USER_TRIGGERED" BOOLEAN DEFAULT TRUE NOT NULL | |||
); | |||
ALTER TABLE "AUDITS" ADD CONSTRAINT "PK_AUDITS" PRIMARY KEY("UUID"); | |||
CREATE INDEX "AUDITS_CREATED_AT" ON "AUDITS"("CREATED_AT" NULLS FIRST); |