.setDescription(format("Navigate through components based on the chosen strategy with specified measures. The %s or the %s parameter must be provided.<br>" +
"Requires one of the following permissions:" +
"<ul>" +
- "<li>'Administer System'</li>" +
- "<li>'Administer' rights on the specified project</li>" +
- "<li>'Browse' on the specified project</li>" +
+ " <li>'Administer System'</li>" +
+ " <li>'Administer' rights on the specified project</li>" +
+ " <li>'Browse' on the specified project</li>" +
"</ul>" +
"When limiting search with the %s parameter, directories are not returned.",
PARAM_BASE_COMPONENT_ID, PARAM_BASE_COMPONENT_KEY, Param.TEXT_QUERY))
import org.sonar.server.plugins.ws.UninstallAction;
import org.sonar.server.plugins.ws.UpdatesAction;
import org.sonar.server.project.ws.ProjectsWsModule;
+import org.sonar.server.projectanalysis.ProjectAnalysisModule;
import org.sonar.server.projectlink.ws.ProjectLinksModule;
import org.sonar.server.property.InternalPropertiesImpl;
import org.sonar.server.qualitygate.QualityGateModule;
// Project Links
ProjectLinksModule.class,
+ // Project Analyses
+ ProjectAnalysisModule.class,
+
// System
ServerLogging.class,
RestartAction.class,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.projectanalysis;
+
+import org.sonar.core.platform.Module;
+import org.sonar.server.projectanalysis.ws.CreateEventAction;
+import org.sonar.server.projectanalysis.ws.ProjectAnalysesWs;
+
+public class ProjectAnalysisModule extends Module {
+
+ @Override
+ protected void configureModule() {
+ add(
+ ProjectAnalysesWs.class,
+ // actions
+ CreateEventAction.class);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.
+ */
+
+@ParametersAreNonnullByDefault
+package org.sonar.server.projectanalysis;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.projectanalysis.ws;
+
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.event.EventDto;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.ProjectAnalyses.CreateEventResponse;
+import org.sonarqube.ws.ProjectAnalyses.Event;
+import org.sonarqube.ws.client.projectanalysis.CreateEventRequest;
+import org.sonarqube.ws.client.projectanalysis.CreateEventRequest.Category;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+import static org.sonar.core.util.Protobuf.setNullable;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonarqube.ws.client.projectanalysis.CreateEventRequest.Category.OTHER;
+import static org.sonarqube.ws.client.projectanalysis.CreateEventRequest.Category.VERSION;
+import static org.sonarqube.ws.client.projectanalysis.CreateEventRequest.Category.fromLabel;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_ANALYSIS;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_CATEGORY;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_DESCRIPTION;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_NAME;
+
+public class CreateEventAction implements ProjectAnalysesWsAction {
+ private final DbClient dbClient;
+ private final UuidFactory uuidFactory;
+ private final System2 system;
+ private final UserSession userSession;
+
+ public CreateEventAction(DbClient dbClient, UuidFactory uuidFactory, System2 system, UserSession userSession) {
+ this.dbClient = dbClient;
+ this.uuidFactory = uuidFactory;
+ this.system = system;
+ this.userSession = userSession;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction("create_event")
+ .setDescription("Add an event to a project analysis.<br>" +
+ "Requires one of the following permissions:" +
+ "<ul>" +
+ " <li>'Administer System'</li>" +
+ " <li>'Administer' rights on the specified project</li>" +
+ "</ul>")
+ .setSince("6.3")
+ .setPost(true)
+ .setResponseExample(getClass().getResource("create_event-example.json"))
+ .setHandler(this);
+
+ action.createParam(PARAM_ANALYSIS)
+ .setDescription("Analysis key")
+ .setExampleValue(Uuids.UUID_EXAMPLE_01)
+ .setRequired(true);
+
+ action.createParam(PARAM_CATEGORY)
+ .setDescription("Category")
+ .setDefaultValue(OTHER)
+ .setPossibleValues(VERSION, OTHER);
+
+ action.createParam(PARAM_NAME)
+ .setDescription("Name")
+ .setExampleValue("5.6")
+ .setRequired(true);
+
+ action.createParam(PARAM_DESCRIPTION)
+ .setDescription("Description")
+ .setExampleValue("Version released");
+ }
+
+ @Override
+ public void handle(Request httpRequest, Response httpResponse) throws Exception {
+ CreateEventRequest request = toAddEventRequest(httpRequest);
+ CreateEventResponse response = doHandle(request);
+
+ writeProtobuf(response, httpRequest, httpResponse);
+ }
+
+ private CreateEventResponse doHandle(CreateEventRequest request) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ SnapshotDto analysis = getAnalysis(dbSession, request);
+ checkPermissions(analysis);
+ checkExistingDbEvents(dbSession, request, analysis);
+ EventDto dbEvent = insertDbEvent(dbSession, request, analysis);
+ return toCreateEventResponse(dbEvent);
+ }
+ }
+
+ private void checkPermissions(SnapshotDto analysis) {
+ userSession.checkComponentUuidPermission(UserRole.ADMIN, analysis.getComponentUuid());
+ }
+
+ private EventDto insertDbEvent(DbSession dbSession, CreateEventRequest request, SnapshotDto analysis) {
+ EventDto dbEvent = dbClient.eventDao().insert(dbSession, toDbEvent(request, analysis));
+ if (VERSION.equals(request.getCategory())) {
+ dbClient.snapshotDao().updateVersion(dbSession, analysis.getUuid(), request.getName());
+ }
+ dbSession.commit();
+ return dbEvent;
+ }
+
+ private SnapshotDto getAnalysis(DbSession dbSession, CreateEventRequest request) {
+ SnapshotDto analysis = dbClient.snapshotDao().selectByUuid(dbSession, request.getAnalysis())
+ .orElseThrow(() -> new NotFoundException(format("Analysis '%s' is not found", request.getAnalysis())));
+ ComponentDto project = dbClient.componentDao().selectByUuid(dbSession, analysis.getComponentUuid()).orNull();
+ checkState(project != null, "Project of analysis '%s' is not found", analysis.getUuid());
+ checkArgument(Qualifiers.PROJECT.equals(project.qualifier()) && Scopes.PROJECT.equals(project.scope()),
+ "An event must be created on a project");
+ return analysis;
+ }
+
+ private static CreateEventRequest toAddEventRequest(Request request) {
+ return CreateEventRequest.builder()
+ .setAnalysis(request.mandatoryParam(PARAM_ANALYSIS))
+ .setName(request.mandatoryParam(PARAM_NAME))
+ .setCategory(request.mandatoryParamAsEnum(PARAM_CATEGORY, Category.class))
+ .setDescription(request.param(PARAM_DESCRIPTION))
+ .build();
+ }
+
+ private EventDto toDbEvent(CreateEventRequest request, SnapshotDto analysis) {
+ return new EventDto()
+ .setUuid(uuidFactory.create())
+ .setAnalysisUuid(analysis.getUuid())
+ .setComponentUuid(analysis.getComponentUuid())
+ .setCategory(request.getCategory().getLabel())
+ .setName(request.getName())
+ .setDescription(request.getDescription())
+ .setCreatedAt(system.now())
+ .setDate(analysis.getCreatedAt());
+ }
+
+ private static CreateEventResponse toCreateEventResponse(EventDto dbEvent) {
+ Event.Builder wsEvent = Event.newBuilder()
+ .setKey(dbEvent.getUuid())
+ .setCategory(fromLabel(dbEvent.getCategory()).name())
+ .setAnalysis(dbEvent.getAnalysisUuid())
+ .setName(dbEvent.getName());
+ setNullable(dbEvent.getDescription(), wsEvent::setDescription);
+
+ return CreateEventResponse.newBuilder().setEvent(wsEvent).build();
+ }
+
+ private void checkExistingDbEvents(DbSession dbSession, CreateEventRequest request, SnapshotDto analysis) {
+ List<EventDto> dbEvents = dbClient.eventDao().selectByAnalysisUuid(dbSession, analysis.getUuid());
+ Predicate<EventDto> similarEventExisting = filterSimilarEvents(request);
+ dbEvents.stream()
+ .filter(similarEventExisting)
+ .findAny()
+ .ifPresent(throwException(request));
+ }
+
+ private static Predicate<EventDto> filterSimilarEvents(CreateEventRequest request) {
+ switch (request.getCategory()) {
+ case VERSION:
+ return dbEvent -> VERSION.getLabel().equals(dbEvent.getCategory());
+ case OTHER:
+ return dbEvent -> OTHER.getLabel().equals(dbEvent.getCategory()) && request.getName().equals(dbEvent.getName());
+ default:
+ throw new IllegalStateException("Event category not handled: " + request.getCategory());
+ }
+ }
+
+ private static Consumer<EventDto> throwException(CreateEventRequest request) {
+ switch (request.getCategory()) {
+ case VERSION:
+ return dbEvent -> {
+ throw new IllegalArgumentException(format("A version event already exists on analysis '%s'", request.getAnalysis()));
+ };
+ case OTHER:
+ return dbEvent -> {
+ throw new IllegalArgumentException(format("An 'other' event with the same name already exists on analysis '%s'", request.getAnalysis()));
+ };
+ default:
+ throw new IllegalStateException("Event category not handled: " + request.getCategory());
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.projectanalysis.ws;
+
+import org.sonar.api.server.ws.WebService;
+
+public class ProjectAnalysesWs implements WebService {
+
+ private final ProjectAnalysesWsAction[] actions;
+
+ public ProjectAnalysesWs(ProjectAnalysesWsAction... actions) {
+ this.actions = actions;
+ }
+
+ @Override
+ public void define(Context context) {
+ NewController controller = context.createController("api/project_analyses")
+ .setDescription("Manage project analyses.")
+ .setSince("6.3");
+
+ for (ProjectAnalysesWsAction action : actions) {
+ action.define(controller);
+ }
+
+ controller.done();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.projectanalysis.ws;
+
+import org.sonar.server.ws.WsAction;
+
+public interface ProjectAnalysesWsAction extends WsAction {
+ // Marker interface
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.
+ */
+
+@ParametersAreNonnullByDefault
+package org.sonar.server.projectanalysis.ws;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+{
+ "event": {
+ "analysis": "A2",
+ "key": "E1",
+ "category": "OTHER",
+ "name": "My Custom Event",
+ "description": "event description"
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.projectanalysis;
+
+import org.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectAnalysisModuleTest {
+
+ @Test
+ public void verify_count_of_added_components() {
+ ComponentContainer container = new ComponentContainer();
+ new ProjectAnalysisModule().configure(container);
+ assertThat(container.size()).isEqualTo(2 + 2);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.projectanalysis.ws;
+
+import com.google.common.base.Throwables;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.component.SnapshotTesting;
+import org.sonar.db.event.EventDto;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.ProjectAnalyses;
+import org.sonarqube.ws.ProjectAnalyses.CreateEventResponse;
+import org.sonarqube.ws.client.projectanalysis.CreateEventRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.db.component.ComponentTesting.newView;
+import static org.sonar.db.component.SnapshotTesting.newAnalysis;
+import static org.sonar.db.component.SnapshotTesting.newSnapshot;
+import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.client.WsRequest.Method.POST;
+import static org.sonarqube.ws.client.projectanalysis.CreateEventRequest.Category.OTHER;
+import static org.sonarqube.ws.client.projectanalysis.CreateEventRequest.Category.VERSION;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_ANALYSIS;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_CATEGORY;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_DESCRIPTION;
+import static org.sonarqube.ws.client.projectanalysis.ProjectAnalysesWsParameters.PARAM_NAME;
+
+public class CreateEventActionTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+ private DbClient dbClient = db.getDbClient();
+ private DbSession dbSession = db.getSession();
+
+ private UuidFactory uuidFactory = UuidFactoryFast.getInstance();
+ private System2 system = mock(System2.class);
+
+ private WsActionTester ws = new WsActionTester(new CreateEventAction(dbClient, uuidFactory, system, userSession));
+
+ @Before
+ public void setUp() {
+ when(system.now()).thenReturn(42L);
+ }
+
+ @Test
+ public void json_example() {
+ ComponentDto project = db.components().insertProject();
+ SnapshotDto analysis = dbClient.snapshotDao().insert(dbSession, SnapshotTesting.newAnalysis(project).setUuid("A2"));
+ db.commit();
+ uuidFactory = mock(UuidFactory.class);
+ when(uuidFactory.create()).thenReturn("E1");
+ ws = new WsActionTester(new CreateEventAction(dbClient, uuidFactory, system, userSession));
+
+ String result = ws.newRequest()
+ .setParam(PARAM_ANALYSIS, analysis.getUuid())
+ .setParam(PARAM_CATEGORY, OTHER.name())
+ .setParam(PARAM_NAME, "My Custom Event")
+ .setParam(PARAM_DESCRIPTION, "event description")
+ .execute().getInput();
+
+ assertJson(result).isSimilarTo(getClass().getResource("create_event-example.json"));
+ }
+
+ @Test
+ public void create_event_in_db() {
+ SnapshotDto analysis = db.components().insertProjectAndSnapshot(newProjectDto());
+ CreateEventRequest.Builder request = CreateEventRequest.builder()
+ .setAnalysis(analysis.getUuid())
+ .setCategory(VERSION)
+ .setName("5.6.3")
+ .setDescription("LTS version");
+ when(system.now()).thenReturn(123_456_789L);
+
+ CreateEventResponse result = call(request);
+
+ List<EventDto> dbEvents = dbClient.eventDao().selectByComponentUuid(dbSession, analysis.getComponentUuid());
+ assertThat(dbEvents).hasSize(1);
+ EventDto dbEvent = dbEvents.get(0);
+ assertThat(dbEvent.getName()).isEqualTo("5.6.3");
+ assertThat(dbEvent.getCategory()).isEqualTo(VERSION.getLabel());
+ assertThat(dbEvent.getDescription()).isEqualTo("LTS version");
+ assertThat(dbEvent.getAnalysisUuid()).isEqualTo(analysis.getUuid());
+ assertThat(dbEvent.getComponentUuid()).isEqualTo(analysis.getComponentUuid());
+ assertThat(dbEvent.getUuid()).isEqualTo(result.getEvent().getKey());
+ assertThat(dbEvent.getCreatedAt()).isEqualTo(123_456_789L);
+ assertThat(dbEvent.getDate()).isEqualTo(analysis.getCreatedAt());
+ }
+
+ @Test
+ public void create_event_as_project_admin() {
+ SnapshotDto analysis = db.components().insertProjectAndSnapshot(newProjectDto("P1"));
+ CreateEventRequest.Builder request = CreateEventRequest.builder()
+ .setAnalysis(analysis.getUuid())
+ .setCategory(VERSION)
+ .setName("5.6.3")
+ .setDescription("LTS version");
+ userSession.anonymous().addProjectUuidPermissions(UserRole.ADMIN, "P1");
+
+ CreateEventResponse result = call(request);
+
+ assertThat(result.getEvent().getKey()).isNotEmpty();
+ }
+
+ @Test
+ public void create_version_event() {
+ SnapshotDto analysis = db.components().insertProjectAndSnapshot(newProjectDto());
+ CreateEventRequest.Builder request = CreateEventRequest.builder()
+ .setAnalysis(analysis.getUuid())
+ .setCategory(VERSION)
+ .setName("5.6.3")
+ .setDescription("LTS version");
+
+ call(request);
+
+ Optional<SnapshotDto> newAnalysis = dbClient.snapshotDao().selectByUuid(dbSession, analysis.getUuid());
+ assertThat(newAnalysis.get().getVersion()).isEqualTo("5.6.3");
+ }
+
+ @Test
+ public void create_other_event_with_ws_response() {
+ SnapshotDto analysis = db.components().insertProjectAndSnapshot(newProjectDto());
+ CreateEventRequest.Builder request = CreateEventRequest.builder()
+ .setAnalysis(analysis.getUuid())
+ .setName("Project Import")
+ .setDescription("Import from another instance");
+
+ CreateEventResponse result = call(request);
+
+ SnapshotDto newAnalysis = dbClient.snapshotDao().selectByUuid(dbSession, analysis.getUuid()).get();
+ assertThat(analysis.getVersion()).isEqualTo(newAnalysis.getVersion());
+ ProjectAnalyses.Event event = result.getEvent();
+ assertThat(event.getKey()).isNotEmpty();
+ assertThat(event.getCategory()).isEqualTo(OTHER.name());
+ assertThat(event.getName()).isEqualTo("Project Import");
+ assertThat(event.getDescription()).isEqualTo("Import from another instance");
+ assertThat(event.getAnalysis()).isEqualTo(analysis.getUuid());
+ }
+
+ @Test
+ public void create_event_without_description() {
+ SnapshotDto analysis = db.components().insertProjectAndSnapshot(newProjectDto());
+ CreateEventRequest.Builder request = CreateEventRequest.builder()
+ .setAnalysis(analysis.getUuid())
+ .setCategory(OTHER)
+ .setName("Project Import");
+
+ CreateEventResponse result = call(request);
+
+ ProjectAnalyses.Event event = result.getEvent();
+ assertThat(event.getKey()).isNotEmpty();
+ assertThat(event.hasDescription()).isFalse();
+ }
+
+ @Test
+ public void create_2_version_events_on_same_project() {
+ ComponentDto project = newProjectDto();
+ SnapshotDto firstAnalysis = db.components().insertProjectAndSnapshot(project);
+ CreateEventRequest.Builder firstRequest = CreateEventRequest.builder()
+ .setAnalysis(firstAnalysis.getUuid())
+ .setCategory(VERSION)
+ .setName("5.6.3");
+ SnapshotDto secondAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(project));
+ db.commit();
+ CreateEventRequest.Builder secondRequest = CreateEventRequest.builder()
+ .setAnalysis(secondAnalysis.getUuid())
+ .setCategory(VERSION)
+ .setName("6.3");
+
+ call(firstRequest);
+ call(secondRequest);
+
+ List<EventDto> events = dbClient.eventDao().selectByComponentUuid(dbSession, project.uuid());
+ assertThat(events).hasSize(2);
+ }
+
+ @Test
+ public void fail_if_analysis_is_not_found() {
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Analysis 'A42' is not found");
+
+ CreateEventRequest.Builder request = CreateEventRequest.builder()
+ .setAnalysis("A42")
+ .setCategory(OTHER)
+ .setName("Project Import");
+
+ call(request);
+ }
+
+ @Test
+ public void fail_if_2_version_events_on_the_same_analysis() {
+ SnapshotDto analysis = db.components().insertProjectAndSnapshot(newProjectDto());
+ CreateEventRequest.Builder request = CreateEventRequest.builder()
+ .setAnalysis(analysis.getUuid())
+ .setCategory(VERSION)
+ .setName("5.6.3");
+ call(request);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("A version event already exists on analysis '" + analysis.getUuid() + "'");
+
+ call(request.setName("6.3"));
+ }
+
+ @Test
+ public void fail_if_2_other_events_on_same_analysis_with_same_name() {
+ SnapshotDto analysis = db.components().insertProjectAndSnapshot(newProjectDto());
+ CreateEventRequest.Builder request = CreateEventRequest.builder()
+ .setAnalysis(analysis.getUuid())
+ .setCategory(OTHER)
+ .setName("Project Import");
+ call(request);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("An 'other' event with the same name already exists on analysis '" + analysis.getUuid() + "'");
+
+ call(request.setName("Project Import"));
+ }
+
+ @Test
+ public void fail_if_category_other_than_authorized() {
+ expectedException.expect(IllegalArgumentException.class);
+
+ SnapshotDto analysis = db.components().insertProjectAndSnapshot(newProjectDto());
+ ws.newRequest()
+ .setParam(PARAM_ANALYSIS, analysis.getUuid())
+ .setParam(PARAM_NAME, "Project Import")
+ .setParam(PARAM_CATEGORY, "QP")
+ .execute();
+ }
+
+ @Test
+ public void fail_if_create_on_other_than_a_project() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("An event must be created on a project");
+
+ SnapshotDto analysis = db.components().insertViewAndSnapshot(newView());
+ CreateEventRequest.Builder request = CreateEventRequest.builder()
+ .setAnalysis(analysis.getUuid())
+ .setCategory(VERSION)
+ .setName("5.6.3");
+
+ call(request);
+ }
+
+ @Test
+ public void fail_if_project_is_not_found() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Project of analysis 'A1' is not found");
+
+ SnapshotDto analysis = dbClient.snapshotDao().insert(dbSession, newSnapshot().setUuid("A1"));
+ db.commit();
+ CreateEventRequest.Builder request = CreateEventRequest.builder()
+ .setAnalysis(analysis.getUuid())
+ .setCategory(VERSION)
+ .setName("5.6.3");
+
+ call(request);
+ }
+
+ @Test
+ public void fail_if_insufficient_permissions() {
+ SnapshotDto analysis = db.components().insertProjectAndSnapshot(newProjectDto("P1"));
+ CreateEventRequest.Builder request = CreateEventRequest.builder()
+ .setAnalysis(analysis.getUuid())
+ .setCategory(VERSION)
+ .setName("5.6.3")
+ .setDescription("LTS version");
+ userSession.anonymous();
+
+ expectedException.expect(ForbiddenException.class);
+
+ call(request);
+
+ }
+
+ @Test
+ public void ws_parameters() {
+ WebService.Action definition = ws.getDef();
+
+ assertThat(definition.isPost()).isTrue();
+ assertThat(definition.key()).isEqualTo("create_event");
+ assertThat(definition.responseExampleAsString()).isNotEmpty();
+ }
+
+ private CreateEventResponse call(CreateEventRequest.Builder requestBuilder) {
+ CreateEventRequest request = requestBuilder.build();
+ TestRequest httpRequest = ws.newRequest()
+ .setMethod(POST.name())
+ .setMediaType(MediaTypes.PROTOBUF);
+
+ httpRequest.setParam(PARAM_CATEGORY, request.getCategory().name())
+ .setParam(PARAM_NAME, request.getName())
+ .setParam(PARAM_ANALYSIS, request.getAnalysis());
+
+ if (request.getDescription() != null) {
+ httpRequest.setParam(PARAM_DESCRIPTION, request.getDescription());
+ }
+
+ try {
+ return CreateEventResponse.parseFrom(httpRequest.execute().getInputStream());
+ } catch (IOException e) {
+ throw Throwables.propagate(e);
+ }
+ }
+}
insert(session, Lists.asList(item, others));
}
+ public void updateVersion(DbSession dbSession, String analysisUuid, String version) {
+ mapper(dbSession).updateVersion(analysisUuid, version);
+ }
+
/**
* Used by Governance
*/
void unsetIsLastFlagForComponentUuid(@Param("componentUuid") String componentUuid);
void setIsLastFlagForAnalysisUuid(@Param("analysisUuid") String analysisUuid);
+
+ void updateVersion(@Param("analysisUuid") String analysisUuid, @Param("version") String version);
}
return session.getMapper(EventMapper.class).selectByComponentUuid(componentUuid);
}
- public void insert(DbSession session, EventDto dto) {
+ public List<EventDto> selectByAnalysisUuid(DbSession dbSession, String uuid) {
+ return mapper(dbSession).selectByAnalysisUuid(uuid);
+ }
+
+ public EventDto insert(DbSession session, EventDto dto) {
session.getMapper(EventMapper.class).insert(dto);
+
+ return dto;
}
public void delete(DbSession session, Long id) {
session.getMapper(EventMapper.class).delete(id);
}
+ private static EventMapper mapper(DbSession session) {
+ return session.getMapper(EventMapper.class);
+ }
}
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import static org.sonar.db.event.EventValidator.checkEventCategory;
+import static org.sonar.db.event.EventValidator.checkEventDescription;
+import static org.sonar.db.event.EventValidator.checkEventName;
+
public class EventDto {
public static final String CATEGORY_VERSION = "Version";
}
public EventDto setName(String name) {
- this.name = name;
+ this.name = checkEventName(name);
return this;
}
}
public EventDto setCategory(String category) {
- this.category = category;
+ this.category = checkEventCategory(category);
return this;
}
}
public EventDto setDescription(@Nullable String description) {
- this.description = description;
+ this.description = checkEventDescription(description);
return this;
}
public interface EventMapper {
- List<EventDto> selectByComponentUuid(String uuid);
+ List<EventDto> selectByComponentUuid(String componentUuid);
+
+ List<EventDto> selectByAnalysisUuid(String analysisUuid);
void insert(EventDto dto);
void delete(long id);
-
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.event;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class EventValidator {
+ private static final int MAX_NAME_LENGTH = 400;
+ private static final int MAX_CATEGORY_LENGTH = 50;
+ private static final int MAX_DESCRIPTION_LENGTH = 4000;
+
+ private EventValidator() {
+ // prevent instantiation
+ }
+
+ public static String checkEventName(String name) {
+ checkArgument(name.length() <= MAX_NAME_LENGTH, "Event name length (%s) is longer than the maximum authorized (%s). '%s' was provided.",
+ name.length(), MAX_NAME_LENGTH, name);
+ return name;
+ }
+
+ public static String checkEventCategory(String category) {
+ checkArgument(category.length() <= MAX_CATEGORY_LENGTH, "Event category length (%s) is longer than the maximum authorized (%s). '%s' was provided.",
+ category.length(), MAX_CATEGORY_LENGTH, category);
+ return category;
+ }
+
+ @CheckForNull
+ public static String checkEventDescription(@Nullable String description) {
+ if (description == null) {
+ return null;
+ }
+
+ checkArgument(description.length() <= MAX_DESCRIPTION_LENGTH, "Event description length (%s) is longer than the maximum authorized (%s). '%s' was provided.",
+ description.length(), MAX_DESCRIPTION_LENGTH, description);
+ return description;
+ }
+
+}
where uuid = #{analysisUuid}
</update>
+ <update id="updateVersion" parameterType="map">
+ update snapshots
+ set version = #{version, jdbcType=VARCHAR}
+ where uuid = #{analysisUuid}
+ </update>
+
<insert id="insert" parameterType="Snapshot" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
insert into snapshots (
uuid,
#{period4Date, jdbcType=BIGINT},
#{period5Date, jdbcType=BIGINT})
</insert>
-
</mapper>
</where>
</select>
+ <select id="selectByAnalysisUuid" parameterType="String" resultType="Event">
+ SELECT
+ <include refid="eventColumns"/>
+ FROM events e
+ <where>
+ AND e.analysis_uuid=#{analysisUuid}
+ </where>
+ </select>
+
<insert id="insert" parameterType="Event" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
INSERT INTO events (uuid, analysis_uuid, component_uuid, name, category, description, event_data, event_date, created_at)
VALUES (
import org.sonar.api.utils.log.Loggers;
import org.sonar.core.util.SequenceUuidFactory;
import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.event.EventDbTester;
import org.sonar.db.organization.OrganizationDbTester;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.organization.OrganizationTesting;
private final UserDbTester userTester;
private final ComponentDbTester componentTester;
+ private final EventDbTester eventTester;
private final OrganizationDbTester organizationTester;
private final PermissionTemplateDbTester permissionTemplateTester;
private final QualityGateDbTester qualityGateDbTester;
initDbClient();
this.userTester = new UserDbTester(this);
this.componentTester = new ComponentDbTester(this);
+ this.eventTester = new EventDbTester(this);
this.organizationTester = new OrganizationDbTester(this);
this.permissionTemplateTester = new PermissionTemplateDbTester(this);
this.qualityGateDbTester = new QualityGateDbTester(this);
return componentTester;
}
+ public EventDbTester events() {
+ return eventTester;
+ }
+
public OrganizationDbTester organizations() {
return organizationTester;
}
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED;
import static org.sonar.db.component.SnapshotQuery.SORT_FIELD.BY_DATE;
import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.ASC;
import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.DESC;
verifyStatusAndIsLastFlag("A3", SnapshotDto.STATUS_PROCESSED, true);
}
+ @Test
+ public void updateVersion() {
+ insertAnalysis("P1", "A1", STATUS_PROCESSED, true);
+ db.commit();
+
+ underTest.updateVersion(dbSession, "A1", "5.6.3");
+
+ SnapshotDto result = underTest.selectByUuid(dbSession, "A1").get();
+
+ assertThat(result.getVersion()).isEqualTo("5.6.3");
+ }
+
private void insertAnalysis(String projectUuid, String uuid, String status, boolean isLastFlag) {
SnapshotDto snapshot = SnapshotTesting.newAnalysis(ComponentTesting.newProjectDto(projectUuid))
.setLast(isLastFlag)
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.db.component.SnapshotTesting.newAnalysis;
+import static org.sonar.db.event.EventTesting.newEvent;
public class EventDaoTest {
public ExpectedException expectedException = ExpectedException.none();
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ private DbClient dbClient = dbTester.getDbClient();
+ private DbSession dbSession = dbTester.getSession();
EventDao underTest = dbTester.getDbClient().eventDao();
assertThat(dto.getCreatedAt()).isEqualTo(1225630680000L);
}
+ @Test
+ public void select_by_analysis_uuid() {
+ ComponentDto project = newProjectDto();
+ SnapshotDto analysis = dbTester.components().insertProjectAndSnapshot(project);
+ SnapshotDto otherAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(project));
+ dbTester.commit();
+ dbTester.events().insertEvent(newEvent(analysis).setUuid("A1"));
+ dbTester.events().insertEvent(newEvent(otherAnalysis).setUuid("O1"));
+ dbTester.events().insertEvent(newEvent(analysis).setUuid("A2"));
+ dbTester.events().insertEvent(newEvent(otherAnalysis).setUuid("O2"));
+ dbTester.events().insertEvent(newEvent(analysis).setUuid("A3"));
+ dbTester.events().insertEvent(newEvent(otherAnalysis).setUuid("O3"));
+
+ List<EventDto> result = underTest.selectByAnalysisUuid(dbSession, analysis.getUuid());
+
+ assertThat(result).hasSize(3);
+ assertThat(result).extracting(EventDto::getUuid).containsOnly("A1", "A2", "A3");
+ }
+
@Test
public void return_different_categories() {
dbTester.prepareDbUnit(getClass(), "shared.xml");
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.event;
+
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+
+public class EventDbTester {
+ private final DbTester db;
+ private final DbClient dbClient;
+ private final DbSession dbSession;
+
+ public EventDbTester(DbTester db) {
+ this.db = db;
+ this.dbClient = db.getDbClient();
+ this.dbSession = db.getSession();
+ }
+
+ public EventDto insertEvent(EventDto event) {
+ dbClient.eventDao().insert(dbSession, event);
+ db.commit();
+
+ return event;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.event;
+
+import org.sonar.db.component.SnapshotDto;
+
+import static java.util.Objects.requireNonNull;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+
+public class EventTesting {
+
+ public static EventDto newEvent(SnapshotDto analysis) {
+ requireNonNull(analysis.getUuid());
+ requireNonNull(analysis.getComponentUuid());
+
+ return new EventDto()
+ .setAnalysisUuid(analysis.getUuid())
+ .setComponentUuid(analysis.getComponentUuid())
+ .setUuid(randomAlphanumeric(40))
+ .setName(randomAlphanumeric(400))
+ .setDescription(randomAlphanumeric(400))
+ .setCategory(randomAlphanumeric(50))
+ .setComponentUuid(analysis.getComponentUuid())
+ .setCreatedAt(System.currentTimeMillis())
+ .setDate(System.currentTimeMillis());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.event;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static com.google.common.base.Strings.repeat;
+
+public class EventValidatorTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void valid_cases() {
+ EventValidator.checkEventName(repeat("a", 400));
+ EventValidator.checkEventCategory(repeat("a", 50));
+ EventValidator.checkEventDescription(repeat("a", 4000));
+ EventValidator.checkEventDescription(null);
+ }
+
+ @Test
+ public void fail_if_name_longer_than_400() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Event name length (401) is longer than the maximum authorized (400).");
+
+ EventValidator.checkEventName(repeat("a", 400 + 1));
+ }
+
+ @Test
+ public void fail_if_category_longer_than_50() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Event category length (51) is longer than the maximum authorized (50). 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' was provided.");
+
+ EventValidator.checkEventCategory(repeat("a", 50 + 1));
+ }
+
+ @Test
+ public void fail_if_description_longer_than_4000() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Event description length (4001) is longer than the maximum authorized (4000).");
+
+ EventValidator.checkEventDescription(repeat("a", 4000 + 1));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.sonarqube.ws.client.projectanalysis;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class CreateEventRequest {
+ private final String analysis;
+ private final Category category;
+ private final String name;
+ private final String description;
+
+ private CreateEventRequest(Builder builder) {
+ analysis = builder.analysis;
+ category = builder.category;
+ name = builder.name;
+ description = builder.description;
+ }
+
+ public String getAnalysis() {
+ return analysis;
+ }
+
+ public Category getCategory() {
+ return category;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @CheckForNull
+ public String getDescription() {
+ return description;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private String analysis;
+ private Category category = Category.OTHER;
+ private String name;
+ private String description;
+
+ private Builder() {
+ // enforce static factory method
+ }
+
+ public Builder setAnalysis(String analysis) {
+ this.analysis = analysis;
+ return this;
+ }
+
+ public Builder setCategory(Category category) {
+ this.category = category;
+ return this;
+ }
+
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder setDescription(@Nullable String description) {
+ this.description = description;
+ return this;
+ }
+
+ public CreateEventRequest build() {
+ checkArgument(analysis != null, "Analysis key is required");
+ checkArgument(category != null, "Category is required");
+ checkArgument(name != null, "Name is required");
+
+ return new CreateEventRequest(this);
+ }
+ }
+
+ public enum Category {
+ VERSION("Version"), OTHER("Other");
+
+ private final String label;
+
+ Category(String label) {
+ this.label = label;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public static Category fromLabel(String label) {
+ for (Category category : values()) {
+ if (category.getLabel().equals(label)) {
+ return category;
+ }
+ }
+
+ throw new IllegalArgumentException("Unknown event category label '" + label + "'");
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.sonarqube.ws.client.projectanalysis;
+
+public class ProjectAnalysesWsParameters {
+ public static final String PARAM_ANALYSIS = "analysis";
+ public static final String PARAM_CATEGORY = "category";
+ public static final String PARAM_NAME = "name";
+ public static final String PARAM_DESCRIPTION = "description";
+
+ private ProjectAnalysesWsParameters() {
+ // static access only
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.
+ */
+
+@ParametersAreNonnullByDefault
+package org.sonarqube.ws.client.projectanalysis;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
--- /dev/null
+// SonarQube, open source software quality management tool.
+// Copyright (C) 2008-2016 SonarSource
+// mailto:contact AT sonarsource DOT com
+//
+// SonarQube 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.
+//
+// SonarQube 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.
+
+syntax = "proto2";
+
+package sonarqube.ws.projectanalysis;
+
+option java_package = "org.sonarqube.ws";
+option java_outer_classname = "ProjectAnalyses";
+option optimize_for = SPEED;
+
+// WS api/project_analyses/create_event
+message CreateEventResponse {
+ optional Event event = 1;
+}
+
+message Event {
+ optional string key = 1;
+ optional string analysis = 2;
+ optional string category = 3;
+ optional string name = 4;
+ optional string description = 5;
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.sonarqube.ws.client.projectanalysis;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonarqube.ws.client.projectanalysis.CreateEventRequest.Category.OTHER;
+import static org.sonarqube.ws.client.projectanalysis.CreateEventRequest.Category.VERSION;
+
+public class CreateEventRequestTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private CreateEventRequest.Builder underTest = CreateEventRequest.builder();
+
+ @Test
+ public void build_request() {
+ CreateEventRequest result = underTest.setAnalysis("P1").setCategory(OTHER).setName("name").setDescription("description").build();
+
+ assertThat(result.getAnalysis()).isEqualTo("P1");
+ assertThat(result.getCategory()).isEqualTo(OTHER);
+ assertThat(result.getName()).isEqualTo("name");
+ assertThat(result.getDescription()).isEqualTo("description");
+ }
+
+ @Test
+ public void other_category_by_default() {
+ CreateEventRequest result = underTest.setAnalysis("P1").setName("name").build();
+
+ assertThat(OTHER.equals(result.getCategory()));
+ }
+
+ @Test
+ public void fail_when_no_category() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Category is required");
+
+ underTest.setAnalysis("P1").setName("name").setCategory(null).build();
+ }
+
+ @Test
+ public void fail_when_no_analysis() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Analysis key is required");
+
+ underTest.setCategory(VERSION).setName("name").build();
+ }
+
+ @Test
+ public void fail_when_no_name() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Name is required");
+
+ underTest.setAnalysis("P1").setCategory(VERSION).build();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.sonarqube.ws.client.projectanalysis;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.test.TestUtils.hasOnlyPrivateConstructors;
+
+public class ProjectAnalysesWsParametersTest {
+
+ @Test
+ public void private_access_only() {
+ assertThat(hasOnlyPrivateConstructors(ProjectAnalysesWsParameters.class)).isTrue();
+ }
+}