);
assertThat(picoContainer.getParent().getParent().getComponentAdapters()).hasSize(
CONTAINER_ITSELF
- + 11 // MigrationConfigurationModule
+ + 12 // MigrationConfigurationModule
+ 17 // level 2
);
assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize(
"active_rule_parameters",
"ce_activity",
"ce_queue",
+ "ce_task_characteristics",
"ce_task_input",
"ce_scanner_context",
"default_qprofiles",
"ERROR_MESSAGE" VARCHAR(1000),
"ERROR_STACKTRACE" CLOB(2147483647)
);
+
CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
CREATE INDEX "CE_ACTIVITY_COMPONENT_UUID" ON "CE_ACTIVITY" ("COMPONENT_UUID");
CREATE INDEX "CE_ACTIVITY_ISLASTKEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
CREATE INDEX "CE_ACTIVITY_ISLAST_STATUS" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
+CREATE TABLE "CE_TASK_CHARACTERISTICS" (
+ "UUID" VARCHAR(40) NOT NULL PRIMARY KEY,
+ "TASK_UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(50) NOT NULL,
+ "TEXT_VALUE" VARCHAR(4000)
+);
+CREATE INDEX "CE_TASK_CHARACTERISTICS_TASK_UUID" ON "CE_TASK_CHARACTERISTICS" ("TASK_UUID");
+
CREATE TABLE "CE_TASK_INPUT" (
"TASK_UUID" VARCHAR(40) NOT NULL PRIMARY KEY,
import org.sonar.db.ce.CeActivityDao;
import org.sonar.db.ce.CeQueueDao;
import org.sonar.db.ce.CeScannerContextDao;
+import org.sonar.db.ce.CeTaskCharacteristicDao;
import org.sonar.db.ce.CeTaskInputDao;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentKeyUpdaterDao;
CeActivityDao.class,
CeQueueDao.class,
CeScannerContextDao.class,
+ CeTaskCharacteristicDao.class,
CeTaskInputDao.class,
ComponentDao.class,
ComponentKeyUpdaterDao.class,
import org.sonar.db.ce.CeActivityDao;
import org.sonar.db.ce.CeQueueDao;
import org.sonar.db.ce.CeScannerContextDao;
+import org.sonar.db.ce.CeTaskCharacteristicDao;
import org.sonar.db.ce.CeTaskInputDao;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentKeyUpdaterDao;
private final CeActivityDao ceActivityDao;
private final CeQueueDao ceQueueDao;
private final CeTaskInputDao ceTaskInputDao;
+ private final CeTaskCharacteristicDao ceTaskCharacteristicsDao;
private final CeScannerContextDao ceScannerContextDao;
private final FileSourceDao fileSourceDao;
private final ComponentLinkDao componentLinkDao;
ceActivityDao = getDao(map, CeActivityDao.class);
ceQueueDao = getDao(map, CeQueueDao.class);
ceTaskInputDao = getDao(map, CeTaskInputDao.class);
+ ceTaskCharacteristicsDao = getDao(map, CeTaskCharacteristicDao.class);
ceScannerContextDao = getDao(map, CeScannerContextDao.class);
fileSourceDao = getDao(map, FileSourceDao.class);
componentLinkDao = getDao(map, ComponentLinkDao.class);
return ceTaskInputDao;
}
+ public CeTaskCharacteristicDao ceTaskCharacteristicsDao() {
+ return ceTaskCharacteristicsDao;
+ }
+
public CeScannerContextDao ceScannerContextDao() {
return ceScannerContextDao;
}
import org.sonar.db.ce.CeActivityMapper;
import org.sonar.db.ce.CeQueueMapper;
import org.sonar.db.ce.CeScannerContextMapper;
+import org.sonar.db.ce.CeTaskCharacteristicDto;
+import org.sonar.db.ce.CeTaskCharacteristicMapper;
import org.sonar.db.ce.CeTaskInputMapper;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentDtoWithSnapshotId;
// DTO aliases, keep them sorted alphabetically
confBuilder.loadAlias("ActiveRule", ActiveRuleDto.class);
confBuilder.loadAlias("ActiveRuleParam", ActiveRuleParamDto.class);
+ confBuilder.loadAlias("CeTaskCharacteristic", CeTaskCharacteristicDto.class);
confBuilder.loadAlias("Component", ComponentDto.class);
confBuilder.loadAlias("ComponentLink", ComponentLinkDto.class);
confBuilder.loadAlias("ComponentWithSnapshot", ComponentDtoWithSnapshotId.class);
CeQueueMapper.class,
CeScannerContextMapper.class,
CeTaskInputMapper.class,
+ CeTaskCharacteristicMapper.class,
ComponentKeyUpdaterMapper.class,
ComponentLinkMapper.class,
ComponentMapper.class,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.ce;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.sonar.db.Dao;
+import org.sonar.db.DbSession;
+
+public class CeTaskCharacteristicDao implements Dao {
+ public void insert(DbSession dbSession, Collection<CeTaskCharacteristicDto> characteristics) {
+ for (CeTaskCharacteristicDto dto : characteristics) {
+ mapper(dbSession).insert(dto);
+ }
+ }
+
+ public Map<String, String> getTaskCharacteristics(DbSession dbSession, String taskUuid) {
+ Map<String, String> map = new LinkedHashMap<>();
+ List<CeTaskCharacteristicDto> characteristics = mapper(dbSession).selectTaskCharacteristics(taskUuid);
+ characteristics.stream().forEach(dto -> map.put(dto.getKey(), dto.getValue()));
+ return map;
+ }
+
+ private static CeTaskCharacteristicMapper mapper(DbSession session) {
+ return session.getMapper(CeTaskCharacteristicMapper.class);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.ce;
+
+public class CeTaskCharacteristicDto {
+ private String uuid;
+ private String taskUuid;
+ private String key;
+ private String value;
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getTaskUuid() {
+ return taskUuid;
+ }
+
+ public void setTaskUuid(String taskUuid) {
+ this.taskUuid = taskUuid;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.ce;
+
+import java.util.List;
+
+import org.apache.ibatis.annotations.Param;
+
+public interface CeTaskCharacteristicMapper {
+ List<CeTaskCharacteristicDto> selectTaskCharacteristics(@Param("taskUuid") String taskUuid);
+
+ void insert(CeTaskCharacteristicDto taskCharacteristic);
+
+}
--- /dev/null
+<?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.ce.CeTaskCharacteristicMapper">
+ <insert id="insert" parameterType="CeTaskCharacteristic" useGeneratedKeys="false">
+ insert into ce_task_characteristics (
+ uuid,
+ task_uuid,
+ kee,
+ text_value
+ )
+ values (
+ #{uuid,jdbcType=VARCHAR},
+ #{taskUuid,jdbcType=VARCHAR},
+ #{key,jdbcType=VARCHAR},
+ #{value,jdbcType=VARCHAR}
+ )
+ </insert>
+
+ <select id="selectTaskCharacteristics" parameterType="map" resultType="CeTaskCharacteristic">
+ SELECT
+ c.uuid as "uuid",
+ c.task_uuid as "taskUuid",
+ c.kee as "key",
+ c.text_value as "value"
+ FROM
+ ce_task_characteristics c
+ WHERE
+ c.task_uuid = #{taskUuid,jdbcType=VARCHAR}
+ </select>
+</mapper>
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new DaoModule().configure(container);
- assertThat(container.size()).isEqualTo(2 + 46);
+ assertThat(container.size()).isEqualTo(2 + 47);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.ce;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+
+import java.util.Collections;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+public class CeTaskCharacteristicDaoTest {
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private CeTaskCharacteristicDao underTest = new CeTaskCharacteristicDao();
+
+ @Test
+ public void test_insert() {
+ CeTaskCharacteristicDto dto = new CeTaskCharacteristicDto();
+ dto.setKey("key");
+ dto.setValue("value");
+ dto.setUuid("uuid");
+ dto.setTaskUuid("task");
+ underTest.insert(dbTester.getSession(), Collections.singletonList(dto));
+ dbTester.getSession().commit();
+
+ assertThat(underTest.getTaskCharacteristics(dbTester.getSession(), "task")).containsOnly(entry("key", "value"));
+ }
+
+ @Test
+ public void test_no_result() {
+ assertThat(underTest.getTaskCharacteristics(dbTester.getSession(), "task")).isEmpty();
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.ce;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Test;
+
+public class CeTaskCharacteristicDtoTest {
+
+ @Test
+ public void test_set_get() {
+ CeTaskCharacteristicDto dto = new CeTaskCharacteristicDto();
+ dto.setKey("key");
+ dto.setValue("value");
+ dto.setTaskUuid("task");
+ dto.setUuid("uuid");
+ assertThat(dto.getKey()).isEqualTo("key");
+ assertThat(dto.getValue()).isEqualTo("value");
+ assertThat(dto.getTaskUuid()).isEqualTo("task");
+ assertThat(dto.getUuid()).isEqualTo("uuid");
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.v66;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+import java.sql.SQLException;
+
+import org.sonar.db.Database;
+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;
+
+public class CreateTableCeTaskCharacteristics extends DdlChange {
+ private static final String TABLE_NAME = "ce_task_characteristics";
+
+ public CreateTableCeTaskCharacteristics(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ VarcharColumnDef uuidColumn = newVarcharColumnDefBuilder()
+ .setColumnName("uuid")
+ .setLimit(UUID_SIZE)
+ .setIsNullable(false)
+ .setIgnoreOracleUnit(true)
+ .build();
+ VarcharColumnDef ceTaskUuidColumn = newVarcharColumnDefBuilder()
+ .setColumnName("task_uuid")
+ .setLimit(UUID_SIZE)
+ .setIsNullable(false)
+ .setIgnoreOracleUnit(true)
+ .build();
+ VarcharColumnDef keyColumn = newVarcharColumnDefBuilder()
+ .setColumnName("kee")
+ .setLimit(512)
+ .setIsNullable(false)
+ .setIgnoreOracleUnit(true)
+ .build();
+ VarcharColumnDef valueColumn = newVarcharColumnDefBuilder()
+ .setColumnName("text_value")
+ .setLimit(512)
+ .setIsNullable(true)
+ .setIgnoreOracleUnit(true)
+ .build();
+
+ context.execute(
+ new CreateTableBuilder(getDialect(), TABLE_NAME)
+ .addPkColumn(uuidColumn)
+ .addColumn(ceTaskUuidColumn)
+ .addColumn(keyColumn)
+ .addColumn(valueColumn)
+ .build());
+
+ context.execute(
+ new CreateIndexBuilder(getDialect())
+ .setTable(TABLE_NAME)
+ .setName("ce_characteristics_" + ceTaskUuidColumn.getName())
+ .addColumn(ceTaskUuidColumn)
+ .setUnique(false)
+ .build());
+ }
+
+}
public class DbVersion66 implements DbVersion {
@Override
public void addSteps(MigrationStepRegistry registry) {
- registry.add(1800, "Add incremental column to snapthots table", AddIncrementalColumnToSnapshotsTable.class);
+ registry
+ .add(1800, "Add incremental column to snapthots table", AddIncrementalColumnToSnapshotsTable.class)
+ .add(1801, "Create table CE task characteristics", CreateTableCeTaskCharacteristics.class);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.v66;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+public class CreateTableCeTaskCharacteristicsTest {
+ private static final String TABLE = "ce_task_characteristics";
+
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(CreateTableCeTaskCharacteristicsTest.class, "empty.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private CreateTableCeTaskCharacteristics underTest = new CreateTableCeTaskCharacteristics(db.database());
+
+ @Test
+ public void creates_table_on_empty_db() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.countRowsOfTable(TABLE)).isEqualTo(0);
+ db.assertPrimaryKey(TABLE, "pk_" + TABLE, "uuid");
+ db.assertColumnDefinition(TABLE, "task_uuid", Types.VARCHAR, 40, false);
+ db.assertColumnDefinition(TABLE, "kee", Types.VARCHAR, 512, false);
+ db.assertColumnDefinition(TABLE, "text_value", Types.VARCHAR, 512, true);
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+
+ underTest.execute();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.v66;
+
+import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMigrationCount;
+import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMinimumMigrationNumber;
+
+import org.junit.Test;
+
+public class DbVersion66Test {
+ private DbVersion66 underTest = new DbVersion66();
+
+ @Test
+ public void migrationNumber_starts_at_1800() {
+ verifyMinimumMigrationNumber(underTest, 1800);
+ }
+
+ @Test
+ public void verify_migration_count() {
+ verifyMigrationCount(underTest, 2);
+ }
+}
*/
package org.sonar.server.ce.ws;
+import static org.sonar.server.ws.WsUtils.checkRequest;
+
import java.io.BufferedInputStream;
import java.io.InputStream;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
import org.apache.commons.lang.StringUtils;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
private static final String PARAM_PROJECT_BRANCH = "projectBranch";
private static final String PARAM_PROJECT_NAME = "projectName";
private static final String PARAM_REPORT_DATA = "report";
+ private static final String PARAM_ANALYSIS_CHARACTERISTIC = "characteristic";
private final ReportSubmitter reportSubmitter;
private final DefaultOrganizationProvider defaultOrganizationProvider;
.createParam(PARAM_REPORT_DATA)
.setRequired(true)
.setDescription("Report file. Format is not an API, it changes among SonarQube versions.");
+
+ action
+ .createParam(PARAM_ANALYSIS_CHARACTERISTIC)
+ .setRequired(false)
+ .setDescription("Optional characteristic of the analysis. Can be repeated to define multiple characteristics.")
+ .setExampleValue("incremental=true")
+ .setSince("6.6");
}
@Override
String projectBranch = wsRequest.param(PARAM_PROJECT_BRANCH);
String projectName = StringUtils.defaultIfBlank(wsRequest.param(PARAM_PROJECT_NAME), projectKey);
+ Map<String, String> characteristics = parseTaskCharacteristics(wsRequest);
+
try (InputStream report = new BufferedInputStream(wsRequest.mandatoryParamAsPart(PARAM_REPORT_DATA).getInputStream())) {
- CeTask task = reportSubmitter.submit(organizationKey, projectKey, projectBranch, projectName, report);
+ CeTask task = reportSubmitter.submit(organizationKey, projectKey, projectBranch, projectName, characteristics, report);
WsCe.SubmitResponse submitResponse = WsCe.SubmitResponse.newBuilder()
.setTaskId(task.getUuid())
.setProjectId(task.getComponentUuid())
WsUtils.writeProtobuf(submitResponse, wsRequest, wsResponse);
}
}
+
+ private static Map<String, String> parseTaskCharacteristics(Request wsRequest) {
+ Map<String, String> characteristics = new LinkedHashMap<>();
+
+ for (String param : wsRequest.multiParam(PARAM_ANALYSIS_CHARACTERISTIC)) {
+ String[] pair = StringUtils.split(param, "=", 2);
+ checkRequest(pair.length == 2, "Parameter '%s' must be a key-value pair with the format 'key=value'.", PARAM_ANALYSIS_CHARACTERISTIC);
+ checkRequest(!characteristics.containsKey(pair[0]), "Key '%s' was provided twice with parameters '%s'", pair[0], PARAM_ANALYSIS_CHARACTERISTIC);
+ characteristics.put(pair[0], pair[1]);
+ }
+ return characteristics;
+ }
+
}
*/
package org.sonar.server.computation.queue;
-import com.google.common.base.Optional;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
+import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
+import static org.sonar.server.component.NewComponent.newComponentBuilder;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
+
import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
import javax.annotation.Nullable;
+
import org.apache.commons.lang.StringUtils;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.server.ServerSide;
import org.sonar.ce.queue.CeTask;
import org.sonar.ce.queue.CeTaskSubmit;
import org.sonar.core.component.ComponentKeys;
+import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
+import org.sonar.db.ce.CeTaskCharacteristicDto;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.permission.PermissionTemplateService;
import org.sonar.server.user.UserSession;
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.lang.String.format;
-import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
-import static org.sonar.server.component.NewComponent.newComponentBuilder;
-import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
+import com.google.common.base.Optional;
@ServerSide
public class ReportSubmitter {
private final ComponentUpdater componentUpdater;
private final PermissionTemplateService permissionTemplateService;
private final DbClient dbClient;
+ private final UuidFactory uuidFactory;
public ReportSubmitter(CeQueue queue, UserSession userSession, ComponentUpdater componentUpdater,
- PermissionTemplateService permissionTemplateService, DbClient dbClient) {
+ PermissionTemplateService permissionTemplateService, UuidFactory uuidFactory, DbClient dbClient) {
this.queue = queue;
this.userSession = userSession;
this.componentUpdater = componentUpdater;
this.permissionTemplateService = permissionTemplateService;
+ this.uuidFactory = uuidFactory;
this.dbClient = dbClient;
}
+ public CeTask submit(String organizationKey, String projectKey, @Nullable String projectBranch, @Nullable String projectName, InputStream reportInput) {
+ return submit(organizationKey, projectKey, projectBranch, projectName, Collections.emptyMap(), reportInput);
+ }
+
/**
* @throws NotFoundException if the organization with the specified key does not exist
* @throws IllegalArgumentException if the organization with the specified key is not the organization of the specified project (when it already exists in DB)
*/
- public CeTask submit(String organizationKey, String projectKey, @Nullable String projectBranch, @Nullable String projectName, InputStream reportInput) {
+ public CeTask submit(String organizationKey, String projectKey, @Nullable String projectBranch, @Nullable String projectName, Map<String, String> characteristics,
+ InputStream reportInput) {
try (DbSession dbSession = dbClient.openSession(false)) {
String effectiveProjectKey = ComponentKeys.createKey(projectKey, projectBranch);
OrganizationDto organizationDto = getOrganizationDtoOrFail(dbSession, organizationKey);
ensureOrganizationIsConsistent(opt, organizationDto);
ComponentDto project = opt.or(() -> createProject(dbSession, organizationDto, projectKey, projectBranch, projectName));
checkScanPermission(project);
- return submitReport(dbSession, reportInput, project);
+ return submitReport(dbSession, reportInput, project, characteristics);
}
}
return componentUpdater.create(dbSession, newProject, userId);
}
- private CeTask submitReport(DbSession dbSession, InputStream reportInput, ComponentDto project) {
- // the report file must be saved before submitting the task
+ private CeTask submitReport(DbSession dbSession, InputStream reportInput, ComponentDto project, Map<String, String> characteristicsMap) {
CeTaskSubmit.Builder submit = queue.prepareSubmit();
+ List<CeTaskCharacteristicDto> characteristics = characteristicsMap.entrySet().stream()
+ .map(e -> toDto(submit.getUuid(), e.getKey(), e.getValue())).collect(Collectors.toList());
+
+ // the report file must be saved before submitting the task
dbClient.ceTaskInputDao().insert(dbSession, submit.getUuid(), reportInput);
+ if (!characteristics.isEmpty()) {
+ dbClient.ceTaskCharacteristicsDao().insert(dbSession, characteristics);
+ }
dbSession.commit();
submit.setType(CeTaskTypes.REPORT);
submit.setSubmitterLogin(userSession.getLogin());
return queue.submit(submit.build());
}
+
+ private CeTaskCharacteristicDto toDto(String taskUuid, String key, String value) {
+ CeTaskCharacteristicDto dto = new CeTaskCharacteristicDto();
+ dto.setTaskUuid(taskUuid);
+ dto.setKey(key);
+ dto.setValue(value);
+ dto.setUuid(uuidFactory.create());
+ return dto;
+ }
}
*/
package org.sonar.server.computation.task.projectanalysis.step;
-import javax.annotation.Nullable;
-
import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@Override
public void visitView(Component view) {
- SnapshotDto snapshot = createAnalysis(analysisMetadataHolder.getUuid(), view, false, null);
+ SnapshotDto snapshot = createAnalysis(analysisMetadataHolder.getUuid(), view, false, false);
updateSnapshotPeriods(snapshot);
persist(snapshot, dbSession);
}
snapshotDto.setPeriodDate(period.getSnapshotDate());
}
- private SnapshotDto createAnalysis(String snapshotUuid, Component component, boolean setVersion, @Nullable Boolean incremental) {
+ private SnapshotDto createAnalysis(String snapshotUuid, Component component, boolean setVersion, boolean incremental) {
String componentUuid = component.getUuid();
return new SnapshotDto()
.setUuid(snapshotUuid)
*/
package org.sonar.server.ce.ws;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyMapOf;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import java.io.ByteArrayInputStream;
import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Matchers;
+import org.mockito.MockitoAnnotations;
import org.sonar.ce.queue.CeTask;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.server.computation.queue.ReportSubmitter;
import org.sonarqube.ws.MediaTypes;
import org.sonarqube.ws.WsCe;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
public class SubmitActionTest {
private static final CeTask A_CE_TASK = new CeTask.Builder()
.setComponentUuid("PROJECT_1").setSubmitterLogin("robert")
.build();
+ @Captor
+ ArgumentCaptor<Map<String, String>> map;
+
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.fromUuid("org1");
private String organizationKey = defaultOrganizationProvider.get().getKey();
private ReportSubmitter reportSubmitter = mock(ReportSubmitter.class);
private SubmitAction underTest = new SubmitAction(reportSubmitter, defaultOrganizationProvider);
private WsActionTester tester = new WsActionTester(underTest);
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
@Test
public void submit_task_to_the_queue_and_ask_for_immediate_processing() {
- when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("My Project"), any(InputStream.class)))
- .thenReturn(A_CE_TASK);
+ when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("My Project"),
+ anyMapOf(String.class, String.class), any(InputStream.class))).thenReturn(A_CE_TASK);
WsCe.SubmitResponse submitResponse = tester.newRequest()
.setParam("projectKey", "my_project")
.setMethod("POST")
.executeProtobuf(WsCe.SubmitResponse.class);
- verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("My Project"), any(InputStream.class));
+ verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("My Project"),
+ anyMapOf(String.class, String.class), any(InputStream.class));
assertThat(submitResponse.getTaskId()).isEqualTo("TASK_1");
assertThat(submitResponse.getProjectId()).isEqualTo("PROJECT_1");
}
+ @Test
+ public void submit_task_with_multiple_characteristics() {
+ when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("My Project"),
+ anyMapOf(String.class, String.class), any(InputStream.class))).thenReturn(A_CE_TASK);
+
+ String[] characteristics = {"branch=branch1", "incremental=true", "key=value1=value2"};
+ WsCe.SubmitResponse submitResponse = tester.newRequest()
+ .setParam("projectKey", "my_project")
+ .setParam("projectName", "My Project")
+ .setMultiParam("characteristic", Arrays.asList(characteristics))
+ .setPart("report", new ByteArrayInputStream("{binary}".getBytes()), "foo.bar")
+ .setMethod("POST")
+ .executeProtobuf(WsCe.SubmitResponse.class);
+
+ assertThat(submitResponse.getTaskId()).isEqualTo("TASK_1");
+ verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("My Project"),
+ map.capture(), any(InputStream.class));
+
+ assertThat(map.getValue()).containsOnly(entry("incremental", "true"), entry("branch", "branch1"), entry("key", "value1=value2"));
+ }
+
@Test
public void test_example_json_response() {
- when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("My Project"), any(InputStream.class)))
- .thenReturn(A_CE_TASK);
+ when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("My Project"),
+ anyMapOf(String.class, String.class), any(InputStream.class))).thenReturn(A_CE_TASK);
TestResponse wsResponse = tester.newRequest()
.setParam("projectKey", "my_project")
*/
@Test
public void project_name_is_optional() {
- when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("my_project"), any(InputStream.class)))
- .thenReturn(A_CE_TASK);
+ when(reportSubmitter.submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("my_project"),
+ anyMapOf(String.class, String.class), any(InputStream.class))).thenReturn(A_CE_TASK);
tester.newRequest()
.setParam("projectKey", "my_project")
.setMethod("POST")
.execute();
- verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("my_project"), any(InputStream.class));
+ verify(reportSubmitter).submit(eq(organizationKey), eq("my_project"), Matchers.isNull(String.class), eq("my_project"),
+ anyMapOf(String.class, String.class), any(InputStream.class));
}
}
*/
package org.sonar.server.computation.queue;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
+import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
+import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS;
+import static org.sonar.db.permission.OrganizationPermission.SCAN;
+
+import java.util.HashMap;
+import java.util.Map;
+
import org.apache.commons.io.IOUtils;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.utils.System2;
import org.sonar.ce.queue.CeQueue;
import org.sonar.ce.queue.CeQueueImpl;
import org.sonar.ce.queue.CeTaskSubmit;
import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.util.SequenceUuidFactory;
+import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.server.permission.PermissionTemplateService;
import org.sonar.server.tester.UserSessionRule;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
-import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
-import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS;
-import static org.sonar.db.permission.OrganizationPermission.SCAN;
-
public class ReportSubmitterTest {
private static final String PROJECT_KEY = "MY_PROJECT";
private ComponentUpdater componentUpdater = mock(ComponentUpdater.class);
private PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class);
private FavoriteUpdater favoriteUpdater = mock(FavoriteUpdater.class);
+ private UuidFactory uuidFactory = new SequenceUuidFactory();
- private ReportSubmitter underTest = new ReportSubmitter(queue, userSession, componentUpdater, permissionTemplateService, db.getDbClient());
+ private ReportSubmitter underTest = new ReportSubmitter(queue, userSession, componentUpdater, permissionTemplateService, uuidFactory, db.getDbClient());
@Before
public void setUp() throws Exception {
underTest.submit(organization.getKey(), project.getDbKey(), null, project.name(), IOUtils.toInputStream("{binary}"));
}
+ @Test
+ public void submit_inserts_characteristics() {
+ userSession
+ .addPermission(OrganizationPermission.SCAN, db.getDefaultOrganization().getUuid())
+ .addPermission(PROVISION_PROJECTS, db.getDefaultOrganization());
+
+ mockSuccessfulPrepareSubmitCall();
+ ComponentDto project = newPrivateProjectDto(db.getDefaultOrganization(), PROJECT_UUID).setKey(PROJECT_KEY);
+ when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), eq(null))).thenReturn(project);
+ when(permissionTemplateService.wouldUserHaveScanPermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), anyInt(), anyString(),
+ eq(PROJECT_KEY), eq(Qualifiers.PROJECT)))
+ .thenReturn(true);
+
+ Map<String, String> taskCharacteristics = new HashMap<>();
+ taskCharacteristics.put("incremental", "true");
+ taskCharacteristics.put("pr", "mypr");
+
+ underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, taskCharacteristics, IOUtils.toInputStream("{binary}"));
+
+ ArgumentCaptor<CeTaskSubmit> submittedTask = ArgumentCaptor.forClass(CeTaskSubmit.class);
+ verify(queue).submit(submittedTask.capture());
+ String taskUuid = submittedTask.getValue().getUuid();
+
+ Map<String, String> insertedCharacteristics = db.getDbClient().ceTaskCharacteristicsDao().getTaskCharacteristics(db.getSession(), taskUuid);
+ assertThat(insertedCharacteristics).containsOnly(entry("incremental", "true"), entry("pr", "mypr"));
+ }
+
@Test
public void submit_a_report_on_existing_project() {
ComponentDto project = db.components().insertPrivateProject(db.getDefaultOrganization());
.setParam("projectBranch", moduleHierarchy.root().getBranch())
.setPart("report", filePart);
+ if (analysisMode.isIncremental()) {
+ post.setParam("characteristic", "incremental=true");
+ }
+
WsResponse response;
try {
response = wsClient.call(post).failIfNotSuccessful();
entry("organization", "MyOrg"),
entry("projectKey", "struts"));
}
+
+ @Test
+ public void test_send_characteristics() throws Exception {
+ ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class),
+ new ReportPublisherStep[0]);
+
+ when(mode.isIncremental()).thenReturn(true);
+ settings.setProperty(CoreProperties.PROJECT_ORGANIZATION_PROPERTY, "MyOrg");
+
+ WsResponse response = mock(WsResponse.class);
+
+ PipedOutputStream out = new PipedOutputStream();
+ PipedInputStream in = new PipedInputStream(out);
+ WsCe.SubmitResponse.newBuilder().build().writeTo(out);
+ out.close();
+
+ when(response.failIfNotSuccessful()).thenReturn(response);
+ when(response.contentStream()).thenReturn(in);
+
+ when(wsClient.call(any(WsRequest.class))).thenReturn(response);
+ underTest.upload(temp.newFile());
+
+ ArgumentCaptor<WsRequest> capture = ArgumentCaptor.forClass(WsRequest.class);
+ verify(wsClient).call(capture.capture());
+
+ WsRequest wsRequest = capture.getValue();
+ assertThat(wsRequest.getParams()).containsOnly(
+ entry("organization", "MyOrg"),
+ entry("projectKey", "struts"),
+ entry("characteristic", "incremental=true"));
+ }
}