]> source.dussan.org Git - sonarqube.git/commitdiff
WS api/projects/ghosts list ghost projects - SONAR-6527
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Mon, 11 May 2015 12:16:25 +0000 (14:16 +0200)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 15 May 2015 13:21:23 +0000 (15:21 +0200)
20 files changed:
server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentDao.java
server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsGhostsAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsProvisionedAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsWs.java
server/sonar-server/src/main/java/org/sonar/server/component/ws/ProvisionedProjectsAction.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/resources/org/sonar/server/component/ws/projects-example-ghosts.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentDaoTest.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsGhostsActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsProvisionedActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsWsTest.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/ProvisionedProjectsActionTest.java [deleted file]
server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/select_ghost_projects.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsGhostsActionTest/all-projects.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsGhostsActionTest/pagination.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsProvisionedActionTest/all-projects.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProvisionedProjectsActionTest/all-projects.json [deleted file]
sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java
sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java

index a57572426b65fe59d4b72b129d42fff66f8c6740..eeb893b1cfb8daddc9ef11febc32f94dae8bdb25 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.server.component.db;
 
 import com.google.common.base.Function;
 import org.apache.ibatis.session.RowBounds;
-import org.sonar.api.ServerComponent;
 import org.sonar.api.ServerSide;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Scopes;
@@ -40,13 +39,13 @@ import org.sonar.server.exceptions.NotFoundException;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
-
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static com.google.common.collect.Maps.newHashMapWithExpectedSize;
+
 /**
  * @since 4.3
  */
@@ -176,20 +175,44 @@ public class ComponentDao extends BaseDao<ComponentMapper, ComponentDto, String>
   }
 
   public List<ComponentDto> selectProvisionedProjects(DbSession session, SearchOptions searchOptions, @Nullable String query) {
-    Map<String, String> parameters = new HashMap<>();
-    parameters.put("qualifier", Qualifiers.PROJECT);
-    if (query != null) {
-      parameters.put("query", "%" + query + "%");
-    }
+    Map<String, String> parameters = newHashMapWithExpectedSize(2);
+    addProjectQualifier(parameters);
+    addPartialQueryParameterIfNotNull(parameters, query);
+
     return mapper(session).selectProvisionedProjects(parameters, new RowBounds(searchOptions.getOffset(), searchOptions.getLimit()));
   }
 
   public int countProvisionedProjects(DbSession session, @Nullable String query) {
-    Map<String, String> parameters = new HashMap<>();
-    parameters.put("qualifier", Qualifiers.PROJECT);
+    Map<String, String> parameters = newHashMapWithExpectedSize(2);
+    addProjectQualifier(parameters);
+    addPartialQueryParameterIfNotNull(parameters, query);
+
+    return mapper(session).countProvisionedProjects(parameters);
+  }
+
+  public List<ComponentDto> selectGhostProjects(DbSession session, @Nullable String query, SearchOptions options) {
+    Map<String, String> parameters = newHashMapWithExpectedSize(2);
+    addProjectQualifier(parameters);
+    addPartialQueryParameterIfNotNull(parameters, query);
+
+    return mapper(session).selectGhostProjects(parameters, new RowBounds(options.getOffset(), options.getLimit()));
+  }
+
+  public long countGhostProjects(DbSession session, @Nullable String query) {
+    Map<String, String> parameters = newHashMapWithExpectedSize(2);
+    addProjectQualifier(parameters);
+    addPartialQueryParameterIfNotNull(parameters, query);
+
+    return mapper(session).countGhostProjects(parameters);
+  }
+
+  private void addPartialQueryParameterIfNotNull(Map<String, String> parameters, @Nullable String query) {
     if (query != null) {
-      parameters.put("query", "%" + query + "%");
+      parameters.put("query", "%" + query.toUpperCase() + "%");
     }
-    return mapper(session).countProvisionedProjects(parameters);
+  }
+
+  private void addProjectQualifier(Map<String, String> parameters) {
+    parameters.put("qualifier", Qualifiers.PROJECT);
   }
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsGhostsAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsGhostsAction.java
new file mode 100644 (file)
index 0000000..86e488f
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+package org.sonar.server.component.ws;
+
+import com.google.common.io.Resources;
+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.server.ws.WebService.Param;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.es.SearchOptions;
+import org.sonar.server.user.UserSession;
+
+import javax.annotation.Nullable;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newHashSet;
+
+public class ProjectsGhostsAction implements ProjectsWsAction {
+  public static final String ACTION = "ghosts";
+  private static final Set<String> POSSIBLE_FIELDS = newHashSet("uuid", "key", "name", "creationDate");
+
+  private final DbClient dbClient;
+  private final UserSession userSession;
+
+  public ProjectsGhostsAction(DbClient dbClient, UserSession userSession) {
+    this.dbClient = dbClient;
+    this.userSession = userSession;
+  }
+
+  @Override
+  public void define(WebService.NewController context) {
+    context
+      .createAction(ACTION)
+      .setDescription("List ghost projects.<br /> Requires admin role.")
+      .setResponseExample(Resources.getResource(getClass(), "projects-example-ghosts.json"))
+      .setSince("5.2")
+      .addPagingParams(100)
+      .addFieldsParam(POSSIBLE_FIELDS)
+      .addSearchQuery("sonar", "names", "keys")
+      .setHandler(this);
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    userSession.checkGlobalPermission(UserRole.ADMIN);
+    DbSession dbSession = dbClient.openSession(false);
+    SearchOptions searchOptions = new SearchOptions()
+      .setPage(request.mandatoryParamAsInt(Param.PAGE),
+        request.mandatoryParamAsInt(Param.PAGE_SIZE));
+    Set<String> desiredFields = fieldsToReturn(request.paramAsStrings(Param.FIELDS));
+    String query = request.param(Param.TEXT_QUERY);
+
+    try {
+      long nbOfProjects = dbClient.componentDao().countGhostProjects(dbSession, query);
+      List<ComponentDto> projects = dbClient.componentDao().selectGhostProjects(dbSession, query, searchOptions);
+      JsonWriter json = response.newJsonWriter().beginObject();
+      writeProjects(json, projects, desiredFields);
+      searchOptions.writeJson(json, nbOfProjects);
+      json.endObject().close();
+    } finally {
+      MyBatis.closeQuietly(dbSession);
+    }
+  }
+
+  private void writeProjects(JsonWriter json, List<ComponentDto> projects, Set<String> fieldsToReturn) {
+    json.name("projects");
+    json.beginArray();
+    for (ComponentDto project : projects) {
+      json.beginObject();
+      json.prop("uuid", project.uuid());
+      writeIfWished(json, "key", project.key(), fieldsToReturn);
+      writeIfWished(json, "name", project.name(), fieldsToReturn);
+      writeIfWished(json, "creationDate", project.getCreatedAt(), fieldsToReturn);
+      json.endObject();
+    }
+    json.endArray();
+  }
+
+  private void writeIfWished(JsonWriter json, String key, String value, Set<String> fieldsToReturn) {
+    if (fieldsToReturn.contains(key)) {
+      json.prop(key, value);
+    }
+  }
+
+  private void writeIfWished(JsonWriter json, String key, Date value, Set<String> desiredFields) {
+    if (desiredFields.contains(key)) {
+      json.propDateTime(key, value);
+    }
+  }
+
+  private Set<String> fieldsToReturn(@Nullable List<String> desiredFieldsFromRequest) {
+    if (desiredFieldsFromRequest == null) {
+      return POSSIBLE_FIELDS;
+    }
+
+    return newHashSet(desiredFieldsFromRequest);
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsProvisionedAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ProjectsProvisionedAction.java
new file mode 100644 (file)
index 0000000..56954c4
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+package org.sonar.server.component.ws;
+
+import com.google.common.io.Resources;
+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.server.ws.WebService.Param;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.es.SearchOptions;
+import org.sonar.server.user.UserSession;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newHashSet;
+
+public class ProjectsProvisionedAction implements ProjectsWsAction {
+  private static final Set<String> POSSIBLE_FIELDS = newHashSet("uuid", "key", "name", "creationDate");
+
+  private final DbClient dbClient;
+  private final UserSession userSession;
+
+  public ProjectsProvisionedAction(DbClient dbClient, UserSession userSession) {
+    this.dbClient = dbClient;
+    this.userSession = userSession;
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    controller
+      .createAction("provisioned")
+      .setDescription(
+        "Get the list of provisioned projects.<br /> " +
+          "Require admin role.")
+      .setSince("5.2")
+      .setResponseExample(Resources.getResource(getClass(), "projects-example-provisioned.json"))
+      .setHandler(this)
+      .addPagingParams(100)
+      .addSearchQuery("sonar", "names", "keys")
+      .addFieldsParam(POSSIBLE_FIELDS);
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    userSession.checkGlobalPermission(UserRole.ADMIN, "You need admin rights.");
+    SearchOptions options = new SearchOptions().setPage(
+      request.mandatoryParamAsInt(Param.PAGE),
+      request.mandatoryParamAsInt(Param.PAGE_SIZE)
+      );
+    Set<String> desiredFields = desiredFields(request);
+    String query = request.param(Param.TEXT_QUERY);
+
+    DbSession dbSession = dbClient.openSession(false);
+    try {
+      List<ComponentDto> projects = dbClient.componentDao().selectProvisionedProjects(dbSession, options, query);
+      int nbOfProjects = dbClient.componentDao().countProvisionedProjects(dbSession, query);
+      JsonWriter json = response.newJsonWriter().beginObject();
+      writeProjects(projects, json, desiredFields);
+      options.writeJson(json, nbOfProjects);
+      json.endObject().close();
+    } finally {
+      MyBatis.closeQuietly(dbSession);
+    }
+  }
+
+  private void writeProjects(List<ComponentDto> projects, JsonWriter json, Set<String> desiredFields) {
+    json.name("projects");
+    json.beginArray();
+    for (ComponentDto project : projects) {
+      json.beginObject();
+      json.prop("uuid", project.uuid());
+      writeIfNeeded(json, "key", project.key(), desiredFields);
+      writeIfNeeded(json, "name", project.name(), desiredFields);
+      writeIfNeeded(json, "creationDate", project.getCreatedAt(), desiredFields);
+      json.endObject();
+    }
+    json.endArray();
+  }
+
+  private void writeIfNeeded(JsonWriter json, String fieldName, String value, Set<String> desiredFields) {
+    if (desiredFields.contains(fieldName)) {
+      json.prop(fieldName, value);
+    }
+  }
+
+  private void writeIfNeeded(JsonWriter json, String fieldName, Date date, Set<String> desiredFields) {
+    if (desiredFields.contains(fieldName)) {
+      json.propDateTime(fieldName, date);
+    }
+  }
+
+  private Set<String> desiredFields(Request request) {
+    List<String> desiredFields = request.paramAsStrings(Param.FIELDS);
+    if (desiredFields == null) {
+      return POSSIBLE_FIELDS;
+    }
+
+    return newHashSet(desiredFields);
+  }
+}
index 13ab2556780103e22650204979e581ab9287f2d5..4b6e9e1050146f6fbb87ae1f2436446473685a77 100644 (file)
@@ -25,6 +25,8 @@ import org.sonar.api.server.ws.RailsHandler;
 import org.sonar.api.server.ws.WebService;
 
 public class ProjectsWs implements WebService {
+  private static final String ENDPOINT = "api/projects";
+
   private final ProjectsWsAction[] actions;
 
   public ProjectsWs(ProjectsWsAction... actions) {
@@ -33,7 +35,7 @@ public class ProjectsWs implements WebService {
 
   @Override
   public void define(Context context) {
-    NewController controller = context.createController("api/projects")
+    NewController controller = context.createController(ENDPOINT)
       .setSince("2.10")
       .setDescription("Projects management");
 
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ProvisionedProjectsAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ProvisionedProjectsAction.java
deleted file mode 100644 (file)
index 9abbd5c..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.
- */
-
-package org.sonar.server.component.ws;
-
-import com.google.common.io.Resources;
-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.server.ws.WebService.Param;
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.api.web.UserRole;
-import org.sonar.core.component.ComponentDto;
-import org.sonar.core.persistence.DbSession;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.server.db.DbClient;
-import org.sonar.server.es.SearchOptions;
-import org.sonar.server.user.UserSession;
-
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-
-public class ProvisionedProjectsAction implements ProjectsWsAction {
-  private static final List<String> POSSIBLE_FIELDS = Arrays.asList("uuid", "key", "name", "creationDate");
-
-  private final DbClient dbClient;
-  private final UserSession userSession;
-
-  public ProvisionedProjectsAction(DbClient dbClient, UserSession userSession) {
-    this.dbClient = dbClient;
-    this.userSession = userSession;
-  }
-
-  @Override
-  public void define(WebService.NewController controller) {
-    WebService.NewAction action = controller
-      .createAction("provisioned")
-      .setDescription(
-        "Get the list of provisioned projects.<br /> " +
-          "Require admin role.")
-      .setSince("5.2")
-      .setResponseExample(Resources.getResource(getClass(), "projects-example-provisioned.json"))
-      .setHandler(this)
-      .addPagingParams(100)
-      .addFieldsParam(POSSIBLE_FIELDS);
-
-    action
-      .createParam(Param.TEXT_QUERY)
-      .setDescription("UTF-8 search query")
-      .setExampleValue("sonar");
-  }
-
-  @Override
-  public void handle(Request request, Response response) throws Exception {
-    userSession.checkGlobalPermission(UserRole.ADMIN, "You need admin rights.");
-    SearchOptions options = new SearchOptions().setPage(
-      request.mandatoryParamAsInt(Param.PAGE),
-      request.mandatoryParamAsInt(Param.PAGE_SIZE)
-    );
-    List<String> desiredFields = desiredFields(request);
-    String query = request.param(Param.TEXT_QUERY);
-
-    DbSession dbSession = dbClient.openSession(false);
-    try {
-      List<ComponentDto> projects = dbClient.componentDao().selectProvisionedProjects(dbSession, options, query);
-      int nbOfProjects = dbClient.componentDao().countProvisionedProjects(dbSession, query);
-      JsonWriter json = response.newJsonWriter().beginObject();
-      writeProjects(projects, json, desiredFields);
-      options.writeJson(json, nbOfProjects);
-      json.endObject().close();
-    } finally {
-      MyBatis.closeQuietly(dbSession);
-    }
-  }
-
-  private void writeProjects(List<ComponentDto> projects, JsonWriter json, List<String> desiredFields) {
-    json.name("projects");
-    json.beginArray();
-    for (ComponentDto project : projects) {
-      json.beginObject();
-      json.prop("uuid", project.uuid());
-      writeIfNeeded(json, "key", project.key(), desiredFields);
-      writeIfNeeded(json, "name", project.name(), desiredFields);
-      writeIfNeeded(json, "creationDate", project.getCreatedAt(), desiredFields);
-      json.endObject();
-    }
-    json.endArray();
-  }
-
-  private void writeIfNeeded(JsonWriter json, String fieldName, String value, List<String> desiredFields) {
-    if (desiredFields.contains(fieldName)) {
-      json.prop(fieldName, value);
-    }
-  }
-
-  private void writeIfNeeded(JsonWriter json, String fieldName, Date date, List<String> desiredFields) {
-    if (desiredFields.contains(fieldName)) {
-      json.propDateTime(fieldName, date);
-    }
-  }
-
-  private List<String> desiredFields(Request request) {
-    List<String> desiredFields = request.paramAsStrings(Param.FIELDS);
-    if (desiredFields == null) {
-      desiredFields = POSSIBLE_FIELDS;
-    }
-
-    return desiredFields;
-  }
-}
index cfaa9efc252815dffcfafc5321ee8058cf980814..2dac82758dc1808d0b852199d96a160b004175a8 100644 (file)
@@ -109,8 +109,9 @@ import org.sonar.server.component.db.SnapshotDao;
 import org.sonar.server.component.ws.ComponentAppAction;
 import org.sonar.server.component.ws.ComponentsWs;
 import org.sonar.server.component.ws.EventsWs;
+import org.sonar.server.component.ws.ProjectsGhostsAction;
 import org.sonar.server.component.ws.ProjectsWs;
-import org.sonar.server.component.ws.ProvisionedProjectsAction;
+import org.sonar.server.component.ws.ProjectsProvisionedAction;
 import org.sonar.server.component.ws.ResourcesWs;
 import org.sonar.server.computation.ComputationThreadLauncher;
 import org.sonar.server.computation.ReportQueue;
@@ -801,7 +802,8 @@ class ServerComponents {
     pico.addSingleton(org.sonar.server.component.ws.SearchAction.class);
     pico.addSingleton(EventsWs.class);
     pico.addSingleton(ComponentCleanerService.class);
-    pico.addSingleton(ProvisionedProjectsAction.class);
+    pico.addSingleton(ProjectsProvisionedAction.class);
+    pico.addSingleton(ProjectsGhostsAction.class);
 
     // views
     pico.addSingleton(ViewIndexDefinition.class);
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/component/ws/projects-example-ghosts.json b/server/sonar-server/src/main/resources/org/sonar/server/component/ws/projects-example-ghosts.json
new file mode 100644 (file)
index 0000000..4bb9f84
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "projects": [
+    {
+      "uuid": "ce4c03d6-430f-40a9-b777-ad877c00aa4d",
+      "key": "org.apache.hbas:hbase",
+      "name": "HBase",
+      "creationDate": "2015-03-04T23:03:44+0100"
+    },
+    {
+      "uuid": "c526ef20-131b-4486-9357-063fa64b5079",
+      "key": "com.microsoft.roslyn:roslyn",
+      "name": "Roslyn",
+      "creationDate": "2013-03-04T23:03:44+0100"
+    }
+  ],
+  "total": 2,
+  "p": 1,
+  "ps": 100
+}
index ff3d0b1fecbb6b4483c22bba9f21012fb08c2248..4c784d43ed5950544a8b5bf2bb3f9127c23260c1 100644 (file)
@@ -575,4 +575,15 @@ public class ComponentDaoTest {
 
     assertThat(numberOfProjects).isEqualTo(1);
   }
+
+  @Test
+  public void select_ghost_projects() throws Exception {
+    db.prepareDbUnit(getClass(), "select_ghost_projects.xml");
+
+    List<ComponentDto> result = sut.selectGhostProjects(session, null, new SearchOptions());
+
+    assertThat(result).hasSize(1);
+    assertThat(result.get(0).key()).isEqualTo("org.ghost.project");
+    assertThat(sut.countGhostProjects(session, null)).isEqualTo(1);
+  }
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsGhostsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsGhostsActionTest.java
new file mode 100644 (file)
index 0000000..469f9dc
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+package org.sonar.server.component.ws;
+
+import com.google.common.io.Resources;
+import org.apache.commons.lang.StringUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.component.SnapshotDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.component.ComponentTesting;
+import org.sonar.server.component.SnapshotTesting;
+import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.component.db.SnapshotDao;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsTester;
+import org.sonar.test.JsonAssert;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectsGhostsActionTest {
+
+  @ClassRule
+  public static DbTester db = new DbTester();
+  @Rule
+  public UserSessionRule userSessionRule = UserSessionRule.standalone();
+  WsTester ws;
+
+  DbClient dbClient;
+  DbSession dbSession;
+
+  @Before
+  public void setUp() {
+    dbClient = new DbClient(db.database(), db.myBatis(), new ComponentDao(), new SnapshotDao(System2.INSTANCE));
+    dbSession = dbClient.openSession(false);
+    ws = new WsTester(new ProjectsWs(new ProjectsGhostsAction(dbClient, userSessionRule)));
+    db.truncateTables();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    dbSession.close();
+  }
+
+  @Test
+  public void ghost_projects_without_analyzed_projects() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
+    insertNewGhostProject("1");
+    insertNewGhostProject("2");
+    insertNewActiveProject("3");
+
+    WsTester.Result result = ws.newGetRequest("api/projects", "ghosts").execute();
+
+    result.assertJson(getClass(), "all-projects.json");
+    assertThat(result.outputAsString()).doesNotContain("analyzed-uuid-3");
+  }
+
+  @Test
+  public void ghost_projects_with_correct_pagination() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
+    for (int i = 1; i <= 10; i++) {
+      insertNewGhostProject(String.valueOf(i));
+    }
+
+    WsTester.Result result = ws.newGetRequest("api/projects", "ghosts")
+      .setParam(Param.PAGE, "3")
+      .setParam(Param.PAGE_SIZE, "4")
+      .execute();
+
+    result.assertJson(getClass(), "pagination.json");
+    assertThat(StringUtils.countMatches(result.outputAsString(), "ghost-uuid-")).isEqualTo(2);
+  }
+
+  @Test
+  public void ghost_projects_with_chosen_fields() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
+    insertNewGhostProject("1");
+
+    WsTester.Result result = ws.newGetRequest("api/projects", "ghosts")
+      .setParam(Param.FIELDS, "name")
+      .execute();
+
+    assertThat(result.outputAsString()).contains("uuid", "name")
+      .doesNotContain("key")
+      .doesNotContain("creationDate");
+  }
+
+  @Test
+  public void ghost_projects_with_partial_query_on_name() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
+
+    insertNewGhostProject("10");
+    insertNewGhostProject("11");
+    insertNewGhostProject("2");
+
+    WsTester.Result result = ws.newGetRequest("api/projects", "ghosts")
+      .setParam(Param.TEXT_QUERY, "name-1")
+      .execute();
+
+    assertThat(result.outputAsString()).contains("ghost-name-10", "ghost-name-11")
+      .doesNotContain("ghost-name-2");
+  }
+
+  @Test
+  public void ghost_projects_with_partial_query_on_key() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
+
+    insertNewGhostProject("1");
+
+    WsTester.Result result = ws.newGetRequest("api/projects", "ghosts")
+      .setParam(Param.TEXT_QUERY, "GHOST-key")
+      .execute();
+
+    assertThat(result.outputAsString()).contains("ghost-key-1");
+  }
+
+  @Test
+  public void ghost_projects_base_on_json_example() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
+    ComponentDto hBaseProject = ComponentTesting.newProjectDto("ce4c03d6-430f-40a9-b777-ad877c00aa4d")
+      .setKey("org.apache.hbas:hbase")
+      .setName("HBase")
+      .setCreatedAt(DateUtils.parseDateTime("2015-03-04T23:03:44+0100"));
+    hBaseProject = dbClient.componentDao().insert(dbSession, hBaseProject);
+    dbClient.snapshotDao().insert(dbSession, SnapshotTesting.createForProject(hBaseProject)
+      .setStatus(SnapshotDto.STATUS_UNPROCESSED));
+    ComponentDto roslynProject = ComponentTesting.newProjectDto("c526ef20-131b-4486-9357-063fa64b5079")
+      .setKey("com.microsoft.roslyn:roslyn")
+      .setName("Roslyn")
+      .setCreatedAt(DateUtils.parseDateTime("2013-03-04T23:03:44+0100"));
+    roslynProject = dbClient.componentDao().insert(dbSession, roslynProject);
+    dbClient.snapshotDao().insert(dbSession, SnapshotTesting.createForProject(roslynProject)
+      .setStatus(SnapshotDto.STATUS_UNPROCESSED));
+    dbSession.commit();
+
+    WsTester.Result result = ws.newGetRequest("api/projects", "ghosts").execute();
+
+    JsonAssert.assertJson(result.outputAsString()).isSimilarTo(Resources.getResource(getClass(), "projects-example-ghosts.json"));
+  }
+
+  @Test(expected = ForbiddenException.class)
+  public void fail_if_does_not_have_sufficient_rights() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.USER, UserRole.ISSUE_ADMIN, UserRole.CODEVIEWER);
+
+    ws.newGetRequest("api/projects", "ghosts").execute();
+  }
+
+  private void insertNewGhostProject(String id) {
+    ComponentDto project = ComponentTesting
+      .newProjectDto("ghost-uuid-" + id)
+      .setName("ghost-name-" + id)
+      .setKey("ghost-key-" + id);
+    project = dbClient.componentDao().insert(dbSession, project);
+    SnapshotDto snapshot = SnapshotTesting.createForProject(project)
+      .setStatus(SnapshotDto.STATUS_UNPROCESSED);
+    dbClient.snapshotDao().insert(dbSession, snapshot);
+    dbSession.commit();
+  }
+
+  private void insertNewActiveProject(String id) {
+    ComponentDto project = ComponentTesting
+      .newProjectDto("analyzed-uuid-" + id)
+      .setName("analyzed-name-" + id)
+      .setKey("analyzed-key-" + id);
+    project = dbClient.componentDao().insert(dbSession, project);
+    SnapshotDto snapshot = SnapshotTesting.createForProject(project);
+    dbClient.snapshotDao().insert(dbSession, snapshot);
+    dbSession.commit();
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsProvisionedActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProjectsProvisionedActionTest.java
new file mode 100644 (file)
index 0000000..927c4e4
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.
+ */
+
+package org.sonar.server.component.ws;
+
+import com.google.common.io.Resources;
+import org.apache.commons.lang.StringUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.component.SnapshotDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.component.ComponentTesting;
+import org.sonar.server.component.SnapshotTesting;
+import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.component.db.SnapshotDao;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsTester;
+import org.sonar.test.JsonAssert;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class ProjectsProvisionedActionTest {
+
+  @ClassRule
+  public static DbTester db = new DbTester();
+  @Rule
+  public UserSessionRule userSessionRule = UserSessionRule.standalone();
+
+  WsTester ws;
+  DbClient dbClient;
+  DbSession dbSession;
+  ComponentDao componentDao;
+  System2 system2 = mock(System2.class);
+
+  @After
+  public void tearDown() {
+    dbSession.close();
+  }
+
+  @Before
+  public void setUp() {
+    dbClient = new DbClient(db.database(), db.myBatis(), new ComponentDao(system2), new SnapshotDao(System2.INSTANCE));
+    dbSession = dbClient.openSession(false);
+    componentDao = dbClient.componentDao();
+    db.truncateTables();
+    ws = new WsTester(new ProjectsWs(new ProjectsProvisionedAction(dbClient, userSessionRule)));
+  }
+
+  @Test
+  public void all_provisioned_projects_without_analyzed_projects() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
+    ComponentDto analyzedProject = ComponentTesting.newProjectDto("analyzed-uuid-1");
+    componentDao.insert(dbSession, newProvisionedProject("1"), newProvisionedProject("2"));
+    analyzedProject = dbClient.componentDao().insert(dbSession, analyzedProject);
+    SnapshotDto snapshot = SnapshotTesting.createForProject(analyzedProject);
+    dbClient.snapshotDao().insert(dbSession, snapshot);
+    dbSession.commit();
+
+    WsTester.Result result = ws.newGetRequest("api/projects", "provisioned").execute();
+
+    result.assertJson(getClass(), "all-projects.json");
+    assertThat(result.outputAsString()).doesNotContain("analyzed-uuid-1");
+  }
+
+  @Test
+  public void provisioned_projects_with_correct_pagination() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
+    for (int i = 1; i <= 10; i++) {
+      componentDao.insert(dbSession, newProvisionedProject(String.valueOf(i)));
+    }
+    dbSession.commit();
+
+    WsTester.TestRequest request = ws.newGetRequest("api/projects", "provisioned")
+      .setParam(Param.PAGE, "3")
+      .setParam(Param.PAGE_SIZE, "4");
+
+    String jsonOutput = request.execute().outputAsString();
+
+    assertThat(StringUtils.countMatches(jsonOutput, "provisioned-uuid-")).isEqualTo(2);
+  }
+
+  @Test
+  public void provisioned_projects_with_desired_fields() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
+    componentDao.insert(dbSession, newProvisionedProject("1"));
+    dbSession.commit();
+
+    String jsonOutput = ws.newGetRequest("api/projects", "provisioned")
+      .setParam(Param.FIELDS, "key")
+      .execute().outputAsString();
+
+    assertThat(jsonOutput).contains("uuid", "key")
+      .doesNotContain("name")
+      .doesNotContain("creationDate");
+  }
+
+  @Test
+  public void provisioned_projects_with_query() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
+    componentDao.insert(dbSession, newProvisionedProject("1"), newProvisionedProject("2"));
+    dbSession.commit();
+
+    String jsonOutput = ws.newGetRequest("api/projects", "provisioned")
+      .setParam(Param.TEXT_QUERY, "provisioned-name-2")
+      .execute().outputAsString();
+
+    assertThat(jsonOutput)
+      .contains("provisioned-name-2", "provisioned-uuid-2")
+      .doesNotContain("provisioned-uuid-1");
+    assertThat(componentDao.countProvisionedProjects(dbSession, "name-2")).isEqualTo(1);
+    assertThat(componentDao.countProvisionedProjects(dbSession, "key-2")).isEqualTo(1);
+    assertThat(componentDao.countProvisionedProjects(dbSession, "visioned-name-")).isEqualTo(2);
+  }
+
+  private static ComponentDto newProvisionedProject(String id) {
+    return ComponentTesting
+      .newProjectDto("provisioned-uuid-" + id)
+      .setName("provisioned-name-" + id)
+      .setKey("provisioned-key-" + id);
+  }
+
+  @Test
+  public void provisioned_projects_as_defined_in_the_example() throws Exception {
+    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
+    ComponentDto hBaseProject = ComponentTesting.newProjectDto("ce4c03d6-430f-40a9-b777-ad877c00aa4d")
+      .setKey("org.apache.hbas:hbase")
+      .setName("HBase")
+      .setCreatedAt(DateUtils.parseDateTime("2015-03-04T23:03:44+0100"));
+    ComponentDto roslynProject = ComponentTesting.newProjectDto("c526ef20-131b-4486-9357-063fa64b5079")
+      .setKey("com.microsoft.roslyn:roslyn")
+      .setName("Roslyn")
+      .setCreatedAt(DateUtils.parseDateTime("2013-03-04T23:03:44+0100"));
+    componentDao.insert(dbSession, hBaseProject, roslynProject);
+    dbSession.commit();
+
+    WsTester.Result result = ws.newGetRequest("api/projects", "provisioned").execute();
+
+    JsonAssert.assertJson(result.outputAsString()).isSimilarTo(Resources.getResource(getClass(), "projects-example-provisioned.json"));
+  }
+}
index 94f05808947ab037c04b444e734283060ad03f39..003bb4fa72a31ea8c34da1b633d8e62027a62f80 100644 (file)
@@ -31,11 +31,12 @@ import static org.assertj.core.api.Assertions.assertThat;
 public class ProjectsWsTest {
 
   WebService.Controller controller;
+  WsTester ws;
 
   @Before
   public void setUp() {
-    WsTester tester = new WsTester(new ProjectsWs());
-    controller = tester.controller("api/projects");
+    ws = new WsTester(new ProjectsWs());
+    controller = ws.controller("api/projects");
   }
 
   @Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProvisionedProjectsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ProvisionedProjectsActionTest.java
deleted file mode 100644 (file)
index 669c305..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 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.
- */
-
-package org.sonar.server.component.ws;
-
-import com.google.common.io.Resources;
-import org.apache.commons.lang.StringUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.server.ws.WebService.Param;
-import org.sonar.api.utils.DateUtils;
-import org.sonar.api.utils.System2;
-import org.sonar.api.web.UserRole;
-import org.sonar.core.component.ComponentDto;
-import org.sonar.core.component.SnapshotDto;
-import org.sonar.core.persistence.DbSession;
-import org.sonar.core.persistence.DbTester;
-import org.sonar.server.component.ComponentTesting;
-import org.sonar.server.component.SnapshotTesting;
-import org.sonar.server.component.db.ComponentDao;
-import org.sonar.server.component.db.SnapshotDao;
-import org.sonar.server.db.DbClient;
-import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.ws.WsTester;
-import org.sonar.test.JsonAssert;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-public class ProvisionedProjectsActionTest {
-
-  @ClassRule
-  public static DbTester db = new DbTester();
-  @Rule
-  public UserSessionRule userSessionRule = UserSessionRule.standalone();
-
-  WsTester ws;
-  DbClient dbClient;
-  DbSession dbSession;
-  ComponentDao componentDao;
-  System2 system2 = mock(System2.class);
-
-  @After
-  public void tearDown() {
-    dbSession.close();
-  }
-
-  @Before
-  public void setUp() {
-    dbClient = new DbClient(db.database(), db.myBatis(), new ComponentDao(system2), new SnapshotDao(System2.INSTANCE));
-    dbSession = dbClient.openSession(false);
-    componentDao = dbClient.componentDao();
-    db.truncateTables();
-    ws = new WsTester(new ProjectsWs(new ProvisionedProjectsAction(dbClient, userSessionRule)));
-  }
-
-  @Test
-  public void all_provisioned_projects_without_analyzed_projects() throws Exception {
-    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
-    ComponentDto analyzedProject = ComponentTesting.newProjectDto("analyzed-uuid-1");
-    componentDao.insert(dbSession, newProvisionedProject("1"), newProvisionedProject("2"));
-    analyzedProject = componentDao.insert(dbSession, analyzedProject);
-    SnapshotDto snapshot = SnapshotTesting.createForProject(analyzedProject);
-    dbClient.snapshotDao().insert(dbSession, snapshot);
-    dbSession.commit();
-
-    WsTester.TestRequest request = ws.newGetRequest("api/projects", "provisioned");
-
-    request.execute().assertJson(getClass(), "all-projects.json");
-  }
-
-  @Test
-  public void provisioned_projects_with_correct_paginated() throws Exception {
-    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
-    for (int i = 1; i <= 10; i++) {
-      componentDao.insert(dbSession, newProvisionedProject(String.valueOf(i)));
-    }
-    dbSession.commit();
-
-    WsTester.TestRequest request = ws.newGetRequest("api/projects", "provisioned")
-      .setParam(Param.PAGE, "3")
-      .setParam(Param.PAGE_SIZE, "4");
-
-    String jsonOutput = request.execute().outputAsString();
-
-    assertThat(StringUtils.countMatches(jsonOutput, "provisioned-uuid-")).isEqualTo(2);
-  }
-
-  @Test
-  public void provisioned_projects_with_desired_fields() throws Exception {
-    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
-    componentDao.insert(dbSession, newProvisionedProject("1"));
-    dbSession.commit();
-
-    String jsonOutput = ws.newGetRequest("api/projects", "provisioned")
-      .setParam(Param.FIELDS, "key")
-      .execute().outputAsString();
-
-    assertThat(jsonOutput).contains("uuid", "key")
-      .doesNotContain("name")
-      .doesNotContain("creationDate");
-  }
-
-  @Test
-  public void provisioned_projects_with_query() throws Exception {
-    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
-    componentDao.insert(dbSession, newProvisionedProject("1"), newProvisionedProject("2"));
-    dbSession.commit();
-
-    String jsonOutput = ws.newGetRequest("api/projects", "provisioned")
-      .setParam(Param.TEXT_QUERY, "provisioned-name-2")
-      .execute().outputAsString();
-
-    assertThat(jsonOutput)
-      .contains("provisioned-name-2", "provisioned-uuid-2")
-      .doesNotContain("provisioned-uuid-1");
-    assertThat(componentDao.countProvisionedProjects(dbSession, "name-2")).isEqualTo(1);
-    assertThat(componentDao.countProvisionedProjects(dbSession, "key-2")).isEqualTo(1);
-    assertThat(componentDao.countProvisionedProjects(dbSession, "visioned-name-")).isEqualTo(2);
-  }
-
-  private static ComponentDto newProvisionedProject(String id) {
-    return ComponentTesting
-      .newProjectDto("provisioned-uuid-" + id)
-      .setName("provisioned-name-" + id)
-      .setKey("provisioned-key-" + id);
-  }
-
-  @Test
-  public void provisioned_projects_as_defined_in_the_example() throws Exception {
-    userSessionRule.setGlobalPermissions(UserRole.ADMIN);
-    ComponentDto hBaseProject = ComponentTesting.newProjectDto("ce4c03d6-430f-40a9-b777-ad877c00aa4d")
-      .setKey("org.apache.hbas:hbase")
-      .setName("HBase")
-      .setCreatedAt(DateUtils.parseDateTime("2015-03-04T23:03:44+0100"));
-    ComponentDto roslynProject = ComponentTesting.newProjectDto("c526ef20-131b-4486-9357-063fa64b5079")
-      .setKey("com.microsoft.roslyn:roslyn")
-      .setName("Roslyn")
-      .setCreatedAt(DateUtils.parseDateTime("2013-03-04T23:03:44+0100"));
-    componentDao.insert(dbSession, hBaseProject, roslynProject);
-    dbSession.commit();
-
-    WsTester.Result result = ws.newGetRequest("api/projects", "provisioned").execute();
-
-    JsonAssert.assertJson(result.outputAsString()).isSimilarTo(Resources.getResource(getClass(), "projects-example-provisioned.json"));
-  }
-}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/select_ghost_projects.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/select_ghost_projects.xml
new file mode 100644 (file)
index 0000000..e7af741
--- /dev/null
@@ -0,0 +1,109 @@
+<dataset>
+
+  <!-- Struts projects is authorized for all user -->
+  <group_roles id="1" group_id="[null]" resource_id="1" role="user"/>
+
+  <!-- Ghost project -->
+  <projects id="42" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.ghost.project" name="Ghost Project"
+            uuid="PPAA" project_uuid="PPAA" module_uuid="[null]" module_uuid_path="."
+            description="the description" long_name="Ghost Project"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" authorization_updated_at="123456789" />
+
+  <!-- root project -->
+  <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" deprecated_kee="org.struts:struts" name="Struts"
+            uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=".ABCD."
+            description="the description" long_name="Apache Struts"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" authorization_updated_at="123456789" />
+  <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="TRK" created_at="1228222680000" build_date="1228222680000"
+             version="[null]" path=""/>
+  <snapshots id="10" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             status="P" islast="[false]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="TRK" created_at="1228136280000" build_date="1228136280000"
+             version="[null]" path=""/>
+  <snapshots id="11" project_id="42" parent_snapshot_id="[null]" root_project_id="42" root_snapshot_id="[null]"
+             status="U" islast="[false]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="TRK" created_at="1228136280000" build_date="1228136280000"
+             version="[null]" path=""/>
+
+  <!-- module -->
+  <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+            uuid="EFGH" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=".ABCD.EFGH."
+            scope="PRJ" qualifier="BRC" long_name="Struts Core"
+            description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" authorization_updated_at="[null]" />
+  <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="BRC" created_at="1228222680000" build_date="1228222680000"
+             version="[null]" path="1."/>
+
+  <!-- directory -->
+  <projects long_name="org.struts" id="3" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts"
+            uuid="GHIJ" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path=".ABCD.EFGH."
+            name="src/org/struts" root_id="2"
+            description="[null]"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts" authorization_updated_at="[null]" />
+  <snapshots id="3" project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="DIR" qualifier="PAC" created_at="1228222680000" build_date="1228222680000"
+             version="[null]" path="1.2."/>
+
+  <!-- file -->
+  <projects long_name="org.struts.RequestContext" id="4" scope="FIL" qualifier="FIL" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+            uuid="KLMN" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path=".ABCD.EFGH."
+            name="RequestContext.java" root_id="2"
+            description="[null]"
+            enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="src/org/struts/RequestContext.java" authorization_updated_at="[null]" />
+
+  <snapshots id="4" project_id="4" parent_snapshot_id="3" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="FIL" qualifier="CLA" created_at="1228222680000" build_date="1228222680000"
+             version="[null]" path="1.2.3."/>
+
+  <!-- Disabled projects -->
+  <projects id="10" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.disabled.project" name="Disabled Project"
+            uuid="DCBA" project_uuid="DCBA" module_uuid="[null]" module_uuid_path="."
+            description="the description" long_name="Disabled project"
+            enabled="[false]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" authorization_updated_at="123456789" />
+
+  <!-- Developer and technical project copy -->
+  <projects id="11" root_id="[null]" scope="PRJ" qualifier="DEV" kee="DEV:anakin@skywalker.name" name="Anakin Skywalker"
+            uuid="OPQR" project_uuid="OPQR" module_uuid="[null]" module_uuid_path=".OPQR."
+            description="the description" long_name="Anakin Skywalker"
+            enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" authorization_updated_at="123456789" />
+  <projects id="12" root_id="11" scope="PRJ" qualifier="DEV_PRJ" kee="DEV:anakin@skywalker.name:org.struts:struts" name="Apache Struts"
+            uuid="STUV" project_uuid="OPQR" module_uuid="OPQR" module_uuid_path=".OPQR."
+            description="the description" long_name="Apache Struts"
+            enabled="[true]" language="[null]" copy_resource_id="1" person_id="11" path="[null]" authorization_updated_at="123456789" />
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsGhostsActionTest/all-projects.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsGhostsActionTest/all-projects.json
new file mode 100644 (file)
index 0000000..cd1aad1
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "projects": [
+    {
+      "uuid": "ghost-uuid-1",
+      "key": "ghost-key-1",
+      "name": "ghost-name-1"
+    },
+    {
+      "uuid": "ghost-uuid-2",
+      "key": "ghost-key-2",
+      "name": "ghost-name-2"
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsGhostsActionTest/pagination.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsGhostsActionTest/pagination.json
new file mode 100644 (file)
index 0000000..8967d5c
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "p": 3,
+  "ps": 4,
+  "total": 10
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsProvisionedActionTest/all-projects.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProjectsProvisionedActionTest/all-projects.json
new file mode 100644 (file)
index 0000000..3fc3e56
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "projects":[
+    {
+      "uuid":"provisioned-uuid-1",
+      "key":"provisioned-key-1",
+      "name":"provisioned-name-1"
+    },
+    {
+      "uuid":"provisioned-uuid-2",
+      "key":"provisioned-key-2",
+      "name":"provisioned-name-2"
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProvisionedProjectsActionTest/all-projects.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/ProvisionedProjectsActionTest/all-projects.json
deleted file mode 100644 (file)
index 3fc3e56..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "projects":[
-    {
-      "uuid":"provisioned-uuid-1",
-      "key":"provisioned-key-1",
-      "name":"provisioned-name-1"
-    },
-    {
-      "uuid":"provisioned-uuid-2",
-      "key":"provisioned-key-2",
-      "name":"provisioned-name-2"
-    }
-  ]
-}
index 6ed690b97d6e475acfc718f27cac2b3a3c3da3e8..aa3960c874753d95bb6e0ca15d21c50c8cf82a9e 100644 (file)
@@ -107,4 +107,8 @@ public interface ComponentMapper {
   List<ComponentDto> selectProvisionedProjects(Map<String, String> parameters, RowBounds rowBounds);
 
   int countProvisionedProjects(Map<String, String> parameters);
+
+  List<ComponentDto> selectGhostProjects(Map<String, String> parameters, RowBounds rowBounds);
+
+  long countGhostProjects(Map<String, String> parameters);
 }
index 84c011c08f2ba9f9ad7d1f0994f14cac8880db68..92d5d1180dcc6faab9dcee01f453f7ec4ffeae96 100644 (file)
     and p.copy_resource_id is null
     <if test="query!=null">
       and (
-      p.name like #{query}
-      or p.kee like #{query}
+      UPPER(p.name) like #{query}
+      or UPPER(p.kee) like #{query}
       )
     </if>
   </sql>
+
+  <select id="selectGhostProjects" parameterType="map" resultType="Component">
+    select distinct <include refid="componentColumns"/>
+    from projects p
+    <include refid="ghostClauses"/>
+  </select>
+
+  <select id="countGhostProjects" parameterType="map" resultType="long">
+    select count(p.id)
+    from projects p
+    <include refid="ghostClauses"/>
+  </select>
+
+  <sql id="ghostClauses">
+    inner join snapshots s1 on s1.project_id = p.id and s1.status='U'
+    left join snapshots s2 on s2.project_id = p.id and s2.status='P'
+    where
+      s2.id is null
+      and p.qualifier=#{qualifier}
+      and p.copy_resource_id is null
+      <if test="query!=null">
+        and (
+        UPPER(p.name) like #{query}
+        or UPPER(p.kee) like #{query}
+        )
+      </if>
+  </sql>
 </mapper>
index 43e0d461c0a54336ebed468dfaee7f9268c7ded9..22b83f19711be4372a9971e90fdb6b23d7eb98f4 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.api.server.ws;
 
+import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
@@ -356,6 +357,20 @@ public interface WebService extends Definable<WebService.Context> {
       return this;
     }
 
+    /**$
+     *
+     * Creates the parameter {@link org.sonar.api.server.ws.WebService.Param#TEXT_QUERY}, which is
+     * used to search for a subset of fields containing the supplied string.<br />
+     * The fields must be in the <strong>plural</strong> form (ex: "names", "keys")
+     */
+    public NewAction addSearchQuery(String exampleValue, String... pluralFields) {
+      String actionDescription = String.format("Searches for %s containing the supplied string.", Joiner.on(" and ").join(pluralFields));
+      createParam(Param.TEXT_QUERY)
+        .setDescription(actionDescription)
+        .setExampleValue(exampleValue);
+      return this;
+    }
+
     /**
      * Add predefined parameters related to sorting of results.
      */