]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6259 Persist components in db
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 19 May 2015 12:02:45 +0000 (14:02 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 21 May 2015 15:37:56 +0000 (17:37 +0200)
15 files changed:
server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentDao.java
server/sonar-server/src/main/java/org/sonar/server/computation/component/Component.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/step/ComponentsCache.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedComponentsCacheStep.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsStep.java [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/computation/step/PersistComponentsStepTest.java [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/find_project_uuids.xml [deleted file]
server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/insert-result.xml
server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/update-result.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/update.xml [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java
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-core/src/test/java/org/sonar/core/component/ComponentDtoTest.java

index ef9e4482f716af2fbc5be0d7d248027ccc43e982..95b22226bcdaa9afdfcc76b7d741166efdbc63ef 100644 (file)
@@ -136,6 +136,10 @@ public class ComponentDao implements DaoComponent {
     });
   }
 
+  public List<ComponentDto> selectComponentsFromProjectUuid(DbSession session, String projectKey) {
+    return mapper(session).selectComponentsFromProjectUuid(projectKey);
+  }
+
   public List<ComponentDto> selectByKeys(DbSession session, Collection<String> keys) {
     return mapper(session).selectByKeys(keys);
   }
@@ -153,24 +157,6 @@ public class ComponentDao implements DaoComponent {
     return mapper(session).selectByKey(key);
   }
 
-  public void insert(DbSession session, ComponentDto item) {
-    mapper(session).insert(item);
-  }
-
-  public void insert(DbSession session, Collection<ComponentDto> items) {
-    for (ComponentDto item : items) {
-      insert(session, item);
-    }
-  }
-
-  public void insert(DbSession session, ComponentDto item, ComponentDto... others) {
-    insert(session, Lists.asList(item, others));
-  }
-
-  public List<String> selectProjectUuids(DbSession session) {
-    return mapper(session).selectProjectUuids();
-  }
-
   public List<UuidWithProjectUuidDto> selectAllViewsAndSubViews(DbSession session) {
     return mapper(session).selectUuidsForQualifiers(Qualifiers.VIEW, Qualifiers.SUBVIEW);
   }
@@ -221,6 +207,24 @@ public class ComponentDao implements DaoComponent {
     parameters.put("qualifier", Qualifiers.PROJECT);
   }
 
+  public void insert(DbSession session, ComponentDto item) {
+    mapper(session).insert(item);
+  }
+
+  public void insert(DbSession session, Collection<ComponentDto> items) {
+    for (ComponentDto item : items) {
+      insert(session, item);
+    }
+  }
+
+  public void insert(DbSession session, ComponentDto item, ComponentDto... others) {
+    insert(session, Lists.asList(item, others));
+  }
+
+  public void update(DbSession session, ComponentDto item) {
+    mapper(session).update(item);
+  }
+
   private ComponentMapper mapper(DbSession session) {
     return session.getMapper(ComponentMapper.class);
   }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/Component.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/Component.java
new file mode 100644 (file)
index 0000000..e3852bc
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.computation.component;
+
+public class Component {
+
+  private Long id;
+  private String uuid;
+  private String key;
+
+  public Long getId() {
+    return id;
+  }
+
+  public Component setId(Long id) {
+    this.id = id;
+    return this;
+  }
+
+  public String getKey() {
+    return key;
+  }
+
+  public Component setKey(String key) {
+    this.key = key;
+    return this;
+  }
+
+  public String getUuid() {
+    return uuid;
+  }
+
+  public Component setUuid(String uuid) {
+    this.uuid = uuid;
+    return this;
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComponentsCache.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComponentsCache.java
new file mode 100644 (file)
index 0000000..5865f0e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.computation.step;
+
+import org.sonar.batch.protocol.output.BatchReportReader;
+import org.sonar.server.computation.component.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * To be finished
+ */
+public class ComponentsCache {
+
+  private final Map<Integer, Component> componentsByRef;
+
+  public ComponentsCache(final BatchReportReader reader) {
+    this.componentsByRef = new HashMap<>();
+  }
+
+  public Component getComponent(int componentRef) {
+    // TODO should we return null or fail on unknown component ?
+    return null;
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedComponentsCacheStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedComponentsCacheStep.java
new file mode 100644 (file)
index 0000000..2ca84a4
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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.computation.step;
+
+import com.google.common.collect.Maps;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.utils.internal.Uuids;
+import org.sonar.batch.protocol.Constants;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.protocol.output.BatchReportReader;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.component.ComponentKeys;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.util.NonNullInputFunction;
+import org.sonar.server.computation.ComputationContext;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.db.DbClient;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * To be finished
+ */
+public class FeedComponentsCacheStep implements ComputationStep {
+
+  private final DbClient dbClient;
+
+  public FeedComponentsCacheStep(DbClient dbClient) {
+    this.dbClient = dbClient;
+  }
+
+  @Override
+  public String[] supportedProjectQualifiers() {
+    return new String[] {Qualifiers.PROJECT};
+  }
+
+  @Override
+  public void execute(ComputationContext context) {
+    DbSession session = dbClient.openSession(false);
+    try {
+      List<ComponentDto> components = dbClient.componentDao().selectComponentsFromProjectUuid(session, context.getProject().uuid());
+      Map<String, ComponentDto> componentDtosByKey = componentDtosByKey(components);
+      Map<Integer, Component> componentsByRef = new HashMap<>();
+      int rootComponentRef = context.getReportMetadata().getRootComponentRef();
+      recursivelyProcessComponent(context, rootComponentRef, context.getReportReader().readComponent(rootComponentRef), componentDtosByKey, componentsByRef);
+    } finally {
+      session.close();
+    }
+  }
+
+  private void recursivelyProcessComponent(ComputationContext context, int componentRef, BatchReport.Component nearestModule,
+                                           Map<String, ComponentDto> componentDtosByKey, Map<Integer, Component> componentsByRef) {
+    BatchReportReader reportReader = context.getReportReader();
+    BatchReport.Component reportComponent = reportReader.readComponent(componentRef);
+    String componentKey = ComponentKeys.createKey(nearestModule.getKey(), reportComponent.getPath(), context.getReportMetadata().getBranch());
+
+    Component component = new Component().setKey(componentKey);
+    ComponentDto componentDto = componentDtosByKey.get(componentKey);
+    if (componentDto == null) {
+      component.setUuid(Uuids.create());
+    } else {
+      component.setId(component.getId());
+      component.setKey(componentKey);
+    }
+
+    for (Integer childRef : reportComponent.getChildRefList()) {
+      // If current component is not a module or a project, we need to keep the parent reference to the nearest module
+      BatchReport.Component nextNearestModule = !reportComponent.getType().equals(Constants.ComponentType.PROJECT) && !reportComponent.getType().equals(Constants.ComponentType.MODULE) ?
+        nearestModule : reportComponent;
+      recursivelyProcessComponent(context, childRef, nextNearestModule, componentDtosByKey, componentsByRef);
+    }
+  }
+
+  private Map<String, ComponentDto> componentDtosByKey(List<ComponentDto> components){
+    return Maps.uniqueIndex(components, new NonNullInputFunction<ComponentDto, String>() {
+      @Override
+      public String doApply(ComponentDto input) {
+        return input.key();
+      }
+    });
+  }
+
+  @Override
+  public String getDescription() {
+    return "Feed components cache";
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistComponentsStep.java
new file mode 100644 (file)
index 0000000..2fbcf62
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * 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.computation.step;
+
+import com.google.common.collect.Maps;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.utils.internal.Uuids;
+import org.sonar.batch.protocol.Constants;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.protocol.output.BatchReportReader;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.component.ComponentKeys;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.util.NonNullInputFunction;
+import org.sonar.server.computation.ComputationContext;
+import org.sonar.server.db.DbClient;
+
+import javax.annotation.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+public class PersistComponentsStep implements ComputationStep {
+
+  private final DbClient dbClient;
+
+  public PersistComponentsStep(DbClient dbClient) {
+    this.dbClient = dbClient;
+  }
+
+  @Override
+  public String[] supportedProjectQualifiers() {
+    return new String[] {Qualifiers.PROJECT};
+  }
+
+  @Override
+  public void execute(ComputationContext context) {
+    DbSession session = dbClient.openSession(false);
+    try {
+      List<ComponentDto> components = dbClient.componentDao().selectComponentsFromProjectUuid(session, context.getProject().uuid());
+      Map<String, ComponentDto> componentDtosByKey = componentDtosByKey(components);
+      int rootComponentRef = context.getReportMetadata().getRootComponentRef();
+      ComponentContext componentContext = new ComponentContext(context, session, componentDtosByKey);
+      recursivelyProcessComponent(componentContext, rootComponentRef, null);
+      session.commit();
+    } finally {
+      session.close();
+    }
+  }
+
+  private void recursivelyProcessComponent(ComponentContext componentContext, int componentRef, @Nullable ComponentDto moduleParent) {
+    BatchReportReader reportReader = componentContext.context.getReportReader();
+    BatchReport.Component reportComponent = reportReader.readComponent(componentRef);
+    ComponentDto componentDto = processComponent(componentContext, reportComponent, moduleParent);
+
+    for (Integer childRef : reportComponent.getChildRefList()) {
+      // If current component is not a module or a project, we need to keep the parent reference to the nearest module
+      ComponentDto nextModuleParent = !reportComponent.getType().equals(Constants.ComponentType.PROJECT) && !reportComponent.getType().equals(Constants.ComponentType.MODULE) ?
+        moduleParent : componentDto;
+      recursivelyProcessComponent(componentContext, childRef, nextModuleParent);
+    }
+  }
+
+  private ComponentDto processComponent(ComponentContext componentContext, BatchReport.Component reportComponent, @Nullable ComponentDto moduleParent) {
+    String path = reportComponent.hasPath() ? reportComponent.getPath() : null;
+    String branch = componentContext.context.getReportMetadata().hasBranch() ? componentContext.context.getReportMetadata().getBranch() : null;
+    String componentKey = reportComponent.hasKey() || moduleParent == null ?
+      ComponentKeys.createKey(reportComponent.getKey(), branch) :
+      ComponentKeys.createEffectiveKey(moduleParent.getKey(), path);
+    ComponentDto existingComponent = componentContext.componentDtosByKey.get(componentKey);
+    if (existingComponent == null) {
+      ComponentDto component = createComponent(reportComponent, componentKey, Uuids.create(), moduleParent);
+      dbClient.componentDao().insert(componentContext.dbSession, component);
+      return component;
+    } else {
+      ComponentDto component = createComponent(reportComponent, componentKey, existingComponent.uuid(), moduleParent);
+      if (updateComponent(existingComponent, component)) {
+        dbClient.componentDao().update(componentContext.dbSession, existingComponent);
+      }
+      return existingComponent;
+    }
+  }
+
+  private ComponentDto createComponent(BatchReport.Component reportComponent, String componentKey, String uuid, @Nullable ComponentDto parentModule) {
+    ComponentDto component = new ComponentDto();
+    component.setUuid(uuid);
+    component.setKey(componentKey);
+    component.setDeprecatedKey(componentKey);
+    component.setEnabled(true);
+    component.setScope(getScope(reportComponent));
+    component.setQualifier(getQualifier(reportComponent));
+    component.setName(getFileName(reportComponent));
+
+    if (isProjectOrModule(reportComponent)) {
+      component.setLongName(component.name());
+      if (reportComponent.hasDescription()) {
+        component.setDescription(reportComponent.getDescription());
+      }
+    } else {
+      component.setLongName(reportComponent.getPath());
+      if (reportComponent.hasPath()) {
+        component.setPath(reportComponent.getPath());
+      }
+      if (reportComponent.hasLanguage()) {
+        component.setLanguage(reportComponent.getLanguage());
+      }
+    }
+    if (parentModule != null) {
+      component.setParentProjectId(parentModule.getId());
+      component.setProjectUuid(parentModule.projectUuid());
+      component.setModuleUuid(parentModule.uuid());
+      component.setModuleUuidPath(reportComponent.getType().equals(Constants.ComponentType.MODULE) ?
+        parentModule.moduleUuidPath() + component.uuid() + ComponentDto.MODULE_UUID_PATH_SEP :
+        parentModule.moduleUuidPath());
+    } else {
+      component.setProjectUuid(uuid);
+      component.setModuleUuidPath(ComponentDto.MODULE_UUID_PATH_SEP + component.uuid() + ComponentDto.MODULE_UUID_PATH_SEP);
+    }
+    return component;
+  }
+
+  private boolean updateComponent(ComponentDto existingComponent, ComponentDto newComponent) {
+    boolean isUpdated = false;
+    if (Scopes.PROJECT.equals(existingComponent.scope())) {
+      if (!newComponent.name().equals(existingComponent.name())) {
+        existingComponent.setName(newComponent.name());
+        isUpdated = true;
+      }
+      if (!StringUtils.equals(existingComponent.description(), newComponent.description())) {
+        existingComponent.setDescription(newComponent.description());
+        isUpdated = true;
+      }
+    }
+
+    if (!StringUtils.equals(existingComponent.moduleUuid(), newComponent.moduleUuid())) {
+      existingComponent.setModuleUuid(newComponent.moduleUuid());
+      isUpdated = true;
+    }
+    if (!existingComponent.moduleUuidPath().equals(newComponent.moduleUuidPath())) {
+      existingComponent.setModuleUuidPath(newComponent.moduleUuidPath());
+      isUpdated = true;
+    }
+    if (!ObjectUtils.equals(existingComponent.parentProjectId(), newComponent.parentProjectId())) {
+      existingComponent.setParentProjectId(newComponent.parentProjectId());
+      isUpdated = true;
+    }
+
+    return isUpdated;
+  }
+
+  private static boolean isProjectOrModule(BatchReport.Component reportComponent) {
+    return reportComponent.getType().equals(Constants.ComponentType.PROJECT) || reportComponent.getType().equals(Constants.ComponentType.MODULE);
+  }
+
+  private static String getScope(BatchReport.Component reportComponent) {
+    switch (reportComponent.getType()) {
+      case PROJECT:
+        return Scopes.PROJECT;
+      case MODULE:
+        return Scopes.PROJECT;
+      case DIRECTORY:
+        return Scopes.DIRECTORY;
+      case FILE:
+        return Scopes.FILE;
+    }
+    throw new IllegalArgumentException(String.format("Unknown type '%s'", reportComponent.getType()));
+  }
+
+  private static String getQualifier(BatchReport.Component reportComponent) {
+    switch (reportComponent.getType()) {
+      case PROJECT:
+        return Qualifiers.PROJECT;
+      case MODULE:
+        return Qualifiers.MODULE;
+      case DIRECTORY:
+        return Qualifiers.DIRECTORY;
+      case FILE:
+        return !reportComponent.getIsTest() ? Qualifiers.FILE : Qualifiers.UNIT_TEST_FILE;
+    }
+    throw new IllegalArgumentException(String.format("Unknown type '%s'", reportComponent.getType()));
+  }
+
+  private static String getFileName(BatchReport.Component reportComponent) {
+    String path = reportComponent.getPath();
+    if (reportComponent.getType() == Constants.ComponentType.PROJECT || reportComponent.getType() == Constants.ComponentType.MODULE) {
+      return reportComponent.getName();
+    } else if (reportComponent.getType().equals(Constants.ComponentType.DIRECTORY)) {
+      return path;
+    } else {
+      return FilenameUtils.getName(path);
+    }
+  }
+
+  private Map<String, ComponentDto> componentDtosByKey(List<ComponentDto> components) {
+    return Maps.uniqueIndex(components, new NonNullInputFunction<ComponentDto, String>() {
+      @Override
+      public String doApply(ComponentDto input) {
+        return input.key();
+      }
+    });
+  }
+
+  private static class ComponentContext {
+    private final ComputationContext context;
+    private final Map<String, ComponentDto> componentDtosByKey;
+    private final DbSession dbSession;
+
+    public ComponentContext(ComputationContext context, DbSession dbSession, Map<String, ComponentDto> componentDtosByKey) {
+      this.componentDtosByKey = componentDtosByKey;
+      this.context = context;
+      this.dbSession = dbSession;
+    }
+  }
+
+  @Override
+  public String getDescription() {
+    return "Feed components cache";
+  }
+}
index aa55b64387f1748cb4b1188a9b8391b8760faf98..62d3b58548e8c606506860856d96c56b3b0d1536 100644 (file)
@@ -173,6 +173,7 @@ public class ComponentDaoTest {
     assertThat(result.path()).isNull();
     assertThat(result.name()).isEqualTo("Struts");
     assertThat(result.longName()).isEqualTo("Apache Struts");
+    assertThat(result.description()).isEqualTo("the description");
     assertThat(result.qualifier()).isEqualTo("TRK");
     assertThat(result.scope()).isEqualTo("PRJ");
     assertThat(result.language()).isNull();
@@ -452,6 +453,66 @@ public class ComponentDaoTest {
     assertThat(sut.selectEnabledFilesFromProject(session, "unknown")).isEmpty();
   }
 
+  @Test
+  public void select_components_from_project() {
+    db.prepareDbUnit(getClass(), "multi-modules.xml");
+
+    List<ComponentDto> components = sut.selectComponentsFromProjectUuid(session, "ABCD");
+    assertThat(components).hasSize(5);
+
+    assertThat(sut.selectComponentsFromProjectUuid(session, "UNKNOWN")).isEmpty();
+  }
+
+  @Test
+  public void select_views_and_sub_views() {
+    db.prepareDbUnit(getClass(), "shared_views.xml");
+
+    assertThat(sut.selectAllViewsAndSubViews(session)).extracting("uuid").containsOnly("ABCD", "EFGH", "FGHI", "IJKL");
+    assertThat(sut.selectAllViewsAndSubViews(session)).extracting("projectUuid").containsOnly("ABCD", "EFGH", "IJKL");
+  }
+
+  @Test
+  public void select_projects_from_view() {
+    db.prepareDbUnit(getClass(), "shared_views.xml");
+
+    assertThat(sut.selectProjectsFromView(session, "ABCD", "ABCD")).containsOnly("JKLM");
+    assertThat(sut.selectProjectsFromView(session, "EFGH", "EFGH")).containsOnly("KLMN", "JKLM");
+    assertThat(sut.selectProjectsFromView(session, "FGHI", "EFGH")).containsOnly("JKLM");
+    assertThat(sut.selectProjectsFromView(session, "IJKL", "IJKL")).isEmpty();
+    assertThat(sut.selectProjectsFromView(session, "Unknown", "Unknown")).isEmpty();
+  }
+
+  @Test
+  public void select_provisioned_projects() {
+    db.prepareDbUnit(getClass(), "select_provisioned_projects.xml");
+
+    List<ComponentDto> result = sut.selectProvisionedProjects(session, new SearchOptions(), null);
+    ComponentDto project = result.get(0);
+
+    assertThat(result).hasSize(1);
+    assertThat(project.getKey()).isEqualTo("org.provisioned.project");
+  }
+
+  @Test
+  public void count_provisioned_projects() {
+    db.prepareDbUnit(getClass(), "select_provisioned_projects.xml");
+
+    int numberOfProjects = sut.countProvisionedProjects(session, null);
+
+    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);
+  }
+
   @Test
   public void insert() {
     db.prepareDbUnit(getClass(), "empty.xml");
@@ -469,6 +530,7 @@ public class ComponentDaoTest {
       .setQualifier("FIL")
       .setScope("FIL")
       .setLanguage("java")
+      .setDescription("description")
       .setPath("src/org/struts/RequestContext.java")
       .setParentProjectId(3L)
       .setCopyResourceId(5L)
@@ -513,59 +575,31 @@ public class ComponentDaoTest {
   }
 
   @Test
-  public void find_project_uuids() {
-    db.prepareDbUnit(getClass(), "find_project_uuids.xml");
-
-    assertThat(sut.selectProjectUuids(session)).containsExactly("ABCD");
-  }
-
-  @Test
-  public void select_views_and_sub_views() {
-    db.prepareDbUnit(getClass(), "shared_views.xml");
-
-    assertThat(sut.selectAllViewsAndSubViews(session)).extracting("uuid").containsOnly("ABCD", "EFGH", "FGHI", "IJKL");
-    assertThat(sut.selectAllViewsAndSubViews(session)).extracting("projectUuid").containsOnly("ABCD", "EFGH", "IJKL");
-  }
-
-  @Test
-  public void select_projects_from_view() {
-    db.prepareDbUnit(getClass(), "shared_views.xml");
-
-    assertThat(sut.selectProjectsFromView(session, "ABCD", "ABCD")).containsOnly("JKLM");
-    assertThat(sut.selectProjectsFromView(session, "EFGH", "EFGH")).containsOnly("KLMN", "JKLM");
-    assertThat(sut.selectProjectsFromView(session, "FGHI", "EFGH")).containsOnly("JKLM");
-    assertThat(sut.selectProjectsFromView(session, "IJKL", "IJKL")).isEmpty();
-    assertThat(sut.selectProjectsFromView(session, "Unknown", "Unknown")).isEmpty();
-  }
-
-  @Test
-  public void select_provisioned_projects() {
-    db.prepareDbUnit(getClass(), "select_provisioned_projects.xml");
-
-    List<ComponentDto> result = sut.selectProvisionedProjects(session, new SearchOptions(), null);
-    ComponentDto project = result.get(0);
-
-    assertThat(result).hasSize(1);
-    assertThat(project.getKey()).isEqualTo("org.provisioned.project");
-  }
-
-  @Test
-  public void count_provisioned_projects() {
-    db.prepareDbUnit(getClass(), "select_provisioned_projects.xml");
+  public void update() {
+    db.prepareDbUnit(getClass(), "update.xml");
 
-    int numberOfProjects = sut.countProvisionedProjects(session, null);
-
-    assertThat(numberOfProjects).isEqualTo(1);
-  }
-
-  @Test
-  public void select_ghost_projects() throws Exception {
-    db.prepareDbUnit(getClass(), "select_ghost_projects.xml");
+    ComponentDto componentDto = new ComponentDto()
+      .setUuid("GHIJ")
+      .setProjectUuid("DCBA")
+      .setModuleUuid("HGFE")
+      .setModuleUuidPath(".DCBA.HGFE.")
+      .setKey("org.struts:struts-core:src/org/struts/RequestContext2.java")
+      .setDeprecatedKey("org.struts:struts-core:src/org/struts/RequestContext2.java")
+      .setName("RequestContext2.java")
+      .setLongName("org.struts.RequestContext2")
+      .setQualifier("LIF")
+      .setScope("LIF")
+      .setLanguage("java2")
+      .setDescription("description2")
+      .setPath("src/org/struts/RequestContext2.java")
+      .setParentProjectId(4L)
+      .setCopyResourceId(6L)
+      .setEnabled(false)
+      .setAuthorizationUpdatedAt(12345678910L);
 
-    List<ComponentDto> result = sut.selectGhostProjects(session, null, new SearchOptions());
+    sut.update(session, componentDto);
+    session.commit();
 
-    assertThat(result).hasSize(1);
-    assertThat(result.get(0).key()).isEqualTo("org.ghost.project");
-    assertThat(sut.countGhostProjects(session, null)).isEqualTo(1);
+    db.assertDbUnit(getClass(), "update-result.xml", "projects");
   }
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistComponentsStepTest.java
new file mode 100644 (file)
index 0000000..33a3240
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * 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.computation.step;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.batch.protocol.Constants;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.protocol.output.BatchReportReader;
+import org.sonar.batch.protocol.output.BatchReportWriter;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.component.ComponentTesting;
+import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.computation.ComputationContext;
+import org.sonar.server.db.DbClient;
+import org.sonar.test.DbTests;
+
+import java.io.File;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Category(DbTests.class)
+public class PersistComponentsStepTest extends BaseStepTest {
+
+  @ClassRule
+  public static DbTester dbTester = new DbTester();
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  File reportDir;
+
+  DbSession session;
+
+  DbClient dbClient;
+
+  PersistComponentsStep sut;
+
+  @Before
+  public void setup() throws Exception {
+    dbTester.truncateTables();
+    session = dbTester.myBatis().openSession(false);
+    dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao());
+
+    reportDir = temp.newFolder();
+
+    sut = new PersistComponentsStep(dbClient);
+  }
+
+  @Override
+  protected ComputationStep step() {
+    return sut;
+  }
+
+  @After
+  public void tearDown() {
+    session.close();
+  }
+
+  @Test
+  public void persist_components() throws Exception {
+    File reportDir = temp.newFolder();
+    BatchReportWriter writer = new BatchReportWriter(reportDir);
+    writer.writeMetadata(BatchReport.Metadata.newBuilder()
+      .setRootComponentRef(1)
+      .build());
+
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(1)
+      .setType(Constants.ComponentType.PROJECT)
+      .setKey("PROJECT_KEY")
+      .setName("Project")
+      .setDescription("Project description")
+      .addChildRef(2)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(2)
+      .setType(Constants.ComponentType.MODULE)
+      .setKey("MODULE_KEY")
+      .setName("Module")
+      .setDescription("Module description")
+      .addChildRef(3)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(3)
+      .setType(Constants.ComponentType.DIRECTORY)
+      .setPath("src/main/java/dir")
+      .addChildRef(4)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(4)
+      .setType(Constants.ComponentType.FILE)
+      .setPath("src/main/java/dir/Foo.java")
+      .setLanguage("java")
+      .build());
+
+    sut.execute(new ComputationContext(new BatchReportReader(reportDir), ComponentTesting.newProjectDto()));
+
+    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4);
+
+    ComponentDto project = dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY");
+    assertThat(project).isNotNull();
+    assertThat(project.name()).isEqualTo("Project");
+    assertThat(project.description()).isEqualTo("Project description");
+    assertThat(project.uuid()).isNotNull();
+    assertThat(project.moduleUuid()).isNull();
+    assertThat(project.moduleUuidPath()).isEqualTo("." + project.uuid() + ".");
+    assertThat(project.projectUuid()).isEqualTo(project.uuid());
+    assertThat(project.qualifier()).isEqualTo("TRK");
+    assertThat(project.scope()).isEqualTo("PRJ");
+    assertThat(project.parentProjectId()).isNull();
+
+    ComponentDto module = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY");
+    assertThat(module).isNotNull();
+    assertThat(module.name()).isEqualTo("Module");
+    assertThat(module.description()).isEqualTo("Module description");
+    assertThat(module.uuid()).isNotNull();
+    assertThat(module.moduleUuid()).isEqualTo(project.uuid());
+    assertThat(module.moduleUuidPath()).isEqualTo(project.moduleUuidPath() + module.uuid() + ".");
+    assertThat(module.projectUuid()).isEqualTo(project.uuid());
+    assertThat(module.qualifier()).isEqualTo("BRC");
+    assertThat(module.scope()).isEqualTo("PRJ");
+    assertThat(module.parentProjectId()).isEqualTo(project.getId());
+
+    ComponentDto directory = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir");
+    assertThat(directory).isNotNull();
+    assertThat(directory.name()).isEqualTo("src/main/java/dir");
+    assertThat(directory.path()).isEqualTo("src/main/java/dir");
+    assertThat(directory.uuid()).isNotNull();
+    assertThat(directory.moduleUuid()).isEqualTo(module.uuid());
+    assertThat(directory.moduleUuidPath()).isEqualTo(module.moduleUuidPath());
+    assertThat(directory.projectUuid()).isEqualTo(project.uuid());
+    assertThat(directory.qualifier()).isEqualTo("DIR");
+    assertThat(directory.scope()).isEqualTo("DIR");
+    assertThat(directory.parentProjectId()).isEqualTo(module.getId());
+
+    ComponentDto file = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir/Foo.java");
+    assertThat(file).isNotNull();
+    assertThat(file.name()).isEqualTo("Foo.java");
+    assertThat(file.path()).isEqualTo("src/main/java/dir/Foo.java");
+    assertThat(file.language()).isEqualTo("java");
+    assertThat(file.uuid()).isNotNull();
+    assertThat(file.moduleUuid()).isEqualTo(module.uuid());
+    assertThat(file.moduleUuidPath()).isEqualTo(module.moduleUuidPath());
+    assertThat(file.projectUuid()).isEqualTo(project.uuid());
+    assertThat(file.qualifier()).isEqualTo("FIL");
+    assertThat(file.scope()).isEqualTo("FIL");
+    assertThat(file.parentProjectId()).isEqualTo(module.getId());
+  }
+
+  @Test
+  public void persist_file_directly_attached_on_root_directory() throws Exception {
+    File reportDir = temp.newFolder();
+    BatchReportWriter writer = new BatchReportWriter(reportDir);
+    writer.writeMetadata(BatchReport.Metadata.newBuilder()
+      .setRootComponentRef(1)
+      .build());
+
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(1)
+      .setType(Constants.ComponentType.PROJECT)
+      .setKey("PROJECT_KEY")
+      .setName("Project")
+      .addChildRef(2)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(2)
+      .setType(Constants.ComponentType.DIRECTORY)
+      .setPath("/")
+      .addChildRef(3)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(3)
+      .setType(Constants.ComponentType.FILE)
+      .setPath("pom.xml")
+      .build());
+
+    sut.execute(new ComputationContext(new BatchReportReader(reportDir), ComponentTesting.newProjectDto()));
+
+    ComponentDto directory = dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY:/");
+    assertThat(directory).isNotNull();
+    assertThat(directory.name()).isEqualTo("/");
+    assertThat(directory.path()).isEqualTo("/");
+
+    ComponentDto file = dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY:pom.xml");
+    assertThat(file).isNotNull();
+    assertThat(file.name()).isEqualTo("pom.xml");
+    assertThat(file.path()).isEqualTo("pom.xml");
+  }
+
+  @Test
+  public void persist_unit_test() throws Exception {
+    File reportDir = temp.newFolder();
+    BatchReportWriter writer = new BatchReportWriter(reportDir);
+    writer.writeMetadata(BatchReport.Metadata.newBuilder()
+      .setRootComponentRef(1)
+      .build());
+
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(1)
+      .setType(Constants.ComponentType.PROJECT)
+      .setKey("PROJECT_KEY")
+      .setName("Project")
+      .addChildRef(2)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(2)
+      .setType(Constants.ComponentType.DIRECTORY)
+      .setPath("src/test/java/dir")
+      .addChildRef(3)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(3)
+      .setType(Constants.ComponentType.FILE)
+      .setPath("src/test/java/dir/FooTest.java")
+      .setIsTest(true)
+      .build());
+
+    sut.execute(new ComputationContext(new BatchReportReader(reportDir), ComponentTesting.newProjectDto()));
+
+    ComponentDto file = dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY:src/test/java/dir/FooTest.java");
+    assertThat(file).isNotNull();
+    assertThat(file.name()).isEqualTo("FooTest.java");
+    assertThat(file.path()).isEqualTo("src/test/java/dir/FooTest.java");
+    assertThat(file.qualifier()).isEqualTo("UTS");
+    assertThat(file.scope()).isEqualTo("FIL");
+  }
+
+  @Test
+  public void use_latest_module_for_files_key() throws Exception {
+    File reportDir = temp.newFolder();
+    BatchReportWriter writer = new BatchReportWriter(reportDir);
+    writer.writeMetadata(BatchReport.Metadata.newBuilder()
+      .setRootComponentRef(1)
+      .build());
+
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(1)
+      .setType(Constants.ComponentType.PROJECT)
+      .setKey("PROJECT_KEY")
+      .setName("Project")
+      .addChildRef(2)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(2)
+      .setType(Constants.ComponentType.MODULE)
+      .setKey("MODULE_KEY")
+      .setName("Module")
+      .addChildRef(3)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(3)
+      .setType(Constants.ComponentType.MODULE)
+      .setKey("SUB_MODULE_KEY")
+      .setName("Sub Module")
+      .addChildRef(4)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(4)
+      .setType(Constants.ComponentType.DIRECTORY)
+      .setPath("src/main/java/dir")
+      .addChildRef(5)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(5)
+      .setType(Constants.ComponentType.FILE)
+      .setPath("src/main/java/dir/Foo.java")
+      .build());
+
+    sut.execute(new ComputationContext(new BatchReportReader(reportDir), ComponentTesting.newProjectDto()));
+
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY")).isNotNull();
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "SUB_MODULE_KEY")).isNotNull();
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "SUB_MODULE_KEY:src/main/java/dir")).isNotNull();
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "SUB_MODULE_KEY:src/main/java/dir/Foo.java")).isNotNull();
+  }
+
+  @Test
+  public void persist_with_branch() throws Exception {
+    File reportDir = temp.newFolder();
+    BatchReportWriter writer = new BatchReportWriter(reportDir);
+    writer.writeMetadata(BatchReport.Metadata.newBuilder()
+      .setRootComponentRef(1)
+      .setBranch("origin/master")
+      .build());
+
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(1)
+      .setType(Constants.ComponentType.PROJECT)
+      .setKey("PROJECT_KEY")
+      .setName("Project")
+      .addChildRef(2)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(2)
+      .setType(Constants.ComponentType.MODULE)
+      .setKey("MODULE_KEY")
+      .setName("Module")
+      .addChildRef(3)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(3)
+      .setType(Constants.ComponentType.DIRECTORY)
+      .setPath("src/main/java/dir")
+      .addChildRef(4)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(4)
+      .setType(Constants.ComponentType.FILE)
+      .setPath("src/main/java/dir/Foo.java")
+      .build());
+
+    sut.execute(new ComputationContext(new BatchReportReader(reportDir), ComponentTesting.newProjectDto()));
+
+    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4);
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY:origin/master")).isNotNull();
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:origin/master")).isNotNull();
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:origin/master:src/main/java/dir")).isNotNull();
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:origin/master:src/main/java/dir/Foo.java")).isNotNull();
+  }
+
+  @Test
+  public void persist_only_new_components() throws Exception {
+    // Project amd module already exists
+    ComponentDto project = ComponentTesting.newProjectDto("ABCD").setKey("PROJECT_KEY").setName("Project");
+    dbClient.componentDao().insert(session, project);
+    ComponentDto module = ComponentTesting.newModuleDto("BCDE", project).setKey("MODULE_KEY").setName("Module");
+    dbClient.componentDao().insert(session, module);
+    session.commit();
+
+    File reportDir = temp.newFolder();
+    BatchReportWriter writer = new BatchReportWriter(reportDir);
+    writer.writeMetadata(BatchReport.Metadata.newBuilder()
+      .setRootComponentRef(1)
+      .build());
+
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(1)
+      .setType(Constants.ComponentType.PROJECT)
+      .setKey("PROJECT_KEY")
+      .setName("Project")
+      .addChildRef(2)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(2)
+      .setType(Constants.ComponentType.MODULE)
+      .setKey("MODULE_KEY")
+      .setName("Module")
+      .addChildRef(3)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(3)
+      .setType(Constants.ComponentType.DIRECTORY)
+      .setPath("src/main/java/dir")
+      .addChildRef(4)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(4)
+      .setType(Constants.ComponentType.FILE)
+      .setPath("src/main/java/dir/Foo.java")
+      .build());
+
+    sut.execute(new ComputationContext(new BatchReportReader(reportDir), project));
+
+    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4);
+
+    ComponentDto projectReloaded = dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY");
+    assertThat(projectReloaded.getId()).isEqualTo(project.getId());
+    assertThat(projectReloaded.uuid()).isEqualTo(project.uuid());
+
+    ComponentDto moduleReloaded = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY");
+    assertThat(moduleReloaded.getId()).isEqualTo(module.getId());
+    assertThat(moduleReloaded.uuid()).isEqualTo(module.uuid());
+    assertThat(moduleReloaded.moduleUuid()).isEqualTo(module.moduleUuid());
+    assertThat(moduleReloaded.moduleUuidPath()).isEqualTo(module.moduleUuidPath());
+    assertThat(moduleReloaded.projectUuid()).isEqualTo(module.projectUuid());
+    assertThat(moduleReloaded.parentProjectId()).isEqualTo(module.parentProjectId());
+
+    ComponentDto directory = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir");
+    assertThat(directory).isNotNull();
+    assertThat(directory.moduleUuid()).isEqualTo(module.uuid());
+    assertThat(directory.moduleUuidPath()).isEqualTo(module.moduleUuidPath());
+    assertThat(directory.projectUuid()).isEqualTo(project.uuid());
+    assertThat(directory.parentProjectId()).isEqualTo(module.getId());
+
+    ComponentDto file = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir/Foo.java");
+    assertThat(file).isNotNull();
+    assertThat(file.moduleUuid()).isEqualTo(module.uuid());
+    assertThat(file.moduleUuidPath()).isEqualTo(module.moduleUuidPath());
+    assertThat(file.projectUuid()).isEqualTo(project.uuid());
+    assertThat(file.parentProjectId()).isEqualTo(module.getId());
+  }
+
+  @Test
+  public void nothing_to_persist() throws Exception {
+    ComponentDto project = ComponentTesting.newProjectDto("ABCD").setKey("PROJECT_KEY").setName("Project");
+    dbClient.componentDao().insert(session, project);
+    ComponentDto module = ComponentTesting.newModuleDto("BCDE", project).setKey("MODULE_KEY").setName("Module");
+    dbClient.componentDao().insert(session, module);
+    ComponentDto directory = ComponentTesting.newDirectory(module, "src/main/java/dir").setUuid("CDEF").setKey("MODULE_KEY:src/main/java/dir");
+    ComponentDto file = ComponentTesting.newFileDto(module, "DEFG").setPath("src/main/java/dir/Foo.java").setName("Foo.java").setKey("MODULE_KEY:src/main/java/dir/Foo.java");
+    dbClient.componentDao().insert(session, directory, file);
+    session.commit();
+
+    File reportDir = temp.newFolder();
+    BatchReportWriter writer = new BatchReportWriter(reportDir);
+    writer.writeMetadata(BatchReport.Metadata.newBuilder()
+      .setRootComponentRef(1)
+      .build());
+
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(1)
+      .setType(Constants.ComponentType.PROJECT)
+      .setKey("PROJECT_KEY")
+      .setName("Project")
+      .addChildRef(2)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(2)
+      .setType(Constants.ComponentType.MODULE)
+      .setKey("MODULE_KEY")
+      .setName("Module")
+      .addChildRef(3)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(3)
+      .setType(Constants.ComponentType.DIRECTORY)
+      .setPath("src/main/java/dir")
+      .addChildRef(4)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(4)
+      .setType(Constants.ComponentType.FILE)
+      .setPath("src/main/java/dir/Foo.java")
+      .build());
+
+    sut.execute(new ComputationContext(new BatchReportReader(reportDir), project));
+
+    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(4);
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY").getId()).isEqualTo(project.getId());
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY").getId()).isEqualTo(module.getId());
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir").getId()).isEqualTo(directory.getId());
+    assertThat(dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir/Foo.java").getId()).isEqualTo(file.getId());
+
+    ComponentDto projectReloaded = dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY");
+    assertThat(projectReloaded.getId()).isEqualTo(project.getId());
+    assertThat(projectReloaded.uuid()).isEqualTo(project.uuid());
+    assertThat(projectReloaded.moduleUuid()).isEqualTo(project.moduleUuid());
+    assertThat(projectReloaded.moduleUuidPath()).isEqualTo(project.moduleUuidPath());
+    assertThat(projectReloaded.projectUuid()).isEqualTo(project.projectUuid());
+    assertThat(projectReloaded.parentProjectId()).isEqualTo(project.parentProjectId());
+
+    ComponentDto moduleReloaded = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY");
+    assertThat(moduleReloaded.getId()).isEqualTo(module.getId());
+    assertThat(moduleReloaded.uuid()).isEqualTo(module.uuid());
+    assertThat(moduleReloaded.moduleUuid()).isEqualTo(module.moduleUuid());
+    assertThat(moduleReloaded.moduleUuidPath()).isEqualTo(module.moduleUuidPath());
+    assertThat(moduleReloaded.projectUuid()).isEqualTo(module.projectUuid());
+    assertThat(moduleReloaded.parentProjectId()).isEqualTo(module.parentProjectId());
+
+    ComponentDto directoryReloaded = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir");
+    assertThat(directoryReloaded).isNotNull();
+    assertThat(directoryReloaded.uuid()).isEqualTo(directory.uuid());
+    assertThat(directoryReloaded.moduleUuid()).isEqualTo(directory.moduleUuid());
+    assertThat(directoryReloaded.moduleUuidPath()).isEqualTo(directory.moduleUuidPath());
+    assertThat(directoryReloaded.projectUuid()).isEqualTo(directory.projectUuid());
+    assertThat(directoryReloaded.parentProjectId()).isEqualTo(directory.parentProjectId());
+    assertThat(directoryReloaded.name()).isEqualTo(directory.name());
+    assertThat(directoryReloaded.path()).isEqualTo(directory.path());
+
+    ComponentDto fileReloaded = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY:src/main/java/dir/Foo.java");
+    assertThat(fileReloaded).isNotNull();
+    assertThat(fileReloaded.uuid()).isEqualTo(file.uuid());
+    assertThat(fileReloaded.moduleUuid()).isEqualTo(file.moduleUuid());
+    assertThat(fileReloaded.moduleUuidPath()).isEqualTo(file.moduleUuidPath());
+    assertThat(fileReloaded.projectUuid()).isEqualTo(file.projectUuid());
+    assertThat(fileReloaded.parentProjectId()).isEqualTo(file.parentProjectId());
+    assertThat(fileReloaded.name()).isEqualTo(file.name());
+    assertThat(fileReloaded.path()).isEqualTo(file.path());
+  }
+
+  @Test
+  public void update_name_and_description() throws Exception {
+    ComponentDto project = ComponentTesting.newProjectDto("ABCD").setKey("PROJECT_KEY").setName("Project");
+    dbClient.componentDao().insert(session, project);
+    ComponentDto module = ComponentTesting.newModuleDto("BCDE", project).setKey("MODULE_KEY").setName("Module");
+    dbClient.componentDao().insert(session, module);
+    session.commit();
+
+    File reportDir = temp.newFolder();
+    BatchReportWriter writer = new BatchReportWriter(reportDir);
+    writer.writeMetadata(BatchReport.Metadata.newBuilder()
+      .setRootComponentRef(1)
+      .build());
+
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(1)
+      .setType(Constants.ComponentType.PROJECT)
+      .setKey("PROJECT_KEY")
+      .setName("New project name")
+      .setDescription("New project description")
+      .addChildRef(2)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(2)
+      .setType(Constants.ComponentType.MODULE)
+      .setKey("MODULE_KEY")
+      .setName("New module name")
+      .setDescription("New module description")
+      .build());
+
+    sut.execute(new ComputationContext(new BatchReportReader(reportDir), project));
+
+    ComponentDto projectReloaded = dbClient.componentDao().selectNullableByKey(session, "PROJECT_KEY");
+    assertThat(projectReloaded.name()).isEqualTo("New project name");
+    assertThat(projectReloaded.description()).isEqualTo("New project description");
+
+    ComponentDto moduleReloaded = dbClient.componentDao().selectNullableByKey(session, "MODULE_KEY");
+    assertThat(moduleReloaded.name()).isEqualTo("New module name");
+    assertThat(moduleReloaded.description()).isEqualTo("New module description");
+  }
+
+  @Test
+  public void update_module_uuid_when_moving_a_module() throws Exception {
+    ComponentDto project = ComponentTesting.newProjectDto("ABCD").setKey("PROJECT_KEY").setName("Project");
+    dbClient.componentDao().insert(session, project);
+    ComponentDto moduleB = ComponentTesting.newModuleDto("BCDE", project).setKey("MODULE_B").setName("Module B");
+    dbClient.componentDao().insert(session, moduleB);
+    ComponentDto directory = ComponentTesting.newDirectory(moduleB, "src/main/java/dir").setUuid("CDEF").setKey("MODULE_B:src/main/java/dir");
+    ComponentDto file = ComponentTesting.newFileDto(moduleB, "DEFG").setPath("src/main/java/dir/Foo.java").setName("Foo.java").setKey("MODULE_B:src/main/java/dir/Foo.java");
+    dbClient.componentDao().insert(session, directory, file);
+    session.commit();
+
+    File reportDir = temp.newFolder();
+    BatchReportWriter writer = new BatchReportWriter(reportDir);
+    writer.writeMetadata(BatchReport.Metadata.newBuilder()
+      .setRootComponentRef(1)
+      .build());
+
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(1)
+      .setType(Constants.ComponentType.PROJECT)
+      .setKey("PROJECT_KEY")
+      .setName("Project")
+      .addChildRef(2)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(2)
+      .setType(Constants.ComponentType.MODULE)
+      .setKey("MODULE_A")
+      .setName("Module A")
+      .addChildRef(3)
+      .build());
+    // Module B is now a sub module of module A
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(3)
+      .setType(Constants.ComponentType.MODULE)
+      .setKey("MODULE_B")
+      .setName("Module B")
+      .addChildRef(4)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(4)
+      .setType(Constants.ComponentType.DIRECTORY)
+      .setPath("src/main/java/dir")
+      .addChildRef(5)
+      .build());
+    writer.writeComponent(BatchReport.Component.newBuilder()
+      .setRef(5)
+      .setType(Constants.ComponentType.FILE)
+      .setPath("src/main/java/dir/Foo.java")
+      .build());
+
+    sut.execute(new ComputationContext(new BatchReportReader(reportDir), project));
+
+    assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(5);
+
+    ComponentDto moduleA = dbClient.componentDao().selectNullableByKey(session, "MODULE_A");
+    assertThat(moduleA).isNotNull();
+
+    ComponentDto moduleBReloaded = dbClient.componentDao().selectNullableByKey(session, "MODULE_B");
+    assertThat(moduleBReloaded).isNotNull();
+    assertThat(moduleBReloaded.uuid()).isEqualTo(moduleB.uuid());
+    assertThat(moduleBReloaded.moduleUuid()).isEqualTo(moduleA.uuid());
+    assertThat(moduleBReloaded.moduleUuidPath()).isEqualTo(moduleA.moduleUuidPath() + moduleBReloaded.uuid() + ".");
+    assertThat(moduleBReloaded.projectUuid()).isEqualTo(project.uuid());
+    assertThat(moduleBReloaded.parentProjectId()).isEqualTo(moduleA.getId());
+
+    ComponentDto directoryReloaded = dbClient.componentDao().selectNullableByKey(session, "MODULE_B:src/main/java/dir");
+    assertThat(directoryReloaded).isNotNull();
+    assertThat(directoryReloaded.uuid()).isEqualTo(directory.uuid());
+    assertThat(directoryReloaded.moduleUuid()).isEqualTo(moduleBReloaded.uuid());
+    assertThat(directoryReloaded.moduleUuidPath()).isEqualTo(moduleBReloaded.moduleUuidPath());
+    assertThat(directoryReloaded.projectUuid()).isEqualTo(project.uuid());
+    assertThat(directoryReloaded.parentProjectId()).isEqualTo(moduleBReloaded.getId());
+
+    ComponentDto fileReloaded = dbClient.componentDao().selectNullableByKey(session, "MODULE_B:src/main/java/dir/Foo.java");
+    assertThat(fileReloaded).isNotNull();
+    assertThat(fileReloaded.uuid()).isEqualTo(file.uuid());
+    assertThat(fileReloaded.moduleUuid()).isEqualTo(moduleBReloaded.uuid());
+    assertThat(fileReloaded.moduleUuidPath()).isEqualTo(moduleBReloaded.moduleUuidPath());
+    assertThat(fileReloaded.projectUuid()).isEqualTo(project.uuid());
+    assertThat(fileReloaded.parentProjectId()).isEqualTo(moduleBReloaded.getId());
+  }
+
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/find_project_uuids.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/find_project_uuids.xml
deleted file mode 100644 (file)
index fbf078c..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-<dataset>
-
-  <!-- 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" />
-
-  <!-- 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]" />
-
-  <!-- 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]" />
-
-  <!-- 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]" />
-
-  <!-- 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 -->
-  <projects id="20" kee="DEV:developer@company.net" name="developer@company.net" long_name="Developer" scope="PRJ" qualifier="DEV" root_id="[null]" description="[null]"
-            uuid="DEV" project_uuid="[null]" module_uuid="[null]" module_uuid_path="."
-            enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]"
-            created_at="2014-09-01" authorization_updated_at="[null]"/>
-
-  <!-- View -->
-  <projects id="30" kee="view" name="View" long_name="View" scope="PRJ" qualifier="VW" root_id="[null]" description="[null]"
-            uuid="VIEW" project_uuid="[null]" module_uuid="[null]" module_uuid_path="."
-            enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]"
-            created_at="2014-09-01" authorization_updated_at="[null]"/>
-
-</dataset>
index 98615e2214e067658f5e44fc2adc4e47b54b1f0d..bf39c3a305b86d3c49ec692fb4e6176311083a69 100644 (file)
@@ -4,7 +4,7 @@
             name="RequestContext.java" long_name="org.struts.RequestContext"
             uuid="GHIJ" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path=".ABCD.EFGH."
       qualifier="FIL" scope="FIL" language="java" path="src/org/struts/RequestContext.java" root_id="3"
-            description="[null]" enabled="[true]" copy_resource_id="5" person_id="[null]"
+            description="description" enabled="[true]" copy_resource_id="5" person_id="[null]"
       authorization_updated_at="123456789" created_at="2014-06-18"
       />
 
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/update-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/update-result.xml
new file mode 100644 (file)
index 0000000..8b0e839
--- /dev/null
@@ -0,0 +1,11 @@
+<dataset>
+
+  <projects id="1" kee="org.struts:struts-core:src/org/struts/RequestContext2.java" deprecated_kee="org.struts:struts-core:src/org/struts/RequestContext2.java"
+            name="RequestContext2.java" long_name="org.struts.RequestContext2"
+            uuid="GHIJ" project_uuid="DCBA" module_uuid="HGFE" module_uuid_path=".DCBA.HGFE."
+            qualifier="LIF" scope="LIF" language="java2" path="src/org/struts/RequestContext2.java" root_id="4"
+            description="description2" enabled="[false]" copy_resource_id="6" person_id="[null]"
+            authorization_updated_at="12345678910" created_at="2014-06-18"
+      />
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/update.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/update.xml
new file mode 100644 (file)
index 0000000..bf39c3a
--- /dev/null
@@ -0,0 +1,11 @@
+<dataset>
+
+  <projects id="1" kee="org.struts:struts-core:src/org/struts/RequestContext.java" deprecated_kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+            name="RequestContext.java" long_name="org.struts.RequestContext"
+            uuid="GHIJ" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path=".ABCD.EFGH."
+      qualifier="FIL" scope="FIL" language="java" path="src/org/struts/RequestContext.java" root_id="3"
+            description="description" enabled="[true]" copy_resource_id="5" person_id="[null]"
+      authorization_updated_at="123456789" created_at="2014-06-18"
+      />
+
+</dataset>
index 5c934b984901c7357dce07d9e9a9483e960c726d..93a2feb82eebedab694672249655c669fcba59f1 100644 (file)
@@ -49,6 +49,7 @@ public class ComponentDto implements Component {
   private String name;
   private String longName;
   private String language;
+  private String description;
   private boolean enabled = true;
 
   private Date createdAt;
@@ -185,6 +186,16 @@ public class ComponentDto implements Component {
     return this;
   }
 
+  @CheckForNull
+  public String description() {
+    return description;
+  }
+
+  public ComponentDto setDescription(@Nullable String description) {
+    this.description = description;
+    return this;
+  }
+
   @CheckForNull
   public Long parentProjectId() {
     return parentProjectId;
index 643c34ddc190776574454b3814bf089950b3893b..be3f2b1241219cdf57abedb8b8cb740286c47a74 100644 (file)
@@ -93,6 +93,11 @@ public interface ComponentMapper {
    */
   List<UuidWithProjectUuidDto> selectUuidsForQualifiers(@Param("qualifiers") String... qualifiers);
 
+  /**
+   * Return all components of a project
+   */
+  List<ComponentDto> selectComponentsFromProjectUuid(@Param("projectUuid") String projectUuid);
+
   /**
    * Return technical projects from a view or a sub-view
    */
@@ -100,8 +105,6 @@ public interface ComponentMapper {
 
   long countById(long id);
 
-  void insert(ComponentDto rule);
-
   List<ComponentDto> selectProvisionedProjects(Map<String, String> parameters, RowBounds rowBounds);
 
   int countProvisionedProjects(Map<String, String> parameters);
@@ -109,4 +112,8 @@ public interface ComponentMapper {
   List<ComponentDto> selectGhostProjects(Map<String, String> parameters, RowBounds rowBounds);
 
   long countGhostProjects(Map<String, String> parameters);
+
+  void insert(ComponentDto componentDto);
+
+  void update(ComponentDto componentDto);
 }
index 54e3ffd9754337034a87394ef3efea7c5f9fa60e..f7690f358a027745af83a930e317e61daaa3919c 100644 (file)
@@ -12,6 +12,7 @@
     p.deprecated_kee as deprecatedKey,
     p.name as name,
     p.long_name as longName,
+    p.description as description,
     p.qualifier as qualifier,
     p.scope as scope,
     p.language as language,
     </where>
   </select>
 
-  <sql id="insertColumns">
-    (kee, deprecated_kee, uuid, project_uuid, module_uuid, module_uuid_path, name, long_name, qualifier, scope, language, root_id, path, copy_resource_id, enabled,
-    created_at, authorization_updated_at)
-  </sql>
-
-  <insert id="insert" parameterType="Component" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
-    insert into projects <include refid="insertColumns"/>
-    values (#{kee}, #{deprecatedKey}, #{uuid}, #{projectUuid}, #{moduleUuid}, #{moduleUuidPath}, #{name}, #{longName}, #{qualifier}, #{scope},
-    #{language}, #{parentProjectId}, #{path}, #{copyResourceId}, #{enabled}, #{createdAt}, #{authorizationUpdatedAt})
-  </insert>
+  <select id="selectComponentsFromProjectUuid" parameterType="map" resultType="Component">
+    SELECT <include refid="componentColumns"/>
+    FROM projects p
+    <where>
+      AND p.project_uuid=#{projectUuid}
+      AND p.enabled=${_true}
+    </where>
+  </select>
 
   <select id="selectProvisionedProjects" parameterType="map" resultType="Component">
     select <include refid="componentColumns"/>
     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>
+    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>
+
+  <insert id="insert" parameterType="Component" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
+    INSERT INTO projects (kee, deprecated_kee, uuid, project_uuid, module_uuid, module_uuid_path, name, long_name, qualifier, scope, language, description, root_id, path, copy_resource_id, enabled,
+    created_at, authorization_updated_at)
+    VALUES (#{kee,jdbcType=VARCHAR}, #{deprecatedKey,jdbcType=VARCHAR}, #{uuid,jdbcType=VARCHAR}, #{projectUuid,jdbcType=VARCHAR}, #{moduleUuid,jdbcType=VARCHAR}, #{moduleUuidPath,jdbcType=VARCHAR},
+            #{name,jdbcType=VARCHAR}, #{longName,jdbcType=VARCHAR}, #{qualifier,jdbcType=VARCHAR}, #{scope,jdbcType=VARCHAR}, #{language,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR},
+            #{parentProjectId,jdbcType=BIGINT}, #{path,jdbcType=VARCHAR}, #{copyResourceId,jdbcType=BIGINT}, #{enabled,jdbcType=BOOLEAN},
+            #{createdAt,jdbcType=TIMESTAMP}, #{authorizationUpdatedAt,jdbcType=BIGINT})
+  </insert>
+
+  <insert id="update" parameterType="Component">
+    UPDATE projects SET
+      kee=#{kee,jdbcType=VARCHAR},
+      deprecated_kee=#{deprecatedKey,jdbcType=VARCHAR},
+      project_uuid=#{projectUuid,jdbcType=VARCHAR},
+      module_uuid=#{moduleUuid,jdbcType=VARCHAR},
+      module_uuid_path=#{moduleUuidPath,jdbcType=VARCHAR},
+      name=#{name,jdbcType=VARCHAR},
+      long_name=#{longName,jdbcType=VARCHAR},
+      qualifier=#{qualifier,jdbcType=VARCHAR},
+      scope=#{scope,jdbcType=VARCHAR},
+      language=#{language,jdbcType=VARCHAR},
+      description=#{description,jdbcType=VARCHAR},
+      root_id=#{parentProjectId,jdbcType=BIGINT},
+      path=#{path,jdbcType=VARCHAR},
+      copy_resource_id=#{copyResourceId,jdbcType=BIGINT},
+      enabled=#{enabled,jdbcType=BOOLEAN},
+      authorization_updated_at=#{authorizationUpdatedAt,jdbcType=BIGINT}
+    WHERE uuid=#{uuid}
+  </insert>
+
 </mapper>
index 5383b6472d16259ddd58bd27dc4b5f07e1e243f7..bd030b5dd789bd4a8f8f65528f710fcb53fdc50b 100644 (file)
@@ -39,6 +39,7 @@ public class ComponentDtoTest {
       .setQualifier("FIL")
       .setScope("FIL")
       .setLanguage("java")
+      .setDescription("desc")
       .setPath("src/org/struts/RequestContext.java")
       .setCopyResourceId(5L)
       .setParentProjectId(3L)
@@ -53,6 +54,7 @@ public class ComponentDtoTest {
     assertThat(componentDto.scope()).isEqualTo("FIL");
     assertThat(componentDto.path()).isEqualTo("src/org/struts/RequestContext.java");
     assertThat(componentDto.language()).isEqualTo("java");
+    assertThat(componentDto.description()).isEqualTo("desc");
     assertThat(componentDto.parentProjectId()).isEqualTo(3L);
     assertThat(componentDto.getCopyResourceId()).isEqualTo(5L);
     assertThat(componentDto.getAuthorizationUpdatedAt()).isEqualTo(123456789L);