private String uuid;
private String userUuid;
private String userLogin;
+ private boolean userTriggered;
private String category;
private String operation;
private String newValue;
this.userLogin = userLogin;
}
+ public boolean isUserTriggered() {
+ return userTriggered;
+ }
+
+ public void setUserTriggered(boolean userTriggered) {
+ this.userTriggered = userTriggered;
+ }
+
public String getCategory() {
return category;
}
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",
uuid,
user_uuid,
user_login,
+ user_triggered,
category,
operation,
new_value,
uuid,
user_uuid,
user_login,
+ user_triggered,
category,
operation,
new_value,
#{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},
"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);
--- /dev/null
+/*
+ * 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());
+ }
+ }
+ }
+}
.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)
;
}
}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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");
+ }
+
+}
--- /dev/null
+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);
--- /dev/null
+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);