@@ -0,0 +1,49 @@ | |||
/* | |||
* 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.db; | |||
import org.sonar.api.ServerComponent; | |||
import org.sonar.core.component.ComponentLinkDto; | |||
import org.sonar.core.component.db.ComponentLinkMapper; | |||
import org.sonar.core.persistence.DaoComponent; | |||
import org.sonar.core.persistence.DbSession; | |||
import java.util.List; | |||
public class ComponentLinkDao implements ServerComponent, DaoComponent { | |||
public List<ComponentLinkDto> selectByComponentUuid(DbSession session, String componentUuid) { | |||
return session.getMapper(ComponentLinkMapper.class).selectByComponentUuid(componentUuid); | |||
} | |||
public void insert(DbSession session, ComponentLinkDto item) { | |||
session.getMapper(ComponentLinkMapper.class).insert(item); | |||
} | |||
public void update(DbSession session, ComponentLinkDto item) { | |||
session.getMapper(ComponentLinkMapper.class).update(item); | |||
} | |||
public void delete(DbSession session, long id) { | |||
session.getMapper(ComponentLinkMapper.class).delete(id); | |||
} | |||
} |
@@ -42,6 +42,7 @@ public class ComputationSteps { | |||
SwitchSnapshotStep.class, | |||
IndexComponentsStep.class, | |||
PurgeDatastoresStep.class, | |||
PersistComponentLinksStep.class, | |||
// ES indexing is done after all db changes | |||
ApplyPermissionsStep.class, |
@@ -0,0 +1,147 @@ | |||
/* | |||
* 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.base.Predicate; | |||
import com.google.common.collect.Iterables; | |||
import org.sonar.api.i18n.I18n; | |||
import org.sonar.api.resources.Qualifiers; | |||
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.ComponentLinkDto; | |||
import org.sonar.core.persistence.DbSession; | |||
import org.sonar.core.persistence.MyBatis; | |||
import org.sonar.server.computation.ComputationContext; | |||
import org.sonar.server.db.DbClient; | |||
import javax.annotation.Nullable; | |||
import java.util.List; | |||
import java.util.Locale; | |||
import java.util.Set; | |||
import static com.google.common.collect.Sets.newHashSet; | |||
public class PersistComponentLinksStep implements ComputationStep { | |||
private final DbClient dbClient; | |||
private final I18n i18n; | |||
public PersistComponentLinksStep(DbClient dbClient, I18n i18n) { | |||
this.dbClient = dbClient; | |||
this.i18n = i18n; | |||
} | |||
@Override | |||
public String[] supportedProjectQualifiers() { | |||
return new String[] {Qualifiers.PROJECT}; | |||
} | |||
@Override | |||
public void execute(ComputationContext context) { | |||
DbSession session = dbClient.openSession(false); | |||
try { | |||
int rootComponentRef = context.getReportMetadata().getRootComponentRef(); | |||
recursivelyProcessComponent(session, context, rootComponentRef); | |||
session.commit(); | |||
} finally { | |||
MyBatis.closeQuietly(session); | |||
} | |||
} | |||
private void recursivelyProcessComponent(DbSession session, ComputationContext context, int componentRef) { | |||
BatchReportReader reportReader = context.getReportReader(); | |||
BatchReport.Component component = reportReader.readComponent(componentRef); | |||
processLinks(session, component); | |||
for (Integer childRef : component.getChildRefsList()) { | |||
recursivelyProcessComponent(session, context, childRef); | |||
} | |||
} | |||
private void processLinks(DbSession session, BatchReport.Component component) { | |||
if (component.getType().equals(Constants.ComponentType.PROJECT) || component.getType().equals(Constants.ComponentType.MODULE)) { | |||
List<BatchReport.ComponentLink> links = component.getLinksList(); | |||
List<ComponentLinkDto> previousLinks = dbClient.componentLinkDao().selectByComponentUuid(session, component.getUuid()); | |||
mergeLinks(session, component.getUuid(), links, previousLinks); | |||
} | |||
} | |||
private void mergeLinks(DbSession session, String componentUuid, List<BatchReport.ComponentLink> links, List<ComponentLinkDto> previousLinks) { | |||
Set<String> linkType = newHashSet(); | |||
for (final BatchReport.ComponentLink link : links) { | |||
String type = convertType(link.getType()); | |||
if (!linkType.contains(type)) { | |||
linkType.add(type); | |||
} else { | |||
throw new IllegalArgumentException(String.format("Link of type '%s' has already been declared on component '%s'", type, componentUuid)); | |||
} | |||
ComponentLinkDto previousLink = Iterables.find(previousLinks, new Predicate<ComponentLinkDto>() { | |||
@Override | |||
public boolean apply(@Nullable ComponentLinkDto input) { | |||
return input != null && input.getType().equals(convertType(link.getType())); | |||
} | |||
}, null); | |||
if (previousLink == null) { | |||
dbClient.componentLinkDao().insert(session, | |||
new ComponentLinkDto() | |||
.setComponentUuid(componentUuid) | |||
.setType(type) | |||
.setName(i18n.message(Locale.ENGLISH, "project_links." + type, null)) | |||
.setHref(link.getHref()) | |||
); | |||
} else { | |||
previousLink.setHref(link.getHref()); | |||
dbClient.componentLinkDao().update(session, previousLink); | |||
} | |||
} | |||
for (ComponentLinkDto dto : previousLinks) { | |||
if (!linkType.contains(dto.getType())) { | |||
dbClient.componentLinkDao().delete(session, dto.getId()); | |||
} | |||
} | |||
} | |||
private static String convertType(Constants.ComponentLinkType type) { | |||
switch (type) { | |||
case HOME: | |||
return "homepage"; | |||
case SCM: | |||
return "scm"; | |||
case SCM_DEV: | |||
return "scm_dev"; | |||
case CI: | |||
return "ci"; | |||
case ISSUE: | |||
return "issue"; | |||
default: | |||
throw new IllegalArgumentException(String.format("Unsupported type %s", type.name())); | |||
} | |||
} | |||
@Override | |||
public String getDescription() { | |||
return "Persist component links"; | |||
} | |||
} |
@@ -37,6 +37,7 @@ import org.sonar.core.user.AuthorizationDao; | |||
import org.sonar.server.activity.db.ActivityDao; | |||
import org.sonar.server.component.db.ComponentDao; | |||
import org.sonar.server.component.db.ComponentIndexDao; | |||
import org.sonar.server.component.db.ComponentLinkDao; | |||
import org.sonar.server.component.db.SnapshotDao; | |||
import org.sonar.server.computation.db.AnalysisReportDao; | |||
import org.sonar.server.dashboard.db.DashboardDao; | |||
@@ -89,6 +90,7 @@ public class DbClient implements ServerComponent { | |||
private final FileSourceDao fileSourceDao; | |||
private final AuthorDao authorDao; | |||
private final ComponentIndexDao componentIndexDao; | |||
private final ComponentLinkDao componentLinkDao; | |||
public DbClient(Database db, MyBatis myBatis, DaoComponent... daoComponents) { | |||
this.db = db; | |||
@@ -123,6 +125,7 @@ public class DbClient implements ServerComponent { | |||
fileSourceDao = getDao(map, FileSourceDao.class); | |||
authorDao = getDao(map, AuthorDao.class); | |||
componentIndexDao = getDao(map, ComponentIndexDao.class); | |||
componentLinkDao = getDao(map, ComponentLinkDao.class); | |||
} | |||
public Database database() { | |||
@@ -233,6 +236,10 @@ public class DbClient implements ServerComponent { | |||
return componentIndexDao; | |||
} | |||
public ComponentLinkDao componentLinkDao() { | |||
return componentLinkDao; | |||
} | |||
private <K> K getDao(Map<Class, DaoComponent> map, Class<K> clazz) { | |||
return (K) map.get(clazz); | |||
} |
@@ -0,0 +1,117 @@ | |||
/* | |||
* 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.db; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.ClassRule; | |||
import org.junit.Test; | |||
import org.junit.experimental.categories.Category; | |||
import org.sonar.core.component.ComponentLinkDto; | |||
import org.sonar.core.persistence.DbSession; | |||
import org.sonar.core.persistence.DbTester; | |||
import org.sonar.test.DbTests; | |||
import java.util.List; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@Category(DbTests.class) | |||
public class ComponentLinkDaoTest { | |||
@ClassRule | |||
public static DbTester dbTester = new DbTester(); | |||
DbSession session; | |||
ComponentLinkDao dao; | |||
@Before | |||
public void createDao() throws Exception { | |||
session = dbTester.myBatis().openSession(false); | |||
dao = new ComponentLinkDao(); | |||
} | |||
@After | |||
public void tearDown() throws Exception { | |||
session.close(); | |||
} | |||
@Test | |||
public void select_by_component_uuid() throws Exception { | |||
dbTester.prepareDbUnit(getClass(), "shared.xml"); | |||
List<ComponentLinkDto> links = dao.selectByComponentUuid(session, "ABCD"); | |||
assertThat(links).hasSize(2); | |||
links = dao.selectByComponentUuid(session, "BCDE"); | |||
assertThat(links).hasSize(1); | |||
ComponentLinkDto link = links.get(0); | |||
assertThat(link.getId()).isEqualTo(3L); | |||
assertThat(link.getComponentUuid()).isEqualTo("BCDE"); | |||
assertThat(link.getType()).isEqualTo("homepage"); | |||
assertThat(link.getName()).isEqualTo("Home"); | |||
assertThat(link.getHref()).isEqualTo("http://www.struts.org"); | |||
} | |||
@Test | |||
public void insert() throws Exception { | |||
dbTester.prepareDbUnit(getClass(), "empty.xml"); | |||
dao.insert(session, new ComponentLinkDto() | |||
.setComponentUuid("ABCD") | |||
.setType("homepage") | |||
.setName("Home") | |||
.setHref("http://www.sonarqube.org") | |||
); | |||
session.commit(); | |||
dbTester.assertDbUnit(getClass(), "insert.xml", "project_links"); | |||
} | |||
@Test | |||
public void update() throws Exception { | |||
dbTester.prepareDbUnit(getClass(), "update.xml"); | |||
dao.update(session, new ComponentLinkDto() | |||
.setId(1L) | |||
.setComponentUuid("ABCD") | |||
.setType("homepage") | |||
.setName("Home") | |||
.setHref("http://www.sonarqube.org") | |||
); | |||
session.commit(); | |||
dbTester.assertDbUnit(getClass(), "update-result.xml", "project_links"); | |||
} | |||
@Test | |||
public void delete() throws Exception { | |||
dbTester.prepareDbUnit(getClass(), "delete.xml"); | |||
dao.delete(session, 1L); | |||
session.commit(); | |||
assertThat(dbTester.countRowsOfTable("project_links")).isEqualTo(0); | |||
} | |||
} |
@@ -42,11 +42,12 @@ public class ComputationStepsTest { | |||
mock(SwitchSnapshotStep.class), | |||
mock(PurgeDatastoresStep.class), | |||
mock(SendIssueNotificationsStep.class), | |||
mock(IndexComponentsStep.class)); | |||
mock(IndexComponentsStep.class), | |||
mock(PersistComponentLinksStep.class)); | |||
assertThat(registry.orderedSteps()).hasSize(11); | |||
assertThat(registry.orderedSteps()).hasSize(12); | |||
assertThat(registry.orderedSteps().get(0)).isInstanceOf(ParseReportStep.class); | |||
assertThat(registry.orderedSteps().get(10)).isInstanceOf(SendIssueNotificationsStep.class); | |||
assertThat(registry.orderedSteps().get(11)).isInstanceOf(SendIssueNotificationsStep.class); | |||
} | |||
@Test |
@@ -0,0 +1,224 @@ | |||
/* | |||
* 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.*; | |||
import org.junit.experimental.categories.Category; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.i18n.I18n; | |||
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.db.ComponentLinkDao; | |||
import org.sonar.server.computation.ComputationContext; | |||
import org.sonar.server.db.DbClient; | |||
import org.sonar.test.DbTests; | |||
import java.io.File; | |||
import java.util.Locale; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
@Category(DbTests.class) | |||
public class PersistComponentLinksStepTest { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
@ClassRule | |||
public static DbTester dbTester = new DbTester(); | |||
DbSession session; | |||
ComponentLinkDao dao; | |||
I18n i18n; | |||
PersistComponentLinksStep step; | |||
@Before | |||
public void setup() throws Exception { | |||
session = dbTester.myBatis().openSession(false); | |||
dao = new ComponentLinkDao(); | |||
DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), dao); | |||
i18n = mock(I18n.class); | |||
when(i18n.message(Locale.ENGLISH, "project_links.homepage", null)).thenReturn("Home"); | |||
when(i18n.message(Locale.ENGLISH, "project_links.scm", null)).thenReturn("Sources"); | |||
when(i18n.message(Locale.ENGLISH, "project_links.scm_dev", null)).thenReturn("Developer connection"); | |||
when(i18n.message(Locale.ENGLISH, "project_links.ci", null)).thenReturn("Continuous integration"); | |||
when(i18n.message(Locale.ENGLISH, "project_links.issue", null)).thenReturn("Issues"); | |||
step = new PersistComponentLinksStep(dbClient, i18n); | |||
} | |||
@After | |||
public void tearDown() throws Exception { | |||
session.close(); | |||
} | |||
@Test | |||
public void execute_only_on_projects() throws Exception { | |||
assertThat(step.supportedProjectQualifiers()).containsOnly("TRK"); | |||
} | |||
@Test | |||
public void add_links_on_project_and_module() throws Exception { | |||
dbTester.prepareDbUnit(getClass(), "empty.xml"); | |||
File reportDir = temp.newFolder(); | |||
// project and 1 module | |||
BatchReportWriter writer = new BatchReportWriter(reportDir); | |||
writer.writeMetadata(BatchReport.Metadata.newBuilder() | |||
.setRootComponentRef(1) | |||
.setProjectKey("PROJECT_KEY") | |||
.setAnalysisDate(150000000L) | |||
.build()); | |||
writer.writeComponent(BatchReport.Component.newBuilder() | |||
.setRef(1) | |||
.setType(Constants.ComponentType.PROJECT) | |||
.setUuid("ABCD") | |||
.addChildRefs(2) | |||
.addLinks(BatchReport.ComponentLink.newBuilder().setType(Constants.ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build()) | |||
.addLinks(BatchReport.ComponentLink.newBuilder().setType(Constants.ComponentLinkType.SCM).setHref("https://github.com/SonarSource/sonar").build()) | |||
.addLinks(BatchReport.ComponentLink.newBuilder().setType(Constants.ComponentLinkType.SCM_DEV).setHref("scm:git:git@github.com:SonarSource/sonar.git/sonar").build()) | |||
.addLinks(BatchReport.ComponentLink.newBuilder().setType(Constants.ComponentLinkType.ISSUE).setHref("http://jira.sonarsource.com/").build()) | |||
.addLinks(BatchReport.ComponentLink.newBuilder().setType(Constants.ComponentLinkType.CI).setHref("http://bamboo.ci.codehaus.org/browse/SONAR").build()) | |||
.build()); | |||
writer.writeComponent(BatchReport.Component.newBuilder() | |||
.setRef(2) | |||
.setType(Constants.ComponentType.MODULE) | |||
.setUuid("BCDE") | |||
.addLinks(BatchReport.ComponentLink.newBuilder().setType(Constants.ComponentLinkType.SCM).setHref("https://github.com/SonarSource/sonar/server").build()) | |||
.build()); | |||
step.execute(new ComputationContext(new BatchReportReader(reportDir), mock(ComponentDto.class))); | |||
dbTester.assertDbUnit(getClass(), "add_links_on_project_and_module-result.xml", "project_links"); | |||
} | |||
@Test | |||
public void nothing_to_do_when_link_already_exists() throws Exception { | |||
dbTester.prepareDbUnit(getClass(), "nothing_to_do_when_link_already_exists.xml"); | |||
File reportDir = temp.newFolder(); | |||
BatchReportWriter writer = new BatchReportWriter(reportDir); | |||
writer.writeMetadata(BatchReport.Metadata.newBuilder() | |||
.setRootComponentRef(1) | |||
.setProjectKey("PROJECT_KEY") | |||
.setAnalysisDate(150000000L) | |||
.build()); | |||
writer.writeComponent(BatchReport.Component.newBuilder() | |||
.setRef(1) | |||
.setType(Constants.ComponentType.PROJECT) | |||
.setUuid("ABCD") | |||
.addLinks(BatchReport.ComponentLink.newBuilder().setType(Constants.ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build()) | |||
.build()); | |||
step.execute(new ComputationContext(new BatchReportReader(reportDir), mock(ComponentDto.class))); | |||
dbTester.assertDbUnit(getClass(), "nothing_to_do_when_link_already_exists.xml", "project_links"); | |||
} | |||
@Test | |||
public void update_link() throws Exception { | |||
dbTester.prepareDbUnit(getClass(), "update_link.xml"); | |||
File reportDir = temp.newFolder(); | |||
BatchReportWriter writer = new BatchReportWriter(reportDir); | |||
writer.writeMetadata(BatchReport.Metadata.newBuilder() | |||
.setRootComponentRef(1) | |||
.setProjectKey("PROJECT_KEY") | |||
.setAnalysisDate(150000000L) | |||
.build()); | |||
writer.writeComponent(BatchReport.Component.newBuilder() | |||
.setRef(1) | |||
.setType(Constants.ComponentType.PROJECT) | |||
.setUuid("ABCD") | |||
.addLinks(BatchReport.ComponentLink.newBuilder().setType(Constants.ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build()) | |||
.build()); | |||
step.execute(new ComputationContext(new BatchReportReader(reportDir), mock(ComponentDto.class))); | |||
dbTester.assertDbUnit(getClass(), "update_link-result.xml", "project_links"); | |||
} | |||
@Test | |||
public void delete_link() throws Exception { | |||
dbTester.prepareDbUnit(getClass(), "delete_link.xml"); | |||
File reportDir = temp.newFolder(); | |||
BatchReportWriter writer = new BatchReportWriter(reportDir); | |||
writer.writeMetadata(BatchReport.Metadata.newBuilder() | |||
.setRootComponentRef(1) | |||
.setProjectKey("PROJECT_KEY") | |||
.setAnalysisDate(150000000L) | |||
.build()); | |||
writer.writeComponent(BatchReport.Component.newBuilder() | |||
.setRef(1) | |||
.setType(Constants.ComponentType.PROJECT) | |||
.setUuid("ABCD") | |||
.build()); | |||
step.execute(new ComputationContext(new BatchReportReader(reportDir), mock(ComponentDto.class))); | |||
assertThat(dbTester.countRowsOfTable("project_links")).isEqualTo(0); | |||
} | |||
@Test | |||
public void fail_when_trying_to_add_same_link_type_multiple_times() throws Exception { | |||
dbTester.prepareDbUnit(getClass(), "empty.xml"); | |||
File reportDir = temp.newFolder(); | |||
BatchReportWriter writer = new BatchReportWriter(reportDir); | |||
writer.writeMetadata(BatchReport.Metadata.newBuilder() | |||
.setRootComponentRef(1) | |||
.setProjectKey("PROJECT_KEY") | |||
.setAnalysisDate(150000000L) | |||
.build()); | |||
writer.writeComponent(BatchReport.Component.newBuilder() | |||
.setRef(1) | |||
.setType(Constants.ComponentType.PROJECT) | |||
.setUuid("ABCD") | |||
.addLinks(BatchReport.ComponentLink.newBuilder().setType(Constants.ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build()) | |||
.addLinks(BatchReport.ComponentLink.newBuilder().setType(Constants.ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build()) | |||
.build()); | |||
try { | |||
step.execute(new ComputationContext(new BatchReportReader(reportDir), mock(ComponentDto.class))); | |||
failBecauseExceptionWasNotThrown(IllegalArgumentException.class); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Link of type 'homepage' has already been declared on component 'ABCD'"); | |||
} | |||
} | |||
} |
@@ -0,0 +1,5 @@ | |||
<dataset> | |||
<project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/> | |||
</dataset> |
@@ -0,0 +1,3 @@ | |||
<dataset> | |||
</dataset> |
@@ -0,0 +1,5 @@ | |||
<dataset> | |||
<project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/> | |||
</dataset> |
@@ -0,0 +1,7 @@ | |||
<dataset> | |||
<project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/> | |||
<project_links id="2" component_uuid="ABCD" link_type="scm" name="Sources" href="https://github.com/SonarSource/sonar"/> | |||
<project_links id="3" component_uuid="BCDE" link_type="homepage" name="Home" href="http://www.struts.org"/> | |||
</dataset> |
@@ -0,0 +1,5 @@ | |||
<dataset> | |||
<project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/> | |||
</dataset> |
@@ -0,0 +1,5 @@ | |||
<dataset> | |||
<project_links id="1" component_uuid="BCDE" link_type="ci" name="CI" href="github"/> | |||
</dataset> |
@@ -0,0 +1,11 @@ | |||
<dataset> | |||
<project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/> | |||
<project_links id="2" component_uuid="ABCD" link_type="scm" name="Sources" href="https://github.com/SonarSource/sonar"/> | |||
<project_links id="3" component_uuid="ABCD" link_type="scm_dev" name="Developer connection" href="scm:git:git@github.com:SonarSource/sonar.git/sonar"/> | |||
<project_links id="4" component_uuid="ABCD" link_type="issue" name="Issues" href="http://jira.sonarsource.com/"/> | |||
<project_links id="5" component_uuid="ABCD" link_type="ci" name="Continuous integration" href="http://bamboo.ci.codehaus.org/browse/SONAR"/> | |||
<project_links id="6" component_uuid="BCDE" link_type="scm" name="Sources" href="https://github.com/SonarSource/sonar/server"/> | |||
</dataset> |
@@ -0,0 +1,5 @@ | |||
<dataset> | |||
<project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/> | |||
</dataset> |
@@ -0,0 +1,3 @@ | |||
<dataset> | |||
</dataset> |
@@ -0,0 +1,5 @@ | |||
<dataset> | |||
<project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/> | |||
</dataset> |
@@ -0,0 +1,5 @@ | |||
<dataset> | |||
<project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/> | |||
</dataset> |
@@ -0,0 +1,5 @@ | |||
<dataset> | |||
<project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonar.org"/> | |||
</dataset> |
@@ -4,15 +4,18 @@ | |||
package org.sonar.batch.protocol; | |||
public final class Constants { | |||
private Constants() {} | |||
private Constants() { | |||
} | |||
public static void registerAllExtensions( | |||
com.google.protobuf.ExtensionRegistry registry) { | |||
com.google.protobuf.ExtensionRegistry registry) { | |||
} | |||
/** | |||
* Protobuf enum {@code Severity} | |||
*/ | |||
public enum Severity | |||
implements com.google.protobuf.ProtocolMessageEnum { | |||
implements com.google.protobuf.ProtocolMessageEnum { | |||
/** | |||
* <code>INFO = 0;</code> | |||
*/ | |||
@@ -32,8 +35,7 @@ public final class Constants { | |||
/** | |||
* <code>BLOCKER = 4;</code> | |||
*/ | |||
BLOCKER(4, 4), | |||
; | |||
BLOCKER(4, 4), ; | |||
/** | |||
* <code>INFO = 0;</code> | |||
@@ -56,49 +58,58 @@ public final class Constants { | |||
*/ | |||
public static final int BLOCKER_VALUE = 4; | |||
public final int getNumber() { return value; } | |||
public final int getNumber() { | |||
return value; | |||
} | |||
public static Severity valueOf(int value) { | |||
switch (value) { | |||
case 0: return INFO; | |||
case 1: return MINOR; | |||
case 2: return MAJOR; | |||
case 3: return CRITICAL; | |||
case 4: return BLOCKER; | |||
default: return null; | |||
case 0: | |||
return INFO; | |||
case 1: | |||
return MINOR; | |||
case 2: | |||
return MAJOR; | |||
case 3: | |||
return CRITICAL; | |||
case 4: | |||
return BLOCKER; | |||
default: | |||
return null; | |||
} | |||
} | |||
public static com.google.protobuf.Internal.EnumLiteMap<Severity> | |||
internalGetValueMap() { | |||
internalGetValueMap() { | |||
return internalValueMap; | |||
} | |||
private static com.google.protobuf.Internal.EnumLiteMap<Severity> | |||
internalValueMap = | |||
new com.google.protobuf.Internal.EnumLiteMap<Severity>() { | |||
public Severity findValueByNumber(int number) { | |||
return Severity.valueOf(number); | |||
} | |||
}; | |||
private static com.google.protobuf.Internal.EnumLiteMap<Severity> internalValueMap = | |||
new com.google.protobuf.Internal.EnumLiteMap<Severity>() { | |||
public Severity findValueByNumber(int number) { | |||
return Severity.valueOf(number); | |||
} | |||
}; | |||
public final com.google.protobuf.Descriptors.EnumValueDescriptor | |||
getValueDescriptor() { | |||
getValueDescriptor() { | |||
return getDescriptor().getValues().get(index); | |||
} | |||
public final com.google.protobuf.Descriptors.EnumDescriptor | |||
getDescriptorForType() { | |||
getDescriptorForType() { | |||
return getDescriptor(); | |||
} | |||
public static final com.google.protobuf.Descriptors.EnumDescriptor | |||
getDescriptor() { | |||
getDescriptor() { | |||
return org.sonar.batch.protocol.Constants.getDescriptor().getEnumTypes().get(0); | |||
} | |||
private static final Severity[] VALUES = values(); | |||
public static Severity valueOf( | |||
com.google.protobuf.Descriptors.EnumValueDescriptor desc) { | |||
com.google.protobuf.Descriptors.EnumValueDescriptor desc) { | |||
if (desc.getType() != getDescriptor()) { | |||
throw new java.lang.IllegalArgumentException( | |||
"EnumValueDescriptor is not for this type."); | |||
@@ -121,7 +132,7 @@ public final class Constants { | |||
* Protobuf enum {@code ComponentType} | |||
*/ | |||
public enum ComponentType | |||
implements com.google.protobuf.ProtocolMessageEnum { | |||
implements com.google.protobuf.ProtocolMessageEnum { | |||
/** | |||
* <code>PROJECT = 0;</code> | |||
*/ | |||
@@ -145,8 +156,7 @@ public final class Constants { | |||
/** | |||
* <code>SUBVIEW = 5;</code> | |||
*/ | |||
SUBVIEW(5, 5), | |||
; | |||
SUBVIEW(5, 5), ; | |||
/** | |||
* <code>PROJECT = 0;</code> | |||
@@ -173,50 +183,60 @@ public final class Constants { | |||
*/ | |||
public static final int SUBVIEW_VALUE = 5; | |||
public final int getNumber() { return value; } | |||
public final int getNumber() { | |||
return value; | |||
} | |||
public static ComponentType valueOf(int value) { | |||
switch (value) { | |||
case 0: return PROJECT; | |||
case 1: return MODULE; | |||
case 2: return DIRECTORY; | |||
case 3: return FILE; | |||
case 4: return VIEW; | |||
case 5: return SUBVIEW; | |||
default: return null; | |||
case 0: | |||
return PROJECT; | |||
case 1: | |||
return MODULE; | |||
case 2: | |||
return DIRECTORY; | |||
case 3: | |||
return FILE; | |||
case 4: | |||
return VIEW; | |||
case 5: | |||
return SUBVIEW; | |||
default: | |||
return null; | |||
} | |||
} | |||
public static com.google.protobuf.Internal.EnumLiteMap<ComponentType> | |||
internalGetValueMap() { | |||
internalGetValueMap() { | |||
return internalValueMap; | |||
} | |||
private static com.google.protobuf.Internal.EnumLiteMap<ComponentType> | |||
internalValueMap = | |||
new com.google.protobuf.Internal.EnumLiteMap<ComponentType>() { | |||
public ComponentType findValueByNumber(int number) { | |||
return ComponentType.valueOf(number); | |||
} | |||
}; | |||
private static com.google.protobuf.Internal.EnumLiteMap<ComponentType> internalValueMap = | |||
new com.google.protobuf.Internal.EnumLiteMap<ComponentType>() { | |||
public ComponentType findValueByNumber(int number) { | |||
return ComponentType.valueOf(number); | |||
} | |||
}; | |||
public final com.google.protobuf.Descriptors.EnumValueDescriptor | |||
getValueDescriptor() { | |||
getValueDescriptor() { | |||
return getDescriptor().getValues().get(index); | |||
} | |||
public final com.google.protobuf.Descriptors.EnumDescriptor | |||
getDescriptorForType() { | |||
getDescriptorForType() { | |||
return getDescriptor(); | |||
} | |||
public static final com.google.protobuf.Descriptors.EnumDescriptor | |||
getDescriptor() { | |||
getDescriptor() { | |||
return org.sonar.batch.protocol.Constants.getDescriptor().getEnumTypes().get(1); | |||
} | |||
private static final ComponentType[] VALUES = values(); | |||
public static ComponentType valueOf( | |||
com.google.protobuf.Descriptors.EnumValueDescriptor desc) { | |||
com.google.protobuf.Descriptors.EnumValueDescriptor desc) { | |||
if (desc.getType() != getDescriptor()) { | |||
throw new java.lang.IllegalArgumentException( | |||
"EnumValueDescriptor is not for this type."); | |||
@@ -235,26 +255,143 @@ public final class Constants { | |||
// @@protoc_insertion_point(enum_scope:ComponentType) | |||
} | |||
/** | |||
* Protobuf enum {@code ComponentLinkType} | |||
*/ | |||
public enum ComponentLinkType | |||
implements com.google.protobuf.ProtocolMessageEnum { | |||
/** | |||
* <code>HOME = 0;</code> | |||
*/ | |||
HOME(0, 0), | |||
/** | |||
* <code>SCM = 1;</code> | |||
*/ | |||
SCM(1, 1), | |||
/** | |||
* <code>SCM_DEV = 2;</code> | |||
*/ | |||
SCM_DEV(2, 2), | |||
/** | |||
* <code>ISSUE = 3;</code> | |||
*/ | |||
ISSUE(3, 3), | |||
/** | |||
* <code>CI = 4;</code> | |||
*/ | |||
CI(4, 4), ; | |||
public static com.google.protobuf.Descriptors.FileDescriptor | |||
/** | |||
* <code>HOME = 0;</code> | |||
*/ | |||
public static final int HOME_VALUE = 0; | |||
/** | |||
* <code>SCM = 1;</code> | |||
*/ | |||
public static final int SCM_VALUE = 1; | |||
/** | |||
* <code>SCM_DEV = 2;</code> | |||
*/ | |||
public static final int SCM_DEV_VALUE = 2; | |||
/** | |||
* <code>ISSUE = 3;</code> | |||
*/ | |||
public static final int ISSUE_VALUE = 3; | |||
/** | |||
* <code>CI = 4;</code> | |||
*/ | |||
public static final int CI_VALUE = 4; | |||
public final int getNumber() { | |||
return value; | |||
} | |||
public static ComponentLinkType valueOf(int value) { | |||
switch (value) { | |||
case 0: | |||
return HOME; | |||
case 1: | |||
return SCM; | |||
case 2: | |||
return SCM_DEV; | |||
case 3: | |||
return ISSUE; | |||
case 4: | |||
return CI; | |||
default: | |||
return null; | |||
} | |||
} | |||
public static com.google.protobuf.Internal.EnumLiteMap<ComponentLinkType> | |||
internalGetValueMap() { | |||
return internalValueMap; | |||
} | |||
private static com.google.protobuf.Internal.EnumLiteMap<ComponentLinkType> internalValueMap = | |||
new com.google.protobuf.Internal.EnumLiteMap<ComponentLinkType>() { | |||
public ComponentLinkType findValueByNumber(int number) { | |||
return ComponentLinkType.valueOf(number); | |||
} | |||
}; | |||
public final com.google.protobuf.Descriptors.EnumValueDescriptor | |||
getValueDescriptor() { | |||
return getDescriptor().getValues().get(index); | |||
} | |||
public final com.google.protobuf.Descriptors.EnumDescriptor | |||
getDescriptorForType() { | |||
return getDescriptor(); | |||
} | |||
public static final com.google.protobuf.Descriptors.EnumDescriptor | |||
getDescriptor() { | |||
return org.sonar.batch.protocol.Constants.getDescriptor().getEnumTypes().get(2); | |||
} | |||
private static final ComponentLinkType[] VALUES = values(); | |||
public static ComponentLinkType valueOf( | |||
com.google.protobuf.Descriptors.EnumValueDescriptor desc) { | |||
if (desc.getType() != getDescriptor()) { | |||
throw new java.lang.IllegalArgumentException( | |||
"EnumValueDescriptor is not for this type."); | |||
} | |||
return VALUES[desc.getIndex()]; | |||
} | |||
private final int index; | |||
private final int value; | |||
private ComponentLinkType(int index, int value) { | |||
this.index = index; | |||
this.value = value; | |||
} | |||
// @@protoc_insertion_point(enum_scope:ComponentLinkType) | |||
} | |||
public static com.google.protobuf.Descriptors.FileDescriptor | |||
getDescriptor() { | |||
return descriptor; | |||
} | |||
private static com.google.protobuf.Descriptors.FileDescriptor | |||
descriptor; | |||
private static com.google.protobuf.Descriptors.FileDescriptor descriptor; | |||
static { | |||
java.lang.String[] descriptorData = { | |||
"\n\017constants.proto*E\n\010Severity\022\010\n\004INFO\020\000\022" + | |||
"\t\n\005MINOR\020\001\022\t\n\005MAJOR\020\002\022\014\n\010CRITICAL\020\003\022\013\n\007B" + | |||
"LOCKER\020\004*X\n\rComponentType\022\013\n\007PROJECT\020\000\022\n" + | |||
"\n\006MODULE\020\001\022\r\n\tDIRECTORY\020\002\022\010\n\004FILE\020\003\022\010\n\004V" + | |||
"IEW\020\004\022\013\n\007SUBVIEW\020\005B\034\n\030org.sonar.batch.pr" + | |||
"otocolH\001" | |||
"\t\n\005MINOR\020\001\022\t\n\005MAJOR\020\002\022\014\n\010CRITICAL\020\003\022\013\n\007B" + | |||
"LOCKER\020\004*X\n\rComponentType\022\013\n\007PROJECT\020\000\022\n" + | |||
"\n\006MODULE\020\001\022\r\n\tDIRECTORY\020\002\022\010\n\004FILE\020\003\022\010\n\004V" + | |||
"IEW\020\004\022\013\n\007SUBVIEW\020\005*F\n\021ComponentLinkType\022" + | |||
"\010\n\004HOME\020\000\022\007\n\003SCM\020\001\022\013\n\007SCM_DEV\020\002\022\t\n\005ISSUE" + | |||
"\020\003\022\006\n\002CI\020\004B\034\n\030org.sonar.batch.protocolH\001" | |||
}; | |||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = | |||
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { | |||
public com.google.protobuf.ExtensionRegistry assignDescriptors( | |||
com.google.protobuf.Descriptors.FileDescriptor root) { | |||
com.google.protobuf.Descriptors.FileDescriptor root) { | |||
descriptor = root; | |||
return null; | |||
} |
@@ -48,6 +48,11 @@ message Metadata { | |||
optional int32 deleted_components_count = 5; | |||
} | |||
message ComponentLink { | |||
optional ComponentLinkType type = 1; | |||
optional string href = 2; | |||
} | |||
message Component { | |||
optional int32 ref = 1; | |||
optional string path = 2; | |||
@@ -56,6 +61,7 @@ message Component { | |||
optional bool is_test = 5; | |||
optional string language = 6; | |||
repeated int32 child_refs = 7 [packed=true]; | |||
repeated ComponentLink links = 10; | |||
// temporary fields during development of computation stack | |||
optional int32 snapshot_id = 8; |
@@ -37,3 +37,11 @@ enum ComponentType { | |||
VIEW = 4; | |||
SUBVIEW = 5; | |||
} | |||
enum ComponentLinkType { | |||
HOME = 0; | |||
SCM = 1; | |||
SCM_DEV = 2; | |||
ISSUE = 3; | |||
CI = 4; | |||
} |
@@ -0,0 +1,79 @@ | |||
/* | |||
* 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.core.component; | |||
/** | |||
* Component links should be merge in a 'links' column (using protobuf for instance) of the projects table. | |||
* But to do this we'll have to wait for the measure filters page (where links are displayed) to be rewritten in JS/WS (because it's in Rails for the moment). | |||
*/ | |||
public class ComponentLinkDto { | |||
private Long id; | |||
private String componentUuid; | |||
private String type; | |||
private String name; | |||
private String href; | |||
public String getName() { | |||
return name; | |||
} | |||
public ComponentLinkDto setName(String name) { | |||
this.name = name; | |||
return this; | |||
} | |||
public String getComponentUuid() { | |||
return componentUuid; | |||
} | |||
public ComponentLinkDto setComponentUuid(String componentUuid) { | |||
this.componentUuid = componentUuid; | |||
return this; | |||
} | |||
public String getHref() { | |||
return href; | |||
} | |||
public ComponentLinkDto setHref(String href) { | |||
this.href = href; | |||
return this; | |||
} | |||
public Long getId() { | |||
return id; | |||
} | |||
public ComponentLinkDto setId(Long id) { | |||
this.id = id; | |||
return this; | |||
} | |||
public String getType() { | |||
return type; | |||
} | |||
public ComponentLinkDto setType(String type) { | |||
this.type = type; | |||
return this; | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
/* | |||
* 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.core.component.db; | |||
import org.sonar.core.component.ComponentLinkDto; | |||
import java.util.List; | |||
public interface ComponentLinkMapper { | |||
List<ComponentLinkDto> selectByComponentUuid(String componentUuid); | |||
void insert(ComponentLinkDto dto); | |||
void update(ComponentLinkDto dto); | |||
void delete(long id); | |||
} |
@@ -35,11 +35,9 @@ import org.sonar.api.database.model.MeasureModel; | |||
import org.sonar.core.activity.db.ActivityDto; | |||
import org.sonar.core.activity.db.ActivityMapper; | |||
import org.sonar.core.cluster.WorkQueue; | |||
import org.sonar.core.component.ComponentDto; | |||
import org.sonar.core.component.FilePathWithHashDto; | |||
import org.sonar.core.component.SnapshotDto; | |||
import org.sonar.core.component.UuidWithProjectUuidDto; | |||
import org.sonar.core.component.*; | |||
import org.sonar.core.component.db.ComponentIndexMapper; | |||
import org.sonar.core.component.db.ComponentLinkMapper; | |||
import org.sonar.core.component.db.ComponentMapper; | |||
import org.sonar.core.component.db.SnapshotMapper; | |||
import org.sonar.core.computation.db.AnalysisReportDto; | |||
@@ -133,6 +131,7 @@ public class MyBatis implements BatchComponent, ServerComponent { | |||
loadAlias(conf, "ActiveDashboard", ActiveDashboardDto.class); | |||
loadAlias(conf, "Author", AuthorDto.class); | |||
loadAlias(conf, "Component", ComponentDto.class); | |||
loadAlias(conf, "ComponentLink", ComponentLinkDto.class); | |||
loadAlias(conf, "Dashboard", DashboardDto.class); | |||
loadAlias(conf, "Dependency", DependencyDto.class); | |||
loadAlias(conf, "DuplicationUnit", DuplicationUnitDto.class); | |||
@@ -204,7 +203,7 @@ public class MyBatis implements BatchComponent, ServerComponent { | |||
GroupMembershipMapper.class, QualityProfileMapper.class, ActiveRuleMapper.class, | |||
MeasureMapper.class, MetricMapper.class, QualityGateMapper.class, QualityGateConditionMapper.class, ComponentMapper.class, SnapshotMapper.class, | |||
ProjectQgateAssociationMapper.class, | |||
AnalysisReportMapper.class, ComponentIndexMapper.class, | |||
AnalysisReportMapper.class, ComponentIndexMapper.class, ComponentLinkMapper.class, | |||
Migration45Mapper.class, Migration50Mapper.class | |||
}; | |||
loadMappers(conf, mappers); |
@@ -0,0 +1,37 @@ | |||
<?xml version="1.0" encoding="UTF-8" ?> | |||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |||
<mapper namespace="org.sonar.core.component.db.ComponentLinkMapper"> | |||
<sql id="componentLinkColumns"> | |||
p.id, | |||
p.component_uuid as "componentUuid", | |||
p.link_type as "type", | |||
p.name as name, | |||
p.href as href | |||
</sql> | |||
<select id="selectByComponentUuid" parameterType="String" resultType="ComponentLink"> | |||
SELECT | |||
<include refid="componentLinkColumns"/> | |||
FROM project_links p | |||
<where> | |||
AND p.component_uuid=#{uuid} | |||
</where> | |||
</select> | |||
<insert id="insert" parameterType="ComponentLink" keyColumn="id" useGeneratedKeys="true" keyProperty="id"> | |||
INSERT INTO project_links (component_uuid, link_type, name, href) | |||
VALUES (#{componentUuid,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{href,jdbcType=VARCHAR}) | |||
</insert> | |||
<insert id="update" parameterType="ComponentLink" keyColumn="id" useGeneratedKeys="true" keyProperty="id"> | |||
UPDATE project_links SET component_uuid=#{componentUuid,jdbcType=VARCHAR}, link_type=#{type,jdbcType=VARCHAR}, name=#{name,jdbcType=VARCHAR}, href=#{href,jdbcType=VARCHAR} | |||
WHERE id=#{id} | |||
</insert> | |||
<delete id="delete"> | |||
DELETE FROM project_links WHERE id=#{id} | |||
</delete> | |||
</mapper> | |||