--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.ce.task.projectanalysis.taskprocessor;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Arrays;
+import java.util.Optional;
+import org.sonar.api.utils.System2;
+import org.sonar.core.config.Frequency;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.property.PropertyDto;
+
+import static org.sonar.core.config.PurgeConstants.AUDIT_HOUSEKEEPING_FREQUENCY;
+import static org.sonar.core.config.PurgeProperties.DEFAULT_FREQUENCY;
+
+public class AuditHousekeepingFrequencyHelper {
+ private final System2 system2;
+
+ public AuditHousekeepingFrequencyHelper(System2 system2) {
+ this.system2 = system2;
+ }
+
+ public PropertyDto getHouseKeepingFrequency(DbClient dbClient, DbSession dbSession) {
+ return Optional.ofNullable(dbClient.propertiesDao()
+ .selectGlobalProperty(dbSession, AUDIT_HOUSEKEEPING_FREQUENCY))
+ .orElse(defaultAuditHouseKeepingProperty());
+ }
+
+ public long getThresholdDate(String frequency) {
+ Optional<Frequency> housekeepingFrequency = Arrays.stream(Frequency.values())
+ .filter(f -> f.name().equalsIgnoreCase(frequency)).findFirst();
+ if (housekeepingFrequency.isEmpty()) {
+ throw new IllegalArgumentException("Unsupported frequency: " + frequency);
+ }
+
+ return Instant.ofEpochMilli(system2.now())
+ .minus(housekeepingFrequency.get().getDays(), ChronoUnit.DAYS)
+ .toEpochMilli();
+ }
+
+ private static PropertyDto defaultAuditHouseKeepingProperty() {
+ PropertyDto property = new PropertyDto();
+ property.setKey(AUDIT_HOUSEKEEPING_FREQUENCY);
+ property.setValue(DEFAULT_FREQUENCY);
+ return property;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.ce.task.projectanalysis.taskprocessor;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.ce.task.step.ComputationStep;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.audit.AuditDto;
+import org.sonar.db.property.PropertyDto;
+
+public final class AuditPurgeStep implements ComputationStep {
+ private static final Logger LOG = Loggers.get(AuditPurgeStep.class);
+
+ private final AuditHousekeepingFrequencyHelper auditHousekeepingFrequencyHelper;
+ private final DbClient dbClient;
+
+ public AuditPurgeStep(AuditHousekeepingFrequencyHelper auditHousekeepingFrequencyHelper, DbClient dbClient) {
+ this.auditHousekeepingFrequencyHelper = auditHousekeepingFrequencyHelper;
+ this.dbClient = dbClient;
+ }
+
+ @Override
+ public void execute(Context context) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ PropertyDto property = auditHousekeepingFrequencyHelper.getHouseKeepingFrequency(dbClient, dbSession);
+ long deleteBefore = auditHousekeepingFrequencyHelper.getThresholdDate(property.getValue());
+ Set<String> auditUuids = dbClient.auditDao()
+ .selectOlderThan(dbSession, deleteBefore)
+ .stream()
+ .map(AuditDto::getUuid)
+ .collect(Collectors.toSet());
+ LOG.info(String.format("%s audit logs to be deleted...", auditUuids.size()));
+ dbClient.auditDao().deleteByUuids(dbSession, auditUuids);
+ dbSession.commit();
+ }
+ }
+
+ @Override
+ public String getDescription() {
+ return "Purge Audit Logs";
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.ce.task.projectanalysis.taskprocessor;
+
+import org.sonar.core.platform.Module;
+
+public class AuditPurgeTaskModule extends Module {
+
+ @Override
+ protected void configureModule() {
+ add(AuditPurgeTaskProcessor.class);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.ce.task.projectanalysis.taskprocessor;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import org.sonar.ce.task.CeTask;
+import org.sonar.ce.task.CeTaskResult;
+import org.sonar.ce.task.container.TaskContainer;
+import org.sonar.ce.task.container.TaskContainerImpl;
+import org.sonar.ce.task.projectanalysis.step.AbstractComputationSteps;
+import org.sonar.ce.task.step.ComputationStep;
+import org.sonar.ce.task.step.ComputationStepExecutor;
+import org.sonar.ce.task.taskprocessor.CeTaskProcessor;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.core.platform.ContainerPopulator;
+
+import static org.sonar.db.ce.CeTaskTypes.AUDIT_PURGE;
+
+public class AuditPurgeTaskProcessor implements CeTaskProcessor {
+ private static final Set<String> HANDLED_TYPES = Set.of(AUDIT_PURGE);
+
+ private final ComponentContainer ceEngineContainer;
+
+ public AuditPurgeTaskProcessor(ComponentContainer ceEngineContainer) {
+ this.ceEngineContainer = ceEngineContainer;
+ }
+
+ @Override
+ public Set<String> getHandledCeTaskTypes() {
+ return HANDLED_TYPES;
+ }
+
+ @CheckForNull
+ @Override
+ public CeTaskResult process(CeTask task) {
+ try (TaskContainer container = new TaskContainerImpl(ceEngineContainer, newContainerPopulator(task))) {
+ container.bootup();
+ container.getComponentByType(ComputationStepExecutor.class).execute();
+ }
+ return null;
+ }
+
+ static ContainerPopulator<TaskContainer> newContainerPopulator(CeTask task) {
+ return taskContainer -> {
+ taskContainer.add(task);
+ taskContainer.add(AuditHousekeepingFrequencyHelper.class);
+ taskContainer.add(AuditPurgeStep.class);
+ taskContainer.add(new AuditPurgeComputationSteps(taskContainer));
+ taskContainer.add(ComputationStepExecutor.class);
+ };
+ }
+
+ public static final class AuditPurgeComputationSteps extends AbstractComputationSteps {
+
+ public AuditPurgeComputationSteps(ContainerPopulator.Container container) {
+ super(container);
+ }
+
+ @Override
+ public List<Class<? extends ComputationStep>> orderedStepClasses() {
+ return Arrays.asList(AuditPurgeStep.class);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.ce.task.projectanalysis.taskprocessor;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.sonar.api.impl.utils.TestSystem2;
+import org.sonar.api.utils.System2;
+import org.sonar.core.config.Frequency;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.property.PropertiesDao;
+import org.sonar.db.property.PropertyDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.config.PurgeConstants.AUDIT_HOUSEKEEPING_FREQUENCY;
+import static org.sonar.core.config.PurgeProperties.DEFAULT_FREQUENCY;
+
+@RunWith(DataProviderRunner.class)
+public class AuditHousekeepingFrequencyHelperTest {
+ private static final long NOW = 10_000_000_000L;
+
+ private final DbClient dbClient = mock(DbClient.class);
+ private final DbSession dbSession = mock(DbSession.class);
+ private final PropertiesDao propertiesDao = mock(PropertiesDao.class);
+ private final System2 system2 = new TestSystem2().setNow(NOW);
+ private final AuditHousekeepingFrequencyHelper underTest = new AuditHousekeepingFrequencyHelper(system2);
+
+ @Test
+ @UseDataProvider("frequencyOptions")
+ public void getThresholdDate(Frequency frequency) {
+ long result = underTest.getThresholdDate(frequency.getDescription());
+
+
+ long expected = Instant.ofEpochMilli(system2.now())
+ .minus(frequency.getDays(), ChronoUnit.DAYS)
+ .toEpochMilli();
+
+ assertThat(result).isEqualTo(expected);
+ }
+
+ @Test
+ public void getThresholdDateForUnknownFrequencyFails() {
+ assertThatThrownBy(() -> underTest.getThresholdDate("Lalala"))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Unsupported frequency: Lalala");
+ }
+
+ @Test
+ public void getHouseKeepingFrequency() {
+ String value = "Weekly";
+ PropertyDto propertyDto = new PropertyDto().setKey(AUDIT_HOUSEKEEPING_FREQUENCY).setValue(value);
+ when(dbClient.propertiesDao()).thenReturn(propertiesDao);
+ when(propertiesDao
+ .selectGlobalProperty(dbSession, AUDIT_HOUSEKEEPING_FREQUENCY))
+ .thenReturn(propertyDto);
+ assertThat(underTest.getHouseKeepingFrequency(dbClient, dbSession).getValue()).isEqualTo(value);
+ }
+
+ @Test
+ public void getDefaultHouseKeepingFrequencyWhenNotSet() {
+ when(dbClient.propertiesDao()).thenReturn(propertiesDao);
+ when(propertiesDao
+ .selectGlobalProperty(dbSession, AUDIT_HOUSEKEEPING_FREQUENCY))
+ .thenReturn(null);
+ assertThat(underTest.getHouseKeepingFrequency(dbClient, dbSession).getValue())
+ .isEqualTo(DEFAULT_FREQUENCY);
+ }
+
+ @DataProvider
+ public static Object[][] frequencyOptions() {
+ return new Object[][] {
+ {Frequency.WEEKLY},
+ {Frequency.MONTHLY},
+ {Frequency.TRIMESTRIAL},
+ {Frequency.YEARLY}
+ };
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.ce.task.projectanalysis.taskprocessor;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.audit.AuditDto;
+import org.sonar.db.audit.AuditTesting;
+import org.sonar.db.property.PropertyDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.config.Frequency.MONTHLY;
+import static org.sonar.core.config.PurgeConstants.AUDIT_HOUSEKEEPING_FREQUENCY;
+
+public class AuditPurgeStepTest {
+ private final static long NOW = 1_400_000_000_000L;
+ private final static long BEFORE = 1_300_000_000_000L;
+ private final static long LATER = 1_500_000_000_000L;
+ private final static ZonedDateTime thresholdDate = Instant.ofEpochMilli(NOW)
+ .atZone(ZoneId.systemDefault());
+ private final static PropertyDto FREQUENCY_PROPERTY = new PropertyDto()
+ .setKey(AUDIT_HOUSEKEEPING_FREQUENCY)
+ .setValue(MONTHLY.name());
+
+ @Rule
+ public final DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+ private final DbClient dbClient = dbTester.getDbClient();
+
+ private final System2 system2 = new System2();
+
+ @Rule
+ public final DbTester db = DbTester.create(system2);
+
+ private final AuditHousekeepingFrequencyHelper auditHousekeepingFrequencyHelper = mock(AuditHousekeepingFrequencyHelper.class);
+
+ private final AuditPurgeStep underTest = new AuditPurgeStep(auditHousekeepingFrequencyHelper, dbClient);
+
+ @Before
+ public void setUp() {
+ when(auditHousekeepingFrequencyHelper.getHouseKeepingFrequency(any(), any())).thenReturn(FREQUENCY_PROPERTY);
+ when(auditHousekeepingFrequencyHelper.getThresholdDate(anyString())).thenReturn(NOW);
+ }
+
+ @Test
+ public void executeDeletesOlderAudits() {
+ prepareRowsWithDeterministicCreatedAt();
+ assertThat(dbClient.auditDao().selectOlderThan(db.getSession(), LATER + 1)).hasSize(3);
+
+ underTest.execute(() -> null);
+
+ assertThat(dbClient.auditDao().selectOlderThan(db.getSession(), LATER + 1)).hasSize(2);
+ }
+
+ @Test
+ public void getDescription() {
+ assertThat(underTest.getDescription()).isEqualTo("Purge Audit Logs");
+ }
+
+ private void prepareRowsWithDeterministicCreatedAt() {
+ insertAudit(BEFORE);
+ insertAudit(NOW);
+ insertAudit(LATER);
+ db.getSession().commit();
+ }
+
+ private void insertAudit(long timestamp) {
+ AuditDto auditDto = AuditTesting.newAuditDto(timestamp);
+ dbClient.auditDao().insert(db.getSession(), auditDto);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.ce.task.projectanalysis.taskprocessor;
+
+import org.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER;
+
+public class AuditPurgeTaskModuleTest {
+
+ @Test
+ public void verifyCountOfAddedComponents() {
+ ComponentContainer container = new ComponentContainer();
+ new AuditPurgeTaskModule().configure(container);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 1);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.ce.task.projectanalysis.taskprocessor;
+
+import java.util.List;
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.ce.task.CeTask;
+import org.sonar.ce.task.container.TaskContainer;
+import org.sonar.ce.task.step.ComputationStep;
+import org.sonar.core.platform.ComponentContainer;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.sonar.ce.task.projectanalysis.taskprocessor.AuditPurgeTaskProcessor.AuditPurgeComputationSteps;
+import static org.sonar.db.ce.CeTaskTypes.AUDIT_PURGE;
+
+public class AuditPurgeTaskProcessorTest {
+
+ private ComponentContainer ceEngineContainer = Mockito.mock(ComponentContainer.class);
+
+ private AuditPurgeTaskProcessor underTest = new AuditPurgeTaskProcessor(ceEngineContainer);
+ private TaskContainer container = Mockito.spy(TaskContainer.class);
+
+ @Test
+ public void getHandledCeTaskTypes() {
+ Assertions.assertThat(underTest.getHandledCeTaskTypes()).containsExactly(AUDIT_PURGE);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void processThrowsNPEIfCeTaskIsNull() {
+ underTest.process(null);
+ }
+
+ @Test
+ public void newContainerPopulator() {
+ CeTask task = new CeTask.Builder()
+ .setUuid("TASK_UUID")
+ .setType("Type")
+ .build();
+
+ AuditPurgeTaskProcessor.newContainerPopulator(task).populateContainer(container);
+ Mockito.verify(container, Mockito.times(5)).add(any());
+ }
+
+ @Test
+ public void orderedStepClasses(){
+ AuditPurgeComputationSteps auditPurgeComputationSteps = new AuditPurgeComputationSteps(null);
+
+ List<Class<? extends ComputationStep>> steps = auditPurgeComputationSteps.orderedStepClasses();
+
+ Assertions.assertThat(steps).containsExactly(AuditPurgeStep.class);
+ }
+
+}
import org.sonar.ce.task.projectanalysis.analysis.ProjectConfigurationFactory;
import org.sonar.ce.task.projectanalysis.issue.AdHocRuleCreator;
import org.sonar.ce.task.projectanalysis.notification.ReportAnalysisFailureNotificationModule;
+import org.sonar.ce.task.projectanalysis.taskprocessor.AuditPurgeTaskModule;
import org.sonar.ce.task.projectanalysis.taskprocessor.IssueSyncTaskModule;
import org.sonar.ce.taskprocessor.CeProcessingScheduler;
import org.sonar.ce.taskprocessor.CeTaskProcessorModule;
CeTaskCommonsModule.class,
ProjectAnalysisTaskModule.class,
IssueSyncTaskModule.class,
+ AuditPurgeTaskModule.class,
CeTaskProcessorModule.class,
OfficialDistribution.class,
package org.sonar.db.audit;
import java.util.List;
+import java.util.Set;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.Pagination;
+import static org.sonar.db.DatabaseUtils.executeLargeUpdates;
import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.MAX_SIZE;
public class AuditDao implements Dao {
getMapper(dbSession).insert(auditDto);
}
- public void deleteIfBeforeSelectedDate(DbSession dbSession, long timestamp) {
- getMapper(dbSession).deleteIfBeforeSelectedDate(timestamp);
+ public List<AuditDto> selectOlderThan(DbSession dbSession, long beforeTimestamp) {
+ return getMapper(dbSession).selectOlderThan(beforeTimestamp);
+ }
+
+ public void deleteByUuids(DbSession dbSession, Set<String> uuids) {
+ executeLargeUpdates(uuids, getMapper(dbSession)::deleteByUuids);
}
}
*/
package org.sonar.db.audit;
+import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.sonar.db.Pagination;
-import java.util.List;
-
public interface AuditMapper {
void insert(@Param("dto") AuditDto auditDto);
void delete(@Param("uuids") List<String> uuids);
- void deleteIfBeforeSelectedDate(@Param("timestamp") long timestamp);
-
List<AuditDto> selectByPeriodPaginated(@Param("start")long start, @Param("end") long end, @Param("pagination") Pagination pagination);
+ List<AuditDto> selectOlderThan(@Param("beforeTimestamp") long beforeTimestamp);
+
+ void deleteByUuids(@Param("uuids") List<String> uuids);
+
}
public final class CeTaskTypes {
- public static final String REPORT = "REPORT";
+ public static final String AUDIT_PURGE = "AUDIT_PURGE";
public static final String BRANCH_ISSUE_SYNC = "ISSUE_SYNC";
+ public static final String REPORT = "REPORT";
private CeTaskTypes() {
// only statics
)
</insert>
- <delete id="deleteIfBeforeSelectedDate">
- delete from audits
+ <select id="selectOlderThan" parameterType="long" resultType="org.sonar.db.audit.AuditDto">
+ select
+ <include refid="sqlColumns"/>
+ from audits a
where
- created_at <= #{timestamp,jdbcType=BIGINT}
+ a.created_at < #{beforeTimestamp,jdbcType=BIGINT}
+ </select>
+
+ <delete id="deleteByUuids" parameterType="string">
+ delete
+ from audits
+ where
+ uuid in
+ <foreach collection="uuids" open="(" close=")" item="uuid" separator=",">
+ #{uuid,jdbcType=VARCHAR}
+ </foreach>
</delete>
</mapper>
package org.sonar.db.audit;
import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.impl.utils.TestSystem2;
public void deleteIfBeforeSelectedDate_deleteTwoRows() {
prepareRowsWithDeterministicCreatedAt(3);
- testAuditDao.deleteIfBeforeSelectedDate(dbSession, 2);
+ Set<String> auditUuids = testAuditDao.selectOlderThan(dbSession, 3)
+ .stream()
+ .map(AuditDto::getUuid)
+ .collect(Collectors.toSet());
+
+ testAuditDao.deleteByUuids(dbSession, auditUuids);
List<AuditDto> auditDtos = testAuditDao.selectByPeriodPaginated(dbSession, 1, 4, 1);
assertThat(auditDtos.size()).isEqualTo(1);
import org.sonar.auth.ldap.LdapModule;
import org.sonar.auth.saml.SamlModule;
import org.sonar.ce.task.projectanalysis.notification.ReportAnalysisFailureNotificationModule;
+import org.sonar.ce.task.projectanalysis.taskprocessor.AuditPurgeTaskProcessor;
import org.sonar.ce.task.projectanalysis.taskprocessor.IssueSyncTaskProcessor;
import org.sonar.ce.task.projectanalysis.taskprocessor.ReportTaskProcessor;
import org.sonar.core.component.DefaultResourceTypes;
CeWsModule.class,
ReportTaskProcessor.class,
IssueSyncTaskProcessor.class,
+ AuditPurgeTaskProcessor.class,
// SonarSource editions
PlatformEditionProvider.class,
return this.description;
}
+ public int getDays() {
+ return this.days;
+ }
+
Frequency(String description, int days) {
this.description = description;
this.days = days;
background_task.type.APP_REFRESH=Recomputation
background_task.type.PROJECT_EXPORT=Project Export
background_task.type.PROJECT_IMPORT=Project Import
+background_task.type.AUDIT_PURGE=Audit Log Purge
background_tasks.page=Background Tasks
background_tasks.page.description=This page allows monitoring of the queue of tasks running asynchronously on the server. It also gives access to the history of finished tasks and their status. Analysis report processing is the most common kind of background task.