Browse Source

SONAR-8464 Create WS api/project_analyses/create_event

tags/6.3-RC1
Teryk Bellahsene 7 years ago
parent
commit
fbb33db724
31 changed files with 1410 additions and 10 deletions
  1. 3
    3
      server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
  2. 4
    0
      server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
  3. 37
    0
      server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ProjectAnalysisModule.java
  4. 25
    0
      server/sonar-server/src/main/java/org/sonar/server/projectanalysis/package-info.java
  5. 214
    0
      server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/CreateEventAction.java
  6. 45
    0
      server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/ProjectAnalysesWs.java
  7. 27
    0
      server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/ProjectAnalysesWsAction.java
  8. 25
    0
      server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/package-info.java
  9. 9
    0
      server/sonar-server/src/main/resources/org/sonar/server/projectanalysis/ws/create_event-example.json
  10. 36
    0
      server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ProjectAnalysisModuleTest.java
  11. 351
    0
      server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/CreateEventActionTest.java
  12. 4
    0
      sonar-db/src/main/java/org/sonar/db/component/SnapshotDao.java
  13. 2
    0
      sonar-db/src/main/java/org/sonar/db/component/SnapshotMapper.java
  14. 10
    1
      sonar-db/src/main/java/org/sonar/db/event/EventDao.java
  15. 7
    3
      sonar-db/src/main/java/org/sonar/db/event/EventDto.java
  16. 3
    2
      sonar-db/src/main/java/org/sonar/db/event/EventMapper.java
  17. 60
    0
      sonar-db/src/main/java/org/sonar/db/event/EventValidator.java
  18. 6
    1
      sonar-db/src/main/resources/org/sonar/db/component/SnapshotMapper.xml
  19. 9
    0
      sonar-db/src/main/resources/org/sonar/db/event/EventMapper.xml
  20. 7
    0
      sonar-db/src/test/java/org/sonar/db/DbTester.java
  21. 13
    0
      sonar-db/src/test/java/org/sonar/db/component/SnapshotDaoTest.java
  22. 28
    0
      sonar-db/src/test/java/org/sonar/db/event/EventDaoTest.java
  23. 45
    0
      sonar-db/src/test/java/org/sonar/db/event/EventDbTester.java
  24. 45
    0
      sonar-db/src/test/java/org/sonar/db/event/EventTesting.java
  25. 64
    0
      sonar-db/src/test/java/org/sonar/db/event/EventValidatorTest.java
  26. 124
    0
      sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalysis/CreateEventRequest.java
  27. 32
    0
      sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalysis/ProjectAnalysesWsParameters.java
  28. 25
    0
      sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalysis/package-info.java
  29. 38
    0
      sonar-ws/src/main/protobuf/ws-projectanalyses.proto
  30. 78
    0
      sonar-ws/src/test/java/org/sonarqube/ws/client/projectanalysis/CreateEventRequestTest.java
  31. 34
    0
      sonar-ws/src/test/java/org/sonarqube/ws/client/projectanalysis/ProjectAnalysesWsParametersTest.java

+ 3
- 3
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java View File

@@ -121,9 +121,9 @@ public class ComponentTreeAction implements MeasuresWsAction {
.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))

+ 4
- 0
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java View File

@@ -148,6 +148,7 @@ import org.sonar.server.plugins.ws.PluginsWs;
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;
@@ -490,6 +491,9 @@ public class PlatformLevel4 extends PlatformLevel {
// Project Links
ProjectLinksModule.class,

// Project Analyses
ProjectAnalysisModule.class,

// System
ServerLogging.class,
RestartAction.class,

+ 37
- 0
server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ProjectAnalysisModule.java View File

@@ -0,0 +1,37 @@
/*
* 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);
}

}

+ 25
- 0
server/sonar-server/src/main/java/org/sonar/server/projectanalysis/package-info.java View File

@@ -0,0 +1,25 @@
/*
* 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;


+ 214
- 0
server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/CreateEventAction.java View File

@@ -0,0 +1,214 @@
/*
* 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());
}
}
}

+ 45
- 0
server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/ProjectAnalysesWs.java View File

@@ -0,0 +1,45 @@
/*
* 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();
}
}

+ 27
- 0
server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/ProjectAnalysesWsAction.java View File

@@ -0,0 +1,27 @@
/*
* 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
}

+ 25
- 0
server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/package-info.java View File

@@ -0,0 +1,25 @@
/*
* 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;


+ 9
- 0
server/sonar-server/src/main/resources/org/sonar/server/projectanalysis/ws/create_event-example.json View File

@@ -0,0 +1,9 @@
{
"event": {
"analysis": "A2",
"key": "E1",
"category": "OTHER",
"name": "My Custom Event",
"description": "event description"
}
}

+ 36
- 0
server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ProjectAnalysisModuleTest.java View File

@@ -0,0 +1,36 @@
/*
* 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);
}
}

+ 351
- 0
server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/CreateEventActionTest.java View File

@@ -0,0 +1,351 @@
/*
* 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);
}
}
}

+ 4
- 0
sonar-db/src/main/java/org/sonar/db/component/SnapshotDao.java View File

@@ -138,6 +138,10 @@ public class SnapshotDao implements Dao {
insert(session, Lists.asList(item, others));
}

public void updateVersion(DbSession dbSession, String analysisUuid, String version) {
mapper(dbSession).updateVersion(analysisUuid, version);
}

/**
* Used by Governance
*/

+ 2
- 0
sonar-db/src/main/java/org/sonar/db/component/SnapshotMapper.java View File

@@ -55,4 +55,6 @@ public interface SnapshotMapper {
void unsetIsLastFlagForComponentUuid(@Param("componentUuid") String componentUuid);

void setIsLastFlagForAnalysisUuid(@Param("analysisUuid") String analysisUuid);

void updateVersion(@Param("analysisUuid") String analysisUuid, @Param("version") String version);
}

+ 10
- 1
sonar-db/src/main/java/org/sonar/db/event/EventDao.java View File

@@ -29,12 +29,21 @@ public class EventDao implements Dao {
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);
}
}

+ 7
- 3
sonar-db/src/main/java/org/sonar/db/event/EventDto.java View File

@@ -22,6 +22,10 @@ package org.sonar.db.event;
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";
@@ -80,7 +84,7 @@ public class EventDto {
}

public EventDto setName(String name) {
this.name = name;
this.name = checkEventName(name);
return this;
}

@@ -89,7 +93,7 @@ public class EventDto {
}

public EventDto setCategory(String category) {
this.category = category;
this.category = checkEventCategory(category);
return this;
}

@@ -127,7 +131,7 @@ public class EventDto {
}

public EventDto setDescription(@Nullable String description) {
this.description = description;
this.description = checkEventDescription(description);
return this;
}


+ 3
- 2
sonar-db/src/main/java/org/sonar/db/event/EventMapper.java View File

@@ -23,10 +23,11 @@ import java.util.List;

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);

}

+ 60
- 0
sonar-db/src/main/java/org/sonar/db/event/EventValidator.java View File

@@ -0,0 +1,60 @@
/*
* 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;
}

}

+ 6
- 1
sonar-db/src/main/resources/org/sonar/db/component/SnapshotMapper.xml View File

@@ -177,6 +177,12 @@
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,
@@ -227,6 +233,5 @@
#{period4Date, jdbcType=BIGINT},
#{period5Date, jdbcType=BIGINT})
</insert>

</mapper>


+ 9
- 0
sonar-db/src/main/resources/org/sonar/db/event/EventMapper.xml View File

@@ -24,6 +24,15 @@
</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 (

+ 7
- 0
sonar-db/src/test/java/org/sonar/db/DbTester.java View File

@@ -65,6 +65,7 @@ import org.sonar.api.utils.System2;
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;
@@ -99,6 +100,7 @@ public class DbTester extends ExternalResource {

private final UserDbTester userTester;
private final ComponentDbTester componentTester;
private final EventDbTester eventTester;
private final OrganizationDbTester organizationTester;
private final PermissionTemplateDbTester permissionTemplateTester;
private final QualityGateDbTester qualityGateDbTester;
@@ -110,6 +112,7 @@ public class DbTester extends ExternalResource {
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);
@@ -177,6 +180,10 @@ public class DbTester extends ExternalResource {
return componentTester;
}

public EventDbTester events() {
return eventTester;
}

public OrganizationDbTester organizations() {
return organizationTester;
}

+ 13
- 0
sonar-db/src/test/java/org/sonar/db/component/SnapshotDaoTest.java View File

@@ -35,6 +35,7 @@ import static com.google.common.collect.Lists.newArrayList;
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;
@@ -296,6 +297,18 @@ public class SnapshotDaoTest {
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)

+ 28
- 0
sonar-db/src/test/java/org/sonar/db/event/EventDaoTest.java View File

@@ -24,9 +24,16 @@ import org.junit.Rule;
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 {

@@ -34,6 +41,8 @@ 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();

@@ -60,6 +69,25 @@ public class EventDaoTest {
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");

+ 45
- 0
sonar-db/src/test/java/org/sonar/db/event/EventDbTester.java View File

@@ -0,0 +1,45 @@
/*
* 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;
}

}

+ 45
- 0
sonar-db/src/test/java/org/sonar/db/event/EventTesting.java View File

@@ -0,0 +1,45 @@
/*
* 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());
}
}

+ 64
- 0
sonar-db/src/test/java/org/sonar/db/event/EventValidatorTest.java View File

@@ -0,0 +1,64 @@
/*
* 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));
}
}

+ 124
- 0
sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalysis/CreateEventRequest.java View File

@@ -0,0 +1,124 @@
/*
* 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 + "'");
}
}
}

+ 32
- 0
sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalysis/ProjectAnalysesWsParameters.java View File

@@ -0,0 +1,32 @@
/*
* 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
}
}

+ 25
- 0
sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalysis/package-info.java View File

@@ -0,0 +1,25 @@
/*
* 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;


+ 38
- 0
sonar-ws/src/main/protobuf/ws-projectanalyses.proto View File

@@ -0,0 +1,38 @@
// 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;
}

+ 78
- 0
sonar-ws/src/test/java/org/sonarqube/ws/client/projectanalysis/CreateEventRequestTest.java View File

@@ -0,0 +1,78 @@
/*
* 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();
}
}

+ 34
- 0
sonar-ws/src/test/java/org/sonarqube/ws/client/projectanalysis/ProjectAnalysesWsParametersTest.java View File

@@ -0,0 +1,34 @@
/*
* 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();
}
}

Loading…
Cancel
Save