From 46477b4afa2515d8c6e1bddda3191e367d67b52e Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Fri, 6 Feb 2015 17:41:40 +0100 Subject: SONAR-3566 Make it clear that issue-related information displayed in widgets is desynchronized --- sonar-core/src/main/resources/org/sonar/l10n/core.properties | 1 + 1 file changed, 1 insertion(+) (limited to 'sonar-core') diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 3698bebfe19..cd2218dda4a 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -1088,6 +1088,7 @@ select2.tooShort=Please enter at least {0} characters widgets.more=More widget.error_occurred_please_read_logs=An error occurred while trying to display the widget "{0}". Please contact the administrator. widget.unsupported_browser_warning=Your browser is out of date and does not support this widget. +widget.as_calculated_on_x=As calculated on {0} widget.alerts.name=Quality Gate widget.alerts.description=Displays the project's quality gate status. -- cgit v1.2.3 From 0ea7c0cedcadefe53e5ea7aab1e66592c0cb9279 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 5 Feb 2015 13:37:25 +0100 Subject: Refactor issue search stack in order to remove dependency with SearchRequestHandler --- .../server/benchmark/IssueIndexBenchmarkTest.java | 6 +- .../core/computation/dbcleaner/ProjectCleaner.java | 15 +- .../main/java/org/sonar/server/db/DbClient.java | 10 +- .../main/java/org/sonar/server/es/BaseIndex.java | 35 ++ .../src/main/java/org/sonar/server/es/EsUtils.java | 65 ++ .../src/main/java/org/sonar/server/es/Facets.java | 153 +++++ .../java/org/sonar/server/es/SearchOptions.java | 179 ++++++ .../java/org/sonar/server/es/SearchResult.java | 58 ++ .../server/issue/AbstractChangeTagsAction.java | 6 +- .../main/java/org/sonar/server/issue/Action.java | 3 +- .../java/org/sonar/server/issue/AssignAction.java | 4 +- .../java/org/sonar/server/issue/CommentAction.java | 7 +- .../server/issue/InternalRubyIssueService.java | 13 +- .../sonar/server/issue/IssueBulkChangeService.java | 55 +- .../java/org/sonar/server/issue/IssueQuery.java | 42 +- .../java/org/sonar/server/issue/IssueService.java | 65 +- .../java/org/sonar/server/issue/PlanAction.java | 9 +- .../org/sonar/server/issue/SetSeverityAction.java | 6 +- .../org/sonar/server/issue/TransitionAction.java | 4 +- .../java/org/sonar/server/issue/db/IssueDao.java | 4 +- .../server/issue/filter/IssueFilterService.java | 30 +- .../org/sonar/server/issue/index/FakeIssueDto.java | 29 - .../org/sonar/server/issue/index/IssueIndex.java | 612 +++++++++--------- .../org/sonar/server/issue/ws/AuthorsAction.java | 19 +- .../sonar/server/issue/ws/BaseIssuesWsAction.java | 30 + .../sonar/server/issue/ws/ComponentTagsAction.java | 6 +- .../org/sonar/server/issue/ws/IssueShowAction.java | 7 +- .../java/org/sonar/server/issue/ws/IssuesWs.java | 36 +- .../org/sonar/server/issue/ws/SearchAction.java | 248 ++++---- .../org/sonar/server/issue/ws/SetTagsAction.java | 21 +- .../java/org/sonar/server/issue/ws/TagsAction.java | 9 +- .../sonar/server/search/StickyFacetBuilder.java | 6 +- .../sonar/server/source/index/SourceLineIndex.java | 12 +- .../org/sonar/server/issue/ws/example-search.json | 11 +- .../computation/dbcleaner/ProjectCleanerTest.java | 41 +- .../test/java/org/sonar/server/es/EsUtilsTest.java | 69 +++ .../org/sonar/server/es/SearchOptionsTest.java | 156 +++++ .../java/org/sonar/server/issue/ActionTest.java | 9 +- .../server/issue/InternalRubyIssueServiceTest.java | 40 +- .../server/issue/IssueBulkChangeServiceTest.java | 682 ++++++++++----------- .../sonar/server/issue/IssueServiceMediumTest.java | 81 +-- .../issue/filter/IssueFilterServiceTest.java | 17 +- .../server/issue/index/IssueIndexMediumTest.java | 608 +++++++++--------- .../sonar/server/issue/ws/AuthorsActionTest.java | 5 +- .../server/issue/ws/ComponentTagsActionTest.java | 5 +- .../sonar/server/issue/ws/IssueShowActionTest.java | 14 +- .../sonar/server/issue/ws/IssueTagsActionTest.java | 5 +- .../org/sonar/server/issue/ws/IssuesWsTest.java | 236 +------ .../issue/ws/SearchActionComponentsMediumTest.java | 13 +- .../server/issue/ws/SearchActionMediumTest.java | 14 +- .../sonar/server/issue/ws/SetTagsActionTest.java | 22 +- .../server/view/index/ViewIndexerMediumTest.java | 10 +- .../default_page_size_is_100.json | 1 - .../SearchActionMediumTest/deprecated_paging.json | 1 - .../ws/SearchActionMediumTest/empty_result.json | 1 - .../issue/ws/SearchActionMediumTest/issue.json | 1 - .../org/sonar/core/issue/db/IssueChangeDao.java | 3 +- .../java/org/sonar/core/issue/db/IssueMapper.java | 3 +- .../java/org/sonar/api/server/ws/WebService.java | 72 ++- .../org/sonar/api/server/ws/WebServiceTest.java | 22 +- 60 files changed, 2269 insertions(+), 1677 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/es/BaseIndex.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/es/Facets.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/es/SearchOptions.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/es/SearchResult.java delete mode 100644 server/sonar-server/src/main/java/org/sonar/server/issue/index/FakeIssueDto.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/issue/ws/BaseIssuesWsAction.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/es/SearchOptionsTest.java (limited to 'sonar-core') diff --git a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/IssueIndexBenchmarkTest.java b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/IssueIndexBenchmarkTest.java index 64950c865d7..4d2a9de9f5b 100644 --- a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/IssueIndexBenchmarkTest.java +++ b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/IssueIndexBenchmarkTest.java @@ -34,14 +34,14 @@ import org.sonar.api.issue.Issue; import org.sonar.api.rule.Severity; import org.sonar.api.utils.internal.Uuids; import org.sonar.server.es.EsClient; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; import org.sonar.server.issue.IssueQuery; import org.sonar.server.issue.index.IssueAuthorizationDao; import org.sonar.server.issue.index.IssueAuthorizationIndexer; import org.sonar.server.issue.index.IssueDoc; import org.sonar.server.issue.index.IssueIndex; import org.sonar.server.issue.index.IssueIndexer; -import org.sonar.server.search.QueryContext; -import org.sonar.server.search.Result; import org.sonar.server.tester.ServerTester; import org.sonar.server.user.MockUserSession; @@ -138,7 +138,7 @@ public class IssueIndexBenchmarkTest { IssueIndex index = tester.get(IssueIndex.class); for (int i = 0; i < 10; i++) { long start = System.currentTimeMillis(); - Result result = index.search(query, new QueryContext()); + SearchResult result = index.search(query, new SearchOptions()); long end = System.currentTimeMillis(); LOGGER.info("Request (" + label + "): {} docs in {} ms", result.getTotal(), end - start); } diff --git a/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/ProjectCleaner.java b/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/ProjectCleaner.java index 021f88ebdaf..dd9a2d955ec 100644 --- a/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/ProjectCleaner.java +++ b/server/sonar-server/src/main/java/org/sonar/core/computation/dbcleaner/ProjectCleaner.java @@ -28,10 +28,13 @@ import org.sonar.api.config.Settings; import org.sonar.api.utils.TimeUtils; import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner; import org.sonar.core.persistence.DbSession; -import org.sonar.core.purge.*; +import org.sonar.core.purge.IdUuidPair; +import org.sonar.core.purge.PurgeConfiguration; +import org.sonar.core.purge.PurgeDao; +import org.sonar.core.purge.PurgeListener; +import org.sonar.core.purge.PurgeProfiler; import org.sonar.server.issue.index.IssueIndex; import org.sonar.server.properties.ProjectSettingsFactory; -import org.sonar.server.search.IndexClient; import javax.annotation.Nullable; import java.util.Date; @@ -46,16 +49,16 @@ public class ProjectCleaner implements ServerComponent { private final PurgeDao purgeDao; private final DefaultPeriodCleaner periodCleaner; private final ProjectSettingsFactory projectSettingsFactory; - private final IndexClient indexClient; + private final IssueIndex issueIndex; public ProjectCleaner(PurgeDao purgeDao, DefaultPeriodCleaner periodCleaner, PurgeProfiler profiler, PurgeListener purgeListener, - ProjectSettingsFactory projectSettingsFactory, IndexClient indexClient) { + ProjectSettingsFactory projectSettingsFactory, IssueIndex issueIndex) { this.purgeDao = purgeDao; this.periodCleaner = periodCleaner; this.profiler = profiler; this.purgeListener = purgeListener; this.projectSettingsFactory = projectSettingsFactory; - this.indexClient = indexClient; + this.issueIndex = issueIndex; } public ProjectCleaner purge(DbSession session, IdUuidPair idUuidPair) { @@ -77,7 +80,7 @@ public class ProjectCleaner implements ServerComponent { private void deleteIndexedIssuesBefore(String uuid, @Nullable Date lastDateWithClosedIssues) { if (lastDateWithClosedIssues != null) { - indexClient.get(IssueIndex.class).deleteClosedIssuesOfProjectBefore(uuid, lastDateWithClosedIssues); + issueIndex.deleteClosedIssuesOfProjectBefore(uuid, lastDateWithClosedIssues); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java index 22cbf6fe36e..ea6ac8f2980 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java @@ -19,10 +19,9 @@ */ package org.sonar.server.db; -import org.sonar.core.user.AuthorDao; - import org.sonar.api.ServerComponent; import org.sonar.core.issue.db.ActionPlanDao; +import org.sonar.core.issue.db.IssueChangeDao; import org.sonar.core.persistence.DaoComponent; import org.sonar.core.persistence.Database; import org.sonar.core.persistence.DbSession; @@ -33,6 +32,7 @@ import org.sonar.core.resource.ResourceDao; import org.sonar.core.source.db.FileSourceDao; import org.sonar.core.technicaldebt.db.CharacteristicDao; import org.sonar.core.template.LoadedTemplateDao; +import org.sonar.core.user.AuthorDao; import org.sonar.core.user.AuthorizationDao; import org.sonar.server.activity.db.ActivityDao; import org.sonar.server.component.db.ComponentDao; @@ -79,6 +79,7 @@ public class DbClient implements ServerComponent { private final UserDao userDao; private final GroupDao groupDao; private final IssueDao issueDao; + private final IssueChangeDao issueChangeDao; private final ActionPlanDao actionPlanDao; private final AnalysisReportDao analysisReportDao; private final DashboardDao dashboardDao; @@ -111,6 +112,7 @@ public class DbClient implements ServerComponent { userDao = getDao(map, UserDao.class); groupDao = getDao(map, GroupDao.class); issueDao = getDao(map, IssueDao.class); + issueChangeDao = getDao(map, IssueChangeDao.class); actionPlanDao = getDao(map, ActionPlanDao.class); analysisReportDao = getDao(map, AnalysisReportDao.class); dashboardDao = getDao(map, DashboardDao.class); @@ -140,6 +142,10 @@ public class DbClient implements ServerComponent { return issueDao; } + public IssueChangeDao issueChangeDao() { + return issueChangeDao; + } + public QualityProfileDao qualityProfileDao() { return qualityProfileDao; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/BaseIndex.java b/server/sonar-server/src/main/java/org/sonar/server/es/BaseIndex.java new file mode 100644 index 00000000000..45d253555b3 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/es/BaseIndex.java @@ -0,0 +1,35 @@ +/* + * 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.es; + +import org.sonar.api.ServerComponent; + +public abstract class BaseIndex implements ServerComponent { + private final EsClient client; + + public BaseIndex(EsClient client) { + this.client = client; + } + + public EsClient getClient() { + return client; + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java b/server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java new file mode 100644 index 00000000000..837754cf20a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/es/EsUtils.java @@ -0,0 +1,65 @@ +/* + * 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.es; + +import com.google.common.base.Function; +import com.google.common.collect.Lists; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.aggregations.bucket.terms.Terms; +import org.sonar.server.search.BaseDoc; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class EsUtils { + + private EsUtils() { + // only static methods + } + + public static List convertToDocs(SearchHits hits, Function, D> converter) { + List docs = new ArrayList<>(); + for (SearchHit hit : hits.getHits()) { + docs.add(converter.apply(hit.getSource())); + } + return docs; + } + + public static LinkedHashMap termsToMap(Terms terms) { + LinkedHashMap map = new LinkedHashMap<>(); + List buckets = terms.getBuckets(); + for (Terms.Bucket bucket : buckets) { + map.put(bucket.getKey(), bucket.getDocCount()); + } + return map; + } + + public static List termsKeys(Terms terms) { + return Lists.transform(terms.getBuckets(), new Function() { + @Override + public String apply(Terms.Bucket bucket) { + return bucket.getKey(); + } + }); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/Facets.java b/server/sonar-server/src/main/java/org/sonar/server/es/Facets.java new file mode 100644 index 00000000000..a22131e18f3 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/es/Facets.java @@ -0,0 +1,153 @@ +/* + * 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.es; + +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.search.aggregations.Aggregation; +import org.elasticsearch.search.aggregations.HasAggregations; +import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogram; +import org.elasticsearch.search.aggregations.bucket.missing.Missing; +import org.elasticsearch.search.aggregations.bucket.terms.Terms; + +import javax.annotation.CheckForNull; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +public class Facets { + + private final Map> facetsByName = new LinkedHashMap<>(); + + public Facets(SearchResponse response) { + if (response.getAggregations() != null) { + for (Aggregation facet : response.getAggregations()) { + processAggregation(facet); + } + } + } + + public Facets(Terms terms) { + processTermsAggregation(terms); + } + + private void processAggregation(Aggregation aggregation) { + if (Missing.class.isAssignableFrom(aggregation.getClass())) { + processMissingAggregation((Missing) aggregation); + } else if (Terms.class.isAssignableFrom(aggregation.getClass())) { + processTermsAggregation((Terms) aggregation); + } else if (HasAggregations.class.isAssignableFrom(aggregation.getClass())) { + processSubAggregations((HasAggregations) aggregation); + } else if (DateHistogram.class.isAssignableFrom(aggregation.getClass())) { + processDateHistogram((DateHistogram) aggregation); + } else { + throw new IllegalArgumentException("Aggregation type not supported yet: " + aggregation.getClass()); + } + } + + private void processMissingAggregation(Missing aggregation) { + long docCount = aggregation.getDocCount(); + if (docCount > 0L) { + getOrCreateFacet(aggregation.getName().replace("_missing", "")).put("", docCount); + } + } + + private void processTermsAggregation(Terms aggregation) { + String facetName = aggregation.getName(); + // TODO document this naming convention + if (facetName.contains("__") && !facetName.startsWith("__")) { + facetName = facetName.substring(0, facetName.indexOf("__")); + } + facetName = facetName.replace("_selected", ""); + LinkedHashMap facet = getOrCreateFacet(facetName); + for (Terms.Bucket value : aggregation.getBuckets()) { + facet.put(value.getKey(), value.getDocCount()); + } + } + + private void processSubAggregations(HasAggregations aggregation) { + for (Aggregation sub : aggregation.getAggregations()) { + processAggregation(sub); + } + } + + private void processDateHistogram(DateHistogram aggregation) { + LinkedHashMap facet = getOrCreateFacet(aggregation.getName()); + for (DateHistogram.Bucket value : aggregation.getBuckets()) { + facet.put(value.getKeyAsText().toString(), value.getDocCount()); + } + } + + public boolean contains(String facetName) { + return facetsByName.containsKey(facetName); + } + + /** + * The buckets of the given facet. Null if the facet does not exist + */ + @CheckForNull + public LinkedHashMap get(String facetName) { + return facetsByName.get(facetName); + } + + public Map> getAll() { + return facetsByName; + } + + /** + * Value of the facet bucket. Null if the facet or the bucket do not exist. + */ + @CheckForNull + public Long getBucketValue(String facetName, String bucketKey) { + LinkedHashMap facet = facetsByName.get(facetName); + if (facet != null) { + return facet.get(bucketKey); + } + return null; + } + + public Set getBucketKeys(String facetName) { + LinkedHashMap facet = facetsByName.get(facetName); + if (facet != null) { + return facet.keySet(); + } + return Collections.emptySet(); + } + + public Set getNames() { + return facetsByName.keySet(); + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this, ToStringStyle.SIMPLE_STYLE); + } + + private LinkedHashMap getOrCreateFacet(String facetName) { + LinkedHashMap facet = facetsByName.get(facetName); + if (facet == null) { + facet = new LinkedHashMap<>(); + facetsByName.put(facetName, facet); + } + return facet; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/SearchOptions.java b/server/sonar-server/src/main/java/org/sonar/server/es/SearchOptions.java new file mode 100644 index 00000000000..57cf4904cef --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/es/SearchOptions.java @@ -0,0 +1,179 @@ +/* + * 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.es; + +import com.google.common.base.Preconditions; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.text.JsonWriter; + +import javax.annotation.Nullable; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Various Elasticsearch request options: paging, fields and facets + */ +public class SearchOptions { + + public static final int DEFAULT_OFFSET = 0; + public static final int DEFAULT_LIMIT = 10; + public static final int MAX_LIMIT = 500; + + private int offset = DEFAULT_OFFSET; + private int limit = DEFAULT_LIMIT; + private final Set facets = new LinkedHashSet<>(); + private final Set fieldsToReturn = new HashSet<>(); + + /** + * Offset of the first result to return. Defaults to {@link #DEFAULT_OFFSET} + */ + public int getOffset() { + return offset; + } + + /** + * Sets the offset of the first result to return (zero-based). + */ + public SearchOptions setOffset(int offset) { + Preconditions.checkArgument(offset >= 0, "Offset must be positive"); + this.offset = offset; + return this; + } + + /** + * Set offset and limit according to page approach. If pageSize is negative, then + * {@link #MAX_LIMIT} is used. + */ + public SearchOptions setPage(int page, int pageSize) { + Preconditions.checkArgument(page >= 1, "Page must be greater or equal to 1 (got " + page + ")"); + setLimit(pageSize); + setOffset((page * this.limit) - this.limit); + return this; + } + + public int getPage() { + return limit > 0 ? (int) Math.ceil((double) (offset + 1) / (double) limit) : 0; + } + + /** + * Limit on the number of results to return. Defaults to {@link #DEFAULT_LIMIT}. + */ + public int getLimit() { + return limit; + } + + /** + * Sets the limit on the number of results to return. + */ + public SearchOptions setLimit(int limit) { + if (limit < 0) { + this.limit = MAX_LIMIT; + } else { + this.limit = Math.min(limit, MAX_LIMIT); + } + return this; + } + + /** + * WARNING - dangerous + */ + @Deprecated + public SearchOptions disableLimit() { + this.limit = 999999; + return this; + } + + /** + * Lists selected facets. + */ + public Collection getFacets() { + return facets; + } + + /** + * Selects facets to return for the domain. + */ + public SearchOptions addFacets(@Nullable Collection f) { + if (f != null) { + this.facets.addAll(f); + } + return this; + } + + public SearchOptions addFacets(String... array) { + Collections.addAll(facets, array); + return this; + } + + public Set getFields() { + return fieldsToReturn; + } + + public boolean hasField(String key) { + return fieldsToReturn.isEmpty() || fieldsToReturn.contains(key); + } + + public SearchOptions addFields(@Nullable Collection c) { + if (c != null) { + for (String s : c) { + if (StringUtils.isNotBlank(s)) { + fieldsToReturn.add(s); + } + } + } + return this; + } + + public SearchOptions addFields(String... array) { + return addFields(Arrays.asList(array)); + } + + public SearchOptions writeJson(JsonWriter json, long totalHits) { + json.prop("total", totalHits); + json.prop(WebService.Param.PAGE, getPage()); + json.prop(WebService.Param.PAGE_SIZE, getLimit()); + return this; + } + + @Deprecated + public SearchOptions writeDeprecatedJson(JsonWriter json, long totalHits) { + int pages = 0; + if (limit > 0) { + pages = (int) (totalHits / limit); + if (totalHits % limit > 0) { + pages++; + } + } + json.name("paging").beginObject() + .prop("pageIndex", getPage()) + .prop("pageSize", getLimit()) + .prop("total", totalHits) + .prop("fTotal", String.valueOf(totalHits)) + .prop("pages", pages) + .endObject(); + return this; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/SearchResult.java b/server/sonar-server/src/main/java/org/sonar/server/es/SearchResult.java new file mode 100644 index 00000000000..731f48730cc --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/es/SearchResult.java @@ -0,0 +1,58 @@ +/* + * 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.es; + +import com.google.common.base.Function; +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.elasticsearch.action.search.SearchResponse; +import org.sonar.server.search.BaseDoc; + +import java.util.List; +import java.util.Map; + +public class SearchResult { + + private final List docs; + private final Facets facets; + private final long total; + + public SearchResult(SearchResponse response, Function, DOC> converter) { + this.facets = new Facets(response); + this.total = response.getHits().totalHits(); + this.docs = EsUtils.convertToDocs(response.getHits(), converter); + } + + public List getDocs() { + return docs; + } + + public long getTotal() { + return total; + } + + public Facets getFacets() { + return this.facets; + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java index d5a0c629341..7aa68b28e11 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java @@ -32,11 +32,9 @@ import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.Set; - public abstract class AbstractChangeTagsAction extends Action implements ServerComponent { private static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); @@ -50,7 +48,7 @@ public abstract class AbstractChangeTagsAction extends Action implements ServerC } @Override - public boolean verify(Map properties, List issues, UserSession userSession){ + public boolean verify(Map properties, Collection issues, UserSession userSession) { parseTags(properties); return true; } @@ -67,7 +65,7 @@ public abstract class AbstractChangeTagsAction extends Action implements ServerC Set result = Sets.newHashSet(); String tagsString = (String) properties.get("tags"); if (!Strings.isNullOrEmpty(tagsString)) { - for(String tag: TAGS_SPLITTER.split(tagsString)) { + for (String tag : TAGS_SPLITTER.split(tagsString)) { RuleTagFormat.validate(tag); result.add(tag); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/Action.java b/server/sonar-server/src/main/java/org/sonar/server/issue/Action.java index e7224a79129..715e725df6e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/Action.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/Action.java @@ -29,6 +29,7 @@ import org.sonar.api.issue.condition.Condition; import org.sonar.api.issue.internal.IssueChangeContext; import org.sonar.server.user.UserSession; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -70,7 +71,7 @@ public abstract class Action implements ServerComponent { return true; } - abstract boolean verify(Map properties, List issues, UserSession userSession); + abstract boolean verify(Map properties, Collection issues, UserSession userSession); abstract boolean execute(Map properties, Context context); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java index d6ead7dd524..8beab29d430 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java @@ -30,7 +30,7 @@ import org.sonar.api.user.UserFinder; import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; -import java.util.List; +import java.util.Collection; import java.util.Map; @@ -50,7 +50,7 @@ public class AssignAction extends Action implements ServerComponent { } @Override - public boolean verify(Map properties, List issues, UserSession userSession){ + public boolean verify(Map properties, Collection issues, UserSession userSession){ String assignee = assigneeValue(properties); if(!Strings.isNullOrEmpty(assignee)) { User user = selectUser(assignee); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java index e50eba29d08..d8700f55600 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java @@ -27,10 +27,9 @@ import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; -import java.util.List; +import java.util.Collection; import java.util.Map; - public class CommentAction extends Action implements ServerComponent { public static final String KEY = "comment"; @@ -44,7 +43,7 @@ public class CommentAction extends Action implements ServerComponent { } @Override - public boolean verify(Map properties, List issues, UserSession userSession) { + public boolean verify(Map properties, Collection issues, UserSession userSession) { comment(properties); return true; } @@ -58,7 +57,7 @@ public class CommentAction extends Action implements ServerComponent { private String comment(Map properties) { String param = (String) properties.get(COMMENT_PROPERTY); if (Strings.isNullOrEmpty(param)) { - throw new IllegalArgumentException("Missing parameter : '"+ COMMENT_PROPERTY +"'"); + throw new IllegalArgumentException("Missing parameter : '" + COMMENT_PROPERTY + "'"); } return param; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java index e60b31e6323..d20fc825c44 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java @@ -43,6 +43,7 @@ import org.sonar.core.issue.workflow.Transition; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; import org.sonar.core.resource.ResourceQuery; +import org.sonar.server.es.SearchOptions; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.issue.filter.IssueFilterParameters; @@ -477,7 +478,7 @@ public class InternalRubyIssueService implements ServerComponent { * Execute issue filter from parameters */ public IssueFilterService.IssueFilterResult execute(Map props) { - return issueFilterService.execute(issueQueryService.createFromMap(props), toContext(props)); + return issueFilterService.execute(issueQueryService.createFromMap(props), toSearchOptions(props)); } /** @@ -636,16 +637,16 @@ public class InternalRubyIssueService implements ServerComponent { } @VisibleForTesting - static QueryContext toContext(Map props) { - QueryContext context = new QueryContext(); + static SearchOptions toSearchOptions(Map props) { + SearchOptions options = new SearchOptions(); Integer pageIndex = RubyUtils.toInteger(props.get(IssueFilterParameters.PAGE_INDEX)); Integer pageSize = RubyUtils.toInteger(props.get(IssueFilterParameters.PAGE_SIZE)); if (pageSize != null && pageSize < 0) { - context.setMaxLimit(); + options.setLimit(SearchOptions.MAX_LIMIT); } else { - context.setPage(pageIndex != null ? pageIndex : 1, pageSize != null ? pageSize : 100); + options.setPage(pageIndex != null ? pageIndex : 1, pageSize != null ? pageSize : 100); } - return context; + return options; } public Collection listTags() { diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java index f235a36e71d..d29e4a4eabe 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java @@ -22,7 +22,9 @@ package org.sonar.server.issue; import com.google.common.base.Function; import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.issue.Issue; @@ -35,16 +37,20 @@ import org.sonar.core.component.ComponentDto; import org.sonar.core.issue.db.IssueDto; import org.sonar.core.issue.db.IssueStorage; import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; import org.sonar.server.db.DbClient; +import org.sonar.server.es.SearchOptions; import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.issue.index.IssueDoc; import org.sonar.server.issue.notification.IssueChangeNotification; import org.sonar.server.rule.DefaultRuleFinder; -import org.sonar.server.search.QueryContext; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; @@ -83,12 +89,12 @@ public class IssueBulkChangeService { IssueBulkChangeResult result = new IssueBulkChangeResult(); - List issues = getByKeysForUpdate(issueBulkChangeQuery.issues()); + Collection issues = getByKeysForUpdate(issueBulkChangeQuery.issues()); Repository repository = new Repository(issues); List bulkActions = getActionsToApply(issueBulkChangeQuery, issues, userSession); IssueChangeContext issueChangeContext = IssueChangeContext.createUser(new Date(), userSession.login()); - Set concernedProjects = new HashSet(); + Set concernedProjects = new HashSet<>(); for (Issue issue : issues) { ActionContext actionContext = new ActionContext(issue, issueChangeContext); for (Action action : bulkActions) { @@ -119,31 +125,36 @@ public class IssueBulkChangeService { return result; } - private List getByKeysForUpdate(List issueKeys) { + private Collection getByKeysForUpdate(List issueKeys) { // Load from index to check permission - List authorizedIndexIssues = issueService.search(IssueQuery.builder().issueKeys(issueKeys).build(), new QueryContext().setMaxLimit()).getHits(); - List authorizedIssueKeys = newArrayList(Iterables.transform(authorizedIndexIssues, new Function() { + SearchOptions options = new SearchOptions().setLimit(SearchOptions.MAX_LIMIT); + // TODO restrict fields to issue key, in order to not load all other fields; + List authorizedIssues = issueService.search(IssueQuery.builder().issueKeys(issueKeys).build(), options).getDocs(); + Collection authorizedKeys = Collections2.transform(authorizedIssues, new Function() { @Override - public String apply(@Nullable Issue input) { - return input != null ? input.key() : null; + public String apply(IssueDoc input) { + return input.key(); } - })); + }); - DbSession session = dbClient.openSession(false); - try { - List issueDtos = dbClient.issueDao().selectByKeys(session, authorizedIssueKeys); - return newArrayList(Iterables.transform(issueDtos, new Function() { - @Override - public Issue apply(@Nullable IssueDto input) { - return input != null ? input.toDefaultIssue() : null; - } - })); - } finally { - session.close(); + if (!authorizedKeys.isEmpty()) { + DbSession session = dbClient.openSession(false); + try { + List dtos = dbClient.issueDao().selectByKeys(session, Lists.newArrayList(authorizedKeys)); + return Collections2.transform(dtos, new Function() { + @Override + public Issue apply(@Nullable IssueDto input) { + return input != null ? input.toDefaultIssue() : null; + } + }); + } finally { + MyBatis.closeQuietly(session); + } } + return Collections.emptyList(); } - private List getActionsToApply(IssueBulkChangeQuery issueBulkChangeQuery, List issues, UserSession userSession) { + private List getActionsToApply(IssueBulkChangeQuery issueBulkChangeQuery, Collection issues, UserSession userSession) { List bulkActions = newArrayList(); for (String actionKey : issueBulkChangeQuery.actions()) { Action action = getAction(actionKey); @@ -207,7 +218,7 @@ public class IssueBulkChangeService { private final Map components = newHashMap(); private final Map projects = newHashMap(); - public Repository(List issues) { + public Repository(Collection issues) { Set ruleKeys = newHashSet(); Set componentKeys = newHashSet(); Set projectKeys = newHashSet(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java index 8067ca37021..94ff60ae58f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java @@ -24,15 +24,17 @@ import com.google.common.collect.ImmutableSet; import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.sonar.api.rule.RuleKey; import org.sonar.server.search.QueryContext; +import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; import javax.annotation.Nullable; - import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Set; +import static com.google.common.collect.Sets.newHashSet; + /** * @since 3.6 */ @@ -84,6 +86,10 @@ public class IssueQuery { private final Boolean ignorePaging; private final Boolean contextualized; + private final String userLogin; + private final Set userGroups; + private final boolean checkAuthorization; + private IssueQuery(Builder builder) { this.issueKeys = defaultCollection(builder.issueKeys); this.severities = defaultCollection(builder.severities); @@ -114,6 +120,9 @@ public class IssueQuery { this.sort = builder.sort; this.asc = builder.asc; this.ignorePaging = builder.ignorePaging; + this.userLogin = builder.userLogin; + this.userGroups = builder.userGroups; + this.checkAuthorization = builder.checkAuthorization; this.contextualized = builder.contextualized; } @@ -247,6 +256,19 @@ public class IssueQuery { return ignorePaging; } + @CheckForNull + public String userLogin() { + return userLogin; + } + + public Set userGroups() { + return newHashSet(defaultCollection(userGroups)); + } + + public boolean checkAuthorization() { + return checkAuthorization; + } + @CheckForNull public Boolean isContextualized() { return contextualized; @@ -292,6 +314,9 @@ public class IssueQuery { private Boolean asc = false; private Boolean ignorePaging = false; private boolean contextualized; + private String userLogin = UserSession.get().login(); + private Set userGroups = UserSession.get().userGroups(); + private boolean checkAuthorization = true; private Builder() { } @@ -475,6 +500,21 @@ public class IssueQuery { return this; } + public Builder userLogin(@Nullable String userLogin) { + this.userLogin = userLogin; + return this; + } + + public Builder userGroups(@Nullable Set userGroups) { + this.userGroups = userGroups; + return this; + } + + public Builder checkAuthorization(boolean checkAuthorization) { + this.checkAuthorization = checkAuthorization; + return this; + } + public Builder setContextualized(boolean b) { this.contextualized = b; return this; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java index 0c893e2fe79..6379acc899f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java @@ -48,13 +48,13 @@ import org.sonar.core.issue.workflow.Transition; import org.sonar.core.persistence.DbSession; import org.sonar.core.rule.RuleDto; import org.sonar.server.db.DbClient; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.issue.actionplan.ActionPlanService; +import org.sonar.server.issue.index.IssueDoc; import org.sonar.server.issue.index.IssueIndex; import org.sonar.server.issue.notification.IssueChangeNotification; -import org.sonar.server.search.FacetValue; -import org.sonar.server.search.IndexClient; -import org.sonar.server.search.QueryContext; import org.sonar.server.source.index.SourceLineDoc; import org.sonar.server.source.index.SourceLineIndex; import org.sonar.server.user.UserSession; @@ -64,14 +64,20 @@ import org.sonar.server.user.index.UserIndex; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; import static com.google.common.collect.Maps.newLinkedHashMap; public class IssueService implements ServerComponent { private final DbClient dbClient; - private final IndexClient indexClient; + private final IssueIndex issueIndex; private final IssueWorkflow workflow; private final IssueUpdater issueUpdater; @@ -84,7 +90,7 @@ public class IssueService implements ServerComponent { private final UserIndex userIndex; private final SourceLineIndex sourceLineIndex; - public IssueService(DbClient dbClient, IndexClient indexClient, + public IssueService(DbClient dbClient, IssueIndex issueIndex, IssueWorkflow workflow, IssueStorage issueStorage, IssueUpdater issueUpdater, @@ -95,7 +101,7 @@ public class IssueService implements ServerComponent { UserFinder userFinder, UserIndex userIndex, SourceLineIndex sourceLineIndex) { this.dbClient = dbClient; - this.indexClient = indexClient; + this.issueIndex = issueIndex; this.workflow = workflow; this.issueStorage = issueStorage; this.issueUpdater = issueUpdater; @@ -301,19 +307,20 @@ public class IssueService implements ServerComponent { public Map findIssueAssignees(IssueQuery query) { Map result = newLinkedHashMap(); - List facetValues = indexClient.get(IssueIndex.class).listAssignees(query); - for (FacetValue facetValue : facetValues) { - if ("_notAssigned_".equals(facetValue.getKey())) { - result.put(null, facetValue.getValue()); + Map buckets = issueIndex.searchForAssignees(query); + for (Map.Entry bucket : buckets.entrySet()) { + if ("_notAssigned_".equals(bucket.getKey())) { + // TODO null key ? + result.put(null, bucket.getValue()); } else { - result.put(facetValue.getKey(), facetValue.getValue()); + result.put(bucket.getKey(), bucket.getValue()); } } return result; } public Issue getByKey(String key) { - return indexClient.get(IssueIndex.class).getByKey(key); + return issueIndex.getByKey(key); } IssueDto getByKeyForUpdate(DbSession session, String key) { @@ -346,20 +353,29 @@ public class IssueService implements ServerComponent { return ruleFinder.findByKey(ruleKey); } - public org.sonar.server.search.Result search(IssueQuery query, QueryContext options) { - return indexClient.get(IssueIndex.class).search(query, options); + public SearchResult search(IssueQuery query, SearchOptions options) { + return issueIndex.search(query, options); } private void verifyLoggedIn() { UserSession.get().checkLoggedIn(); } - public Collection listTags(@Nullable String query, int pageSize) { - return indexClient.get(IssueIndex.class).listTagsMatching(query, pageSize); + /** + * Search for all tags, whatever issue resolution or user access rights + */ + public List listTags(@Nullable String textQuery, int pageSize) { + IssueQuery query = IssueQuery.builder() + .checkAuthorization(false) + .build(); + return issueIndex.listTags(query, textQuery, pageSize); } - public Collection listAuthors(@Nullable String query, int pageSize) { - return indexClient.get(IssueIndex.class).listAuthorsMatching(query, pageSize); + public List listAuthors(@Nullable String textQuery, int pageSize) { + IssueQuery query = IssueQuery.builder() + .checkAuthorization(false) + .build(); + return issueIndex.listAuthors(query, textQuery, pageSize); } public Collection setTags(String issueKey, Collection tags) { @@ -379,8 +395,17 @@ public class IssueService implements ServerComponent { } } + // TODO check compatibility with Views, projects, etc. public Map listTagsForComponent(String componentUuid, int pageSize) { - return indexClient.get(IssueIndex.class).listTagsForComponent(componentUuid, pageSize); + IssueQuery query = IssueQuery.builder() + .userLogin(UserSession.get().login()) + .userGroups(UserSession.get().userGroups()) + .moduleRootUuids(Arrays.asList(componentUuid)) + .onComponentOnly(false) + .setContextualized(true) + .resolved(false) + .build(); + return issueIndex.countTags(query, pageSize); } @CheckForNull diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java index 49aa95a09ac..1bf7d3ee49b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java @@ -30,10 +30,9 @@ import org.sonar.core.issue.IssueUpdater; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.user.UserSession; -import java.util.List; +import java.util.Collection; import java.util.Map; - public class PlanAction extends Action implements ServerComponent { public static final String KEY = "plan"; @@ -50,7 +49,7 @@ public class PlanAction extends Action implements ServerComponent { } @Override - public boolean verify(Map properties, List issues, UserSession userSession) { + public boolean verify(Map properties, Collection issues, UserSession userSession) { String actionPlanValue = planValue(properties); if (!Strings.isNullOrEmpty(actionPlanValue)) { ActionPlan actionPlan = selectActionPlan(actionPlanValue, userSession); @@ -67,7 +66,7 @@ public class PlanAction extends Action implements ServerComponent { @Override public boolean execute(Map properties, Context context) { - if(!properties.containsKey(VERIFIED_ACTION_PLAN)) { + if (!properties.containsKey(VERIFIED_ACTION_PLAN)) { throw new IllegalArgumentException("Action plan is missing from the execution parameters"); } ActionPlan actionPlan = (ActionPlan) properties.get(VERIFIED_ACTION_PLAN); @@ -78,7 +77,7 @@ public class PlanAction extends Action implements ServerComponent { return (String) properties.get("plan"); } - private void verifyIssuesAreAllRelatedOnActionPlanProject(List issues, ActionPlan actionPlan) { + private void verifyIssuesAreAllRelatedOnActionPlanProject(Collection issues, ActionPlan actionPlan) { String projectKey = actionPlan.projectKey(); for (Issue issue : issues) { DefaultIssue defaultIssue = (DefaultIssue) issue; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java index a48f22c4e2a..7a8efdc62b4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java @@ -30,7 +30,7 @@ import org.sonar.api.web.UserRole; import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; -import java.util.List; +import java.util.Collection; import java.util.Map; public class SetSeverityAction extends Action implements ServerComponent { @@ -45,7 +45,7 @@ public class SetSeverityAction extends Action implements ServerComponent { super.setConditions(new IsUnResolved(), new Condition() { @Override public boolean matches(Issue issue) { - return isCurrentUserIssueAdmin(((DefaultIssue) issue).projectKey()); + return isCurrentUserIssueAdmin(issue.projectKey()); } }); } @@ -55,7 +55,7 @@ public class SetSeverityAction extends Action implements ServerComponent { } @Override - public boolean verify(Map properties, List issues, UserSession userSession) { + public boolean verify(Map properties, Collection issues, UserSession userSession) { severity(properties); return true; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java index b2c39ccaa9b..bb18afbfdda 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java @@ -31,7 +31,7 @@ import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.issue.workflow.Transition; import org.sonar.server.user.UserSession; -import java.util.List; +import java.util.Collection; import java.util.Map; public class TransitionAction extends Action implements ServerComponent { @@ -46,7 +46,7 @@ public class TransitionAction extends Action implements ServerComponent { } @Override - public boolean verify(Map properties, List issues, UserSession userSession) { + public boolean verify(Map properties, Collection issues, UserSession userSession) { transition(properties); return true; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java b/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java index f8bd5f91d53..958135fb50d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java @@ -28,8 +28,6 @@ import org.sonar.core.persistence.MyBatis; import org.sonar.server.exceptions.NotFoundException; import javax.annotation.CheckForNull; - -import java.util.Collection; import java.util.List; public class IssueDao extends org.sonar.core.issue.db.IssueDao implements DaoComponent { @@ -55,7 +53,7 @@ public class IssueDao extends org.sonar.core.issue.db.IssueDao implements DaoCom return mapper(session).selectByActionPlan(actionPlan); } - public List selectByKeys(DbSession session, Collection keys) { + public List selectByKeys(DbSession session, List keys) { return mapper(session).selectByKeys(keys); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterService.java index bf53ae3f6c3..df6025a9b34 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterService.java @@ -25,7 +25,6 @@ import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import org.sonar.api.ServerComponent; -import org.sonar.api.issue.Issue; import org.sonar.api.utils.Paging; import org.sonar.core.issue.DefaultIssueFilter; import org.sonar.core.issue.IssueFilterSerializer; @@ -35,14 +34,15 @@ import org.sonar.core.issue.db.IssueFilterFavouriteDao; import org.sonar.core.issue.db.IssueFilterFavouriteDto; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.user.AuthorizationDao; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.issue.IssueQuery; +import org.sonar.server.issue.index.IssueDoc; import org.sonar.server.issue.index.IssueIndex; -import org.sonar.server.search.QueryContext; -import org.sonar.server.search.Result; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; @@ -61,8 +61,8 @@ public class IssueFilterService implements ServerComponent { private final IssueFilterSerializer serializer; public IssueFilterService(IssueFilterDao filterDao, IssueFilterFavouriteDao favouriteDao, - IssueIndex issueIndex, AuthorizationDao authorizationDao, - IssueFilterSerializer serializer) { + IssueIndex issueIndex, AuthorizationDao authorizationDao, + IssueFilterSerializer serializer) { this.filterDao = filterDao; this.favouriteDao = favouriteDao; this.issueIndex = issueIndex; @@ -70,8 +70,8 @@ public class IssueFilterService implements ServerComponent { this.serializer = serializer; } - public IssueFilterResult execute(IssueQuery issueQuery, QueryContext context) { - return createIssueFilterResult(issueIndex.search(issueQuery, context), context); + public IssueFilterResult execute(IssueQuery issueQuery, SearchOptions options) { + return createIssueFilterResult(issueIndex.search(issueQuery, options), options); } public DefaultIssueFilter find(Long id, UserSession userSession) { @@ -335,8 +335,8 @@ public class IssueFilterService implements ServerComponent { return authorizationDao.selectGlobalPermissions(user).contains(GlobalPermissions.SYSTEM_ADMIN); } - private IssueFilterResult createIssueFilterResult(Result issues, QueryContext context) { - return new IssueFilterResult(issues.getHits(), Paging.create(context.getLimit(), context.getPage(), ((Long) issues.getTotal()).intValue())); + private IssueFilterResult createIssueFilterResult(SearchResult issues, SearchOptions options) { + return new IssueFilterResult(issues.getDocs(), Paging.create(options.getLimit(), options.getPage(), (int) issues.getTotal())); } private boolean hasUserSharingPermission(String user) { @@ -345,21 +345,21 @@ public class IssueFilterService implements ServerComponent { public static class IssueFilterResult { - private final List issues; + private final List issues; private final Paging paging; - public IssueFilterResult(List issues, Paging paging) { + public IssueFilterResult(List issues, Paging paging) { this.issues = issues; this.paging = paging; } - public List issues(){ + public List issues() { return issues; } - public Paging paging(){ + public Paging paging() { return paging; } - } - + } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/FakeIssueDto.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/FakeIssueDto.java deleted file mode 100644 index 0db9d5d5b24..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/FakeIssueDto.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.issue.index; - -import org.sonar.core.persistence.Dto; - -public class FakeIssueDto extends Dto { - @Override - public String getKey() { - throw new UnsupportedOperationException(); - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java index 42064038b10..6e42d8c5b21 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -20,24 +20,25 @@ package org.sonar.server.issue.index; import com.google.common.base.Function; -import com.google.common.base.Preconditions; -import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.commons.lang.BooleanUtils; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.index.query.*; +import org.elasticsearch.index.query.BoolFilterBuilder; +import org.elasticsearch.index.query.FilterBuilder; +import org.elasticsearch.index.query.FilterBuilders; +import org.elasticsearch.index.query.OrFilterBuilder; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogram.Interval; +import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogram; import org.elasticsearch.search.aggregations.bucket.missing.InternalMissing; import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket; -import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order; import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder; import org.elasticsearch.search.aggregations.metrics.min.Min; import org.joda.time.Duration; @@ -45,10 +46,16 @@ import org.sonar.api.issue.Issue; import org.sonar.api.rule.Severity; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.System2; +import org.sonar.server.es.BaseIndex; +import org.sonar.server.es.EsClient; +import org.sonar.server.es.EsUtils; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; import org.sonar.server.es.Sorting; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.issue.IssueQuery; import org.sonar.server.issue.filter.IssueFilterParameters; -import org.sonar.server.search.*; +import org.sonar.server.search.StickyFacetBuilder; import org.sonar.server.user.UserSession; import org.sonar.server.view.index.ViewIndexDefinition; @@ -56,179 +63,155 @@ import javax.annotation.CheckForNull; import javax.annotation.Nullable; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; import static com.google.common.collect.Lists.newArrayList; -public class IssueIndex extends BaseIndex { - +/** + * The unique entry-point to interact with Elasticsearch index "issues". + * All the requests are listed here. + */ +public class IssueIndex extends BaseIndex { + + public static final List SUPPORTED_FACETS = ImmutableList.of( + IssueFilterParameters.SEVERITIES, + IssueFilterParameters.STATUSES, + IssueFilterParameters.RESOLUTIONS, + IssueFilterParameters.ACTION_PLANS, + IssueFilterParameters.PROJECT_UUIDS, + IssueFilterParameters.RULES, + IssueFilterParameters.ASSIGNEES, + IssueFilterParameters.REPORTERS, + IssueFilterParameters.AUTHORS, + IssueFilterParameters.MODULE_UUIDS, + IssueFilterParameters.FILE_UUIDS, + IssueFilterParameters.DIRECTORIES, + IssueFilterParameters.LANGUAGES, + IssueFilterParameters.TAGS, + IssueFilterParameters.CREATED_AT); + + // TODO to be documented private static final String FILTER_COMPONENT_ROOT = "__componentRoot"; + // TODO to be documented + // TODO move to Facets ? private static final String FACET_SUFFIX_MISSING = "_missing"; - private static final int DEFAULT_ISSUE_FACET_SIZE = 15; - + private static final int DEFAULT_FACET_SIZE = 15; private static final Duration TWENTY_DAYS = Duration.standardDays(20L); private static final Duration TWENTY_WEEKS = Duration.standardDays(20L * 7L); private static final Duration TWENTY_MONTHS = Duration.standardDays(20L * 30L); - private final Sorting sorting; + /** + * Convert an Elasticsearch result (a map) to an {@link org.sonar.server.issue.index.IssueDoc}. It's + * used for {@link org.sonar.server.es.SearchResult}. + */ + private static final Function, IssueDoc> DOC_CONVERTER = new Function, IssueDoc>() { + @Override + public IssueDoc apply(Map input) { + return new IssueDoc(input); + } + }; + private final Sorting sorting; private final System2 system; - public IssueIndex(SearchClient client, System2 system) { - super(IndexDefinition.ISSUES, null, client); - this.system = system; + public IssueIndex(EsClient client, System2 system) { + super(client); - sorting = new Sorting(); - sorting.add(IssueQuery.SORT_BY_ASSIGNEE, IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE); - sorting.add(IssueQuery.SORT_BY_STATUS, IssueIndexDefinition.FIELD_ISSUE_STATUS); - sorting.add(IssueQuery.SORT_BY_SEVERITY, IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE); - sorting.add(IssueQuery.SORT_BY_CREATION_DATE, IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT); - sorting.add(IssueQuery.SORT_BY_UPDATE_DATE, IssueIndexDefinition.FIELD_ISSUE_FUNC_UPDATED_AT); - sorting.add(IssueQuery.SORT_BY_CLOSE_DATE, IssueIndexDefinition.FIELD_ISSUE_FUNC_CLOSED_AT); - sorting.add(IssueQuery.SORT_BY_FILE_LINE, IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID); - sorting.add(IssueQuery.SORT_BY_FILE_LINE, IssueIndexDefinition.FIELD_ISSUE_FILE_PATH); - sorting.add(IssueQuery.SORT_BY_FILE_LINE, IssueIndexDefinition.FIELD_ISSUE_LINE); - sorting.add(IssueQuery.SORT_BY_FILE_LINE, IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE).reverse(); - sorting.add(IssueQuery.SORT_BY_FILE_LINE, IssueIndexDefinition.FIELD_ISSUE_KEY); + this.system = system; + this.sorting = new Sorting(); + this.sorting.add(IssueQuery.SORT_BY_ASSIGNEE, IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE); + this.sorting.add(IssueQuery.SORT_BY_STATUS, IssueIndexDefinition.FIELD_ISSUE_STATUS); + this.sorting.add(IssueQuery.SORT_BY_SEVERITY, IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE); + this.sorting.add(IssueQuery.SORT_BY_CREATION_DATE, IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT); + this.sorting.add(IssueQuery.SORT_BY_UPDATE_DATE, IssueIndexDefinition.FIELD_ISSUE_FUNC_UPDATED_AT); + this.sorting.add(IssueQuery.SORT_BY_CLOSE_DATE, IssueIndexDefinition.FIELD_ISSUE_FUNC_CLOSED_AT); + this.sorting.add(IssueQuery.SORT_BY_FILE_LINE, IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID); + this.sorting.add(IssueQuery.SORT_BY_FILE_LINE, IssueIndexDefinition.FIELD_ISSUE_FILE_PATH); + this.sorting.add(IssueQuery.SORT_BY_FILE_LINE, IssueIndexDefinition.FIELD_ISSUE_LINE); + this.sorting.add(IssueQuery.SORT_BY_FILE_LINE, IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE).reverse(); + this.sorting.add(IssueQuery.SORT_BY_FILE_LINE, IssueIndexDefinition.FIELD_ISSUE_KEY); // by default order by updated date and issue key (in order to be deterministic when same ms) - sorting.addDefault(IssueIndexDefinition.FIELD_ISSUE_FUNC_UPDATED_AT).reverse(); - sorting.addDefault(IssueIndexDefinition.FIELD_ISSUE_KEY); - } - - @Override - protected void initializeIndex() { - // replaced by IssueIndexDefinition - } - - @Override - protected String getKeyValue(String keyString) { - return keyString; - } - - @Override - protected Map mapProperties() { - throw new UnsupportedOperationException("Being refactored"); + this.sorting.addDefault(IssueIndexDefinition.FIELD_ISSUE_FUNC_UPDATED_AT).reverse(); + this.sorting.addDefault(IssueIndexDefinition.FIELD_ISSUE_KEY); } - @Override - protected Map mapKey() { - throw new UnsupportedOperationException("Being refactored"); - } - - @Override - protected IssueDoc toDoc(Map fields) { - Preconditions.checkNotNull(fields, "Cannot construct Issue with null response"); - return new IssueDoc(fields); - } - - @Override - public Issue getNullableByKey(String key) { - Result result = search(IssueQuery.builder().issueKeys(newArrayList(key)).build(), new QueryContext()); + /** + * Warning, this method is not efficient as routing (the project uuid) is not known. + * All the ES cluster nodes are involved. + */ + @CheckForNull + public IssueDoc getNullableByKey(String key) { + SearchResult result = search(IssueQuery.builder().issueKeys(newArrayList(key)).build(), new SearchOptions()); if (result.getTotal() == 1) { - return result.getHits().get(0); + return result.getDocs().get(0); } return null; } - public List listAssignees(IssueQuery query) { - QueryContext queryContext = new QueryContext().setPage(1, 0); - - SearchRequestBuilder esSearch = getClient() - .prepareSearch(IssueIndexDefinition.INDEX) - .setTypes(IssueIndexDefinition.TYPE_ISSUE); - - QueryBuilder esQuery = QueryBuilders.matchAllQuery(); - BoolFilterBuilder esFilter = getFilter(query, queryContext); - if (esFilter.hasClauses()) { - esSearch.setQuery(QueryBuilders.filteredQuery(esQuery, esFilter)); - } else { - esSearch.setQuery(esQuery); + /** + * Warning, see {@link #getNullableByKey(String)}. + * A {@link org.sonar.server.exceptions.NotFoundException} is thrown if key does not exist. + */ + public IssueDoc getByKey(String key) { + IssueDoc value = getNullableByKey(key); + if (value == null) { + throw new NotFoundException(String.format("Issue with key '%s' does not exist", key)); } - esSearch.addAggregation(AggregationBuilders.terms(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE) - .size(Integer.MAX_VALUE) - .field(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE)); - esSearch.addAggregation(AggregationBuilders.missing("notAssigned") - .field(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE)); - - SearchResponse response = esSearch.get(); - Terms aggregation = (Terms) response.getAggregations().getAsMap().get(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE); - List facetValues = newArrayList(); - for (Terms.Bucket value : aggregation.getBuckets()) { - facetValues.add(new FacetValue(value.getKey(), value.getDocCount())); - } - facetValues.add(new FacetValue("_notAssigned_", ((InternalMissing) response.getAggregations().get("notAssigned")).getDocCount())); - - return facetValues; + return value; } - public Result search(IssueQuery query, QueryContext options) { - SearchRequestBuilder esSearch = getClient() + public SearchResult search(IssueQuery query, SearchOptions options) { + SearchRequestBuilder requestBuilder = getClient() .prepareSearch(IssueIndexDefinition.INDEX) .setTypes(IssueIndexDefinition.TYPE_ISSUE); - if (options.isScroll()) { - esSearch.setSearchType(SearchType.SCAN); - esSearch.setScroll(TimeValue.timeValueMinutes(3)); - } - - setSorting(query, esSearch); - setPagination(options, esSearch); + configureSorting(query, requestBuilder); + configurePagination(options, requestBuilder); QueryBuilder esQuery = QueryBuilders.matchAllQuery(); - Map filters = getFilters(query, options); - setQueryFilter(esSearch, esQuery, filters); - - setFacets(query, options, filters, esQuery, esSearch); - - SearchResponse response = esSearch.get(); - return new Result<>(this, response); - } - - protected void setQueryFilter(SearchRequestBuilder esSearch, QueryBuilder esQuery, Map filters) { BoolFilterBuilder esFilter = FilterBuilders.boolFilter(); + Map filters = createFilters(query); for (FilterBuilder filter : filters.values()) { if (filter != null) { esFilter.must(filter); } } - if (esFilter.hasClauses()) { - esSearch.setQuery(QueryBuilders.filteredQuery(esQuery, esFilter)); + requestBuilder.setQuery(QueryBuilders.filteredQuery(esQuery, esFilter)); } else { - esSearch.setQuery(esQuery); + requestBuilder.setQuery(esQuery); } + + configureStickyFacets(query, options, filters, esQuery, requestBuilder); + return new SearchResult<>(requestBuilder.get(), DOC_CONVERTER); } - private BoolFilterBuilder getFilter(IssueQuery query, QueryContext options) { - BoolFilterBuilder esFilter = FilterBuilders.boolFilter(); - for (FilterBuilder filter : getFilters(query, options).values()) { - if (filter != null) { - esFilter.must(filter); - } + private void configureSorting(IssueQuery query, SearchRequestBuilder esRequest) { + String sortField = query.sort(); + if (sortField != null) { + boolean asc = BooleanUtils.isTrue(query.asc()); + sorting.fill(esRequest, sortField, asc); + } else { + sorting.fillDefault(esRequest); } - return esFilter; } - public void deleteClosedIssuesOfProjectBefore(String uuid, Date beforeDate) { - FilterBuilder projectFilter = FilterBuilders.boolFilter().must(FilterBuilders.termsFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, uuid)); - FilterBuilder dateFilter = FilterBuilders.rangeFilter(IssueIndexDefinition.FIELD_ISSUE_FUNC_CLOSED_AT).lt(beforeDate.getTime()); - QueryBuilder queryBuilder = QueryBuilders.filteredQuery( - QueryBuilders.matchAllQuery(), - FilterBuilders.andFilter(projectFilter, dateFilter) - ); - - getClient().prepareDeleteByQuery(IssueIndexDefinition.INDEX).setQuery(queryBuilder).get(); + protected void configurePagination(SearchOptions options, SearchRequestBuilder esSearch) { + esSearch.setFrom(options.getOffset()).setSize(options.getLimit()); } - /* Build main filter (match based) */ - protected Map getFilters(IssueQuery query, QueryContext options) { - - Map filters = Maps.newHashMap(); - - filters.put("__authorization", getAuthorizationFilter(options)); + private Map createFilters(IssueQuery query) { + Map filters = new HashMap<>(); + filters.put("__authorization", createAuthorizationFilter(query)); // Issue is assigned Filter String isAssigned = "__isAssigned"; @@ -255,20 +238,20 @@ public class IssueIndex extends BaseIndex { } // Field Filters - filters.put(IssueIndexDefinition.FIELD_ISSUE_KEY, matchFilter(IssueIndexDefinition.FIELD_ISSUE_KEY, query.issueKeys())); - filters.put(IssueIndexDefinition.FIELD_ISSUE_ACTION_PLAN, matchFilter(IssueIndexDefinition.FIELD_ISSUE_ACTION_PLAN, query.actionPlans())); - filters.put(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE, matchFilter(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE, query.assignees())); + filters.put(IssueIndexDefinition.FIELD_ISSUE_KEY, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_KEY, query.issueKeys())); + filters.put(IssueIndexDefinition.FIELD_ISSUE_ACTION_PLAN, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_ACTION_PLAN, query.actionPlans())); + filters.put(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE, query.assignees())); addComponentRelatedFilters(query, filters); - filters.put(IssueIndexDefinition.FIELD_ISSUE_LANGUAGE, matchFilter(IssueIndexDefinition.FIELD_ISSUE_LANGUAGE, query.languages())); - filters.put(IssueIndexDefinition.FIELD_ISSUE_TAGS, matchFilter(IssueIndexDefinition.FIELD_ISSUE_TAGS, query.tags())); - filters.put(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, matchFilter(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, query.resolutions())); - filters.put(IssueIndexDefinition.FIELD_ISSUE_REPORTER, matchFilter(IssueIndexDefinition.FIELD_ISSUE_REPORTER, query.reporters())); - filters.put(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, matchFilter(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, query.authors())); - filters.put(IssueIndexDefinition.FIELD_ISSUE_RULE_KEY, matchFilter(IssueIndexDefinition.FIELD_ISSUE_RULE_KEY, query.rules())); - filters.put(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, matchFilter(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, query.severities())); - filters.put(IssueIndexDefinition.FIELD_ISSUE_STATUS, matchFilter(IssueIndexDefinition.FIELD_ISSUE_STATUS, query.statuses())); + filters.put(IssueIndexDefinition.FIELD_ISSUE_LANGUAGE, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_LANGUAGE, query.languages())); + filters.put(IssueIndexDefinition.FIELD_ISSUE_TAGS, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_TAGS, query.tags())); + filters.put(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, query.resolutions())); + filters.put(IssueIndexDefinition.FIELD_ISSUE_REPORTER, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_REPORTER, query.reporters())); + filters.put(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, query.authors())); + filters.put(IssueIndexDefinition.FIELD_ISSUE_RULE_KEY, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_RULE_KEY, query.rules())); + filters.put(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, query.severities())); + filters.put(IssueIndexDefinition.FIELD_ISSUE_STATUS, createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_STATUS, query.statuses())); addDatesFilter(filters, query); @@ -276,14 +259,14 @@ public class IssueIndex extends BaseIndex { } private void addComponentRelatedFilters(IssueQuery query, Map filters) { - FilterBuilder viewFilter = viewFilter(query.viewUuids()); - FilterBuilder componentFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.componentUuids()); - FilterBuilder projectFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, query.projectUuids()); - FilterBuilder moduleRootFilter = moduleRootFilter(query.moduleRootUuids()); - FilterBuilder moduleFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, query.moduleUuids()); - FilterBuilder directoryRootFilter = directoryRootFilter(query.moduleUuids(), query.directories()); - FilterBuilder directoryFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, query.directories()); - FilterBuilder fileFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.fileUuids()); + FilterBuilder viewFilter = createViewFilter(query.viewUuids()); + FilterBuilder componentFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.componentUuids()); + FilterBuilder projectFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, query.projectUuids()); + FilterBuilder moduleRootFilter = createModuleRootFilter(query.moduleRootUuids()); + FilterBuilder moduleFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, query.moduleUuids()); + FilterBuilder directoryRootFilter = createDirectoryRootFilter(query.moduleUuids(), query.directories()); + FilterBuilder directoryFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, query.directories()); + FilterBuilder fileFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.fileUuids()); if (BooleanUtils.isTrue(query.isContextualized())) { if (viewFilter != null) { @@ -325,12 +308,12 @@ public class IssueIndex extends BaseIndex { } @CheckForNull - private FilterBuilder moduleRootFilter(Collection componentUuids) { + private FilterBuilder createModuleRootFilter(Collection componentUuids) { if (componentUuids.isEmpty()) { return null; } - FilterBuilder componentFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentUuids); - FilterBuilder modulePathFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_PATH, componentUuids); + FilterBuilder componentFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentUuids); + FilterBuilder modulePathFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_PATH, componentUuids); FilterBuilder compositeFilter = null; if (componentFilter != null) { if (modulePathFilter != null) { @@ -341,14 +324,15 @@ public class IssueIndex extends BaseIndex { } else if (modulePathFilter != null) { compositeFilter = modulePathFilter; } + System.out.println("compositeFilter:" + compositeFilter); return compositeFilter; } @CheckForNull - private FilterBuilder directoryRootFilter(Collection moduleUuids, Collection directoryPaths) { + private FilterBuilder createDirectoryRootFilter(Collection moduleUuids, Collection directoryPaths) { BoolFilterBuilder directoryTop = null; - FilterBuilder moduleFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, moduleUuids); - FilterBuilder directoryFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, directoryPaths); + FilterBuilder moduleFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, moduleUuids); + FilterBuilder directoryFilter = createTermsFilter(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, directoryPaths); if (moduleFilter != null) { directoryTop = FilterBuilders.boolFilter(); directoryTop.must(moduleFilter); @@ -363,7 +347,7 @@ public class IssueIndex extends BaseIndex { } @CheckForNull - private FilterBuilder viewFilter(Collection viewUuids) { + private FilterBuilder createViewFilter(Collection viewUuids) { if (viewUuids.isEmpty()) { return null; } @@ -381,26 +365,29 @@ public class IssueIndex extends BaseIndex { } public static String viewsLookupCacheKey(String viewUuid) { - return IssueIndexDefinition.TYPE_ISSUE + viewUuid + ViewIndexDefinition.TYPE_VIEW; + return String.format("%s%s%s", IssueIndexDefinition.TYPE_ISSUE, viewUuid, ViewIndexDefinition.TYPE_VIEW); } - private FilterBuilder getAuthorizationFilter(QueryContext options) { - String user = options.getUserLogin(); - Set groups = options.getUserGroups(); - OrFilterBuilder groupsAndUser = FilterBuilders.orFilter(); - if (user != null) { - groupsAndUser.add(FilterBuilders.termFilter(IssueIndexDefinition.FIELD_AUTHORIZATION_USERS, user)); - } - for (String group : groups) { - groupsAndUser.add(FilterBuilders.termFilter(IssueIndexDefinition.FIELD_AUTHORIZATION_GROUPS, group)); + private FilterBuilder createAuthorizationFilter(IssueQuery query) { + if (query.checkAuthorization()) { + String user = query.userLogin(); + OrFilterBuilder groupsAndUser = FilterBuilders.orFilter(); + if (user != null) { + groupsAndUser.add(FilterBuilders.termFilter(IssueIndexDefinition.FIELD_AUTHORIZATION_USERS, user)); + } + for (String group : query.userGroups()) { + groupsAndUser.add(FilterBuilders.termFilter(IssueIndexDefinition.FIELD_AUTHORIZATION_GROUPS, group)); + } + return FilterBuilders.hasParentFilter(IssueIndexDefinition.TYPE_AUTHORIZATION, + QueryBuilders.filteredQuery( + QueryBuilders.matchAllQuery(), + FilterBuilders.boolFilter() + .must(groupsAndUser) + .cache(true)) + ); + } else { + return FilterBuilders.matchAllFilter(); } - return FilterBuilders.hasParentFilter(IssueIndexDefinition.TYPE_AUTHORIZATION, - QueryBuilders.filteredQuery( - QueryBuilders.matchAllQuery(), - FilterBuilders.boolFilter() - .must(groupsAndUser) - .cache(true)) - ); } private void addDatesFilter(Map filters, IssueQuery query) { @@ -424,9 +411,9 @@ public class IssueIndex extends BaseIndex { } } - private void setFacets(IssueQuery query, QueryContext options, Map filters, QueryBuilder esQuery, SearchRequestBuilder esSearch) { - if (options.isFacet()) { - StickyFacetBuilder stickyFacetBuilder = stickyFacetBuilder(esQuery, filters); + private void configureStickyFacets(IssueQuery query, SearchOptions options, Map filters, QueryBuilder esQuery, SearchRequestBuilder esSearch) { + if (!options.getFacets().isEmpty()) { + StickyFacetBuilder stickyFacetBuilder = new StickyFacetBuilder(esQuery, filters); // Execute Term aggregations addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, IssueFilterParameters.SEVERITIES, IssueIndexDefinition.FIELD_ISSUE_SEVERITY, Severity.ALL.toArray()); @@ -450,33 +437,85 @@ public class IssueIndex extends BaseIndex { addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, IssueFilterParameters.AUTHORS, IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, query.authors().toArray()); - if (options.facets().contains(IssueFilterParameters.TAGS)) { + if (options.getFacets().contains(IssueFilterParameters.TAGS)) { esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(IssueIndexDefinition.FIELD_ISSUE_TAGS, IssueFilterParameters.TAGS, query.tags().toArray())); } - if (options.facets().contains(IssueFilterParameters.RESOLUTIONS)) { - esSearch.addAggregation(getResolutionFacet(filters, esQuery)); + if (options.getFacets().contains(IssueFilterParameters.RESOLUTIONS)) { + esSearch.addAggregation(createResolutionFacet(filters, esQuery)); } - if (options.facets().contains(IssueFilterParameters.ASSIGNEES)) { - esSearch.addAggregation(getAssigneesFacet(query, filters, esQuery)); + if (options.getFacets().contains(IssueFilterParameters.ASSIGNEES)) { + esSearch.addAggregation(createAssigneesFacet(query, filters, esQuery)); } - if (options.facets().contains(IssueFilterParameters.ACTION_PLANS)) { - esSearch.addAggregation(getActionPlansFacet(query, filters, esQuery)); + if (options.getFacets().contains(IssueFilterParameters.ACTION_PLANS)) { + esSearch.addAggregation(createActionPlansFacet(query, filters, esQuery)); } - if (options.facets().contains(IssueFilterParameters.CREATED_AT)) { + if (options.getFacets().contains(IssueFilterParameters.CREATED_AT)) { esSearch.addAggregation(getCreatedAtFacet(query, filters, esQuery)); } } } - private void addSimpleStickyFacetIfNeeded(QueryContext options, StickyFacetBuilder stickyFacetBuilder, SearchRequestBuilder esSearch, + private void addSimpleStickyFacetIfNeeded(SearchOptions options, StickyFacetBuilder stickyFacetBuilder, SearchRequestBuilder esSearch, String facetName, String fieldName, Object... selectedValues) { - if (options.facets().contains(facetName)) { - esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(fieldName, facetName, DEFAULT_ISSUE_FACET_SIZE, selectedValues)); + if (options.getFacets().contains(facetName)) { + esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(fieldName, facetName, DEFAULT_FACET_SIZE, selectedValues)); + } + } + + private AggregationBuilder getCreatedAtFacet(IssueQuery query, Map filters, QueryBuilder esQuery) { + Date now = system.newDate(); + SimpleDateFormat tzFormat = new SimpleDateFormat("XX"); + tzFormat.setTimeZone(TimeZone.getDefault()); + String timeZoneString = tzFormat.format(now); + + DateHistogram.Interval bucketSize = DateHistogram.Interval.YEAR; + Date createdAfter = query.createdAfter(); + long startTime = createdAfter == null ? getMinCreatedAt(filters, esQuery) : createdAfter.getTime(); + Date createdBefore = query.createdBefore(); + long endTime = createdBefore == null ? now.getTime() : createdBefore.getTime(); + Duration timeSpan = new Duration(startTime, endTime); + if (timeSpan.isShorterThan(TWENTY_DAYS)) { + bucketSize = DateHistogram.Interval.DAY; + } else if (timeSpan.isShorterThan(TWENTY_WEEKS)) { + bucketSize = DateHistogram.Interval.WEEK; + } else if (timeSpan.isShorterThan(TWENTY_MONTHS)) { + bucketSize = DateHistogram.Interval.MONTH; + } + + return AggregationBuilders.dateHistogram(IssueFilterParameters.CREATED_AT) + .field(IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT) + .interval(bucketSize) + .minDocCount(0L) + .format(DateUtils.DATETIME_FORMAT) + .preZone(timeZoneString) + .postZone(timeZoneString) + .extendedBounds(startTime, endTime); + } + + private long getMinCreatedAt(Map filters, QueryBuilder esQuery) { + String facetNameAndField = IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT; + SearchRequestBuilder esRequest = getClient() + .prepareSearch(IssueIndexDefinition.INDEX) + .setTypes(IssueIndexDefinition.TYPE_ISSUE) + .setSearchType(SearchType.COUNT); + BoolFilterBuilder esFilter = FilterBuilders.boolFilter(); + for (FilterBuilder filter : filters.values()) { + if (filter != null) { + esFilter.must(filter); + } } + if (esFilter.hasClauses()) { + esRequest.setQuery(QueryBuilders.filteredQuery(esQuery, esFilter)); + } else { + esRequest.setQuery(esQuery); + } + esRequest.addAggregation(AggregationBuilders.min(facetNameAndField).field(facetNameAndField)); + Min minValue = esRequest.get().getAggregations().get(facetNameAndField); + return (long) minValue.getValue(); } - private AggregationBuilder getAssigneesFacet(IssueQuery query, Map filters, QueryBuilder esQuery) { + private AggregationBuilder createAssigneesFacet(IssueQuery query, Map filters, QueryBuilder queryBuilder) { String fieldName = IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE; String facetName = IssueFilterParameters.ASSIGNEES; @@ -484,9 +523,9 @@ public class IssueIndex extends BaseIndex { Map assigneeFilters = Maps.newHashMap(filters); assigneeFilters.remove("__isAssigned"); assigneeFilters.remove(fieldName); - StickyFacetBuilder assigneeFacetBuilder = new StickyFacetBuilder(esQuery, assigneeFilters); + StickyFacetBuilder assigneeFacetBuilder = new StickyFacetBuilder(queryBuilder, assigneeFilters); BoolFilterBuilder facetFilter = assigneeFacetBuilder.getStickyFacetFilter(fieldName); - FilterAggregationBuilder facetTopAggregation = assigneeFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, DEFAULT_ISSUE_FACET_SIZE); + FilterAggregationBuilder facetTopAggregation = assigneeFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, DEFAULT_FACET_SIZE); List assignees = Lists.newArrayList(query.assignees()); UserSession session = UserSession.get(); @@ -507,7 +546,7 @@ public class IssueIndex extends BaseIndex { .subAggregation(facetTopAggregation); } - private AggregationBuilder getResolutionFacet(Map filters, QueryBuilder esQuery) { + private AggregationBuilder createResolutionFacet(Map filters, QueryBuilder esQuery) { String fieldName = IssueIndexDefinition.FIELD_ISSUE_RESOLUTION; String facetName = IssueFilterParameters.RESOLUTIONS; @@ -517,7 +556,7 @@ public class IssueIndex extends BaseIndex { resolutionFilters.remove(fieldName); StickyFacetBuilder assigneeFacetBuilder = new StickyFacetBuilder(esQuery, resolutionFilters); BoolFilterBuilder facetFilter = assigneeFacetBuilder.getStickyFacetFilter(fieldName); - FilterAggregationBuilder facetTopAggregation = assigneeFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, DEFAULT_ISSUE_FACET_SIZE); + FilterAggregationBuilder facetTopAggregation = assigneeFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, DEFAULT_FACET_SIZE); facetTopAggregation = assigneeFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, Issue.RESOLUTIONS.toArray()); // Add missing facet for unresolved issues @@ -532,7 +571,7 @@ public class IssueIndex extends BaseIndex { .subAggregation(facetTopAggregation); } - private AggregationBuilder getActionPlansFacet(IssueQuery query, Map filters, QueryBuilder esQuery) { + private AggregationBuilder createActionPlansFacet(IssueQuery query, Map filters, QueryBuilder esQuery) { String fieldName = IssueIndexDefinition.FIELD_ISSUE_ACTION_PLAN; String facetName = IssueFilterParameters.ACTION_PLANS; @@ -542,7 +581,7 @@ public class IssueIndex extends BaseIndex { actionPlanFilters.remove(fieldName); StickyFacetBuilder actionPlanFacetBuilder = new StickyFacetBuilder(esQuery, actionPlanFilters); BoolFilterBuilder facetFilter = actionPlanFacetBuilder.getStickyFacetFilter(fieldName); - FilterAggregationBuilder facetTopAggregation = actionPlanFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, DEFAULT_ISSUE_FACET_SIZE); + FilterAggregationBuilder facetTopAggregation = actionPlanFacetBuilder.buildTopFacetAggregation(fieldName, facetName, facetFilter, DEFAULT_FACET_SIZE); facetTopAggregation = actionPlanFacetBuilder.addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, query.actionPlans().toArray()); // Add missing facet for unresolved issues @@ -557,66 +596,8 @@ public class IssueIndex extends BaseIndex { .subAggregation(facetTopAggregation); } - private AggregationBuilder getCreatedAtFacet(IssueQuery query, Map filters, QueryBuilder esQuery) { - Date now = system.newDate(); - SimpleDateFormat tzFormat = new SimpleDateFormat("XX"); - tzFormat.setTimeZone(TimeZone.getDefault()); - String timeZoneString = tzFormat.format(now); - - Interval bucketSize = Interval.YEAR; - Date createdAfter = query.createdAfter(); - long startTime = createdAfter == null ? getMinCreatedAt(filters, esQuery) : createdAfter.getTime(); - Date createdBefore = query.createdBefore(); - long endTime = createdBefore == null ? now.getTime() : createdBefore.getTime(); - Duration timeSpan = new Duration(startTime, endTime); - if (timeSpan.isShorterThan(TWENTY_DAYS)) { - bucketSize = Interval.DAY; - } else if (timeSpan.isShorterThan(TWENTY_WEEKS)) { - bucketSize = Interval.WEEK; - } else if (timeSpan.isShorterThan(TWENTY_MONTHS)) { - bucketSize = Interval.MONTH; - } - - - return AggregationBuilders.dateHistogram(IssueFilterParameters.CREATED_AT) - .field(IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT) - .interval(bucketSize) - .minDocCount(0L) - .format(DateUtils.DATETIME_FORMAT) - .preZone(timeZoneString) - .postZone(timeZoneString) - .extendedBounds(startTime, endTime); - } - - private long getMinCreatedAt(Map filters, QueryBuilder esQuery) { - String facetNameAndField = IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT; - SearchRequestBuilder minCount = getClient() - .prepareSearch(IssueIndexDefinition.INDEX) - .setTypes(IssueIndexDefinition.TYPE_ISSUE) - .setSearchType(SearchType.COUNT); - setQueryFilter(minCount, esQuery, filters); - minCount.addAggregation(AggregationBuilders.min(facetNameAndField).field(facetNameAndField)); - Min minValue = minCount.get().getAggregations().get(facetNameAndField); - return (long) minValue.getValue(); - } - - private void setSorting(IssueQuery query, SearchRequestBuilder esRequest) { - String sortField = query.sort(); - if (sortField != null) { - boolean asc = BooleanUtils.isTrue(query.asc()); - sorting.fill(esRequest, sortField, asc); - } else { - sorting.fillDefault(esRequest); - } - } - - protected void setPagination(QueryContext options, SearchRequestBuilder esSearch) { - esSearch.setFrom(options.getOffset()); - esSearch.setSize(options.getLimit()); - } - @CheckForNull - private FilterBuilder matchFilter(String field, Collection values) { + private FilterBuilder createTermsFilter(String field, Collection values) { if (!values.isEmpty()) { return FilterBuilders.termsFilter(field, values); } else { @@ -624,55 +605,100 @@ public class IssueIndex extends BaseIndex { } } - public Collection listTagsMatching(@Nullable String query, int pageSize) { - return listTermsMatching(IssueIndexDefinition.FIELD_ISSUE_TAGS, query, pageSize); + public List listTags(IssueQuery query, @Nullable String textQuery, int maxNumberOfTags) { + Terms terms = listTermsMatching(IssueIndexDefinition.FIELD_ISSUE_TAGS, query, textQuery, Terms.Order.term(true), maxNumberOfTags); + return EsUtils.termsKeys(terms); } - public Collection listAuthorsMatching(@Nullable String query, int pageSize) { - return listTermsMatching(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, query, pageSize); + public Map countTags(IssueQuery query, int maxNumberOfTags) { + Terms terms = listTermsMatching(IssueIndexDefinition.FIELD_ISSUE_TAGS, query, null, Terms.Order.count(false), maxNumberOfTags); + return EsUtils.termsToMap(terms); } - private Collection listTermsMatching(String fieldName, @Nullable String query, int pageSize) { - SearchRequestBuilder count = getClient().prepareSearch(IssueIndexDefinition.INDEX) - .setTypes(IssueIndexDefinition.TYPE_ISSUE) - .setQuery(QueryBuilders.matchAllQuery()); + public List listAuthors(IssueQuery query, @Nullable String textQuery, int maxNumberOfAuthors) { + Terms terms = listTermsMatching(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, query, textQuery, Terms.Order.term(true), maxNumberOfAuthors); + return EsUtils.termsKeys(terms); + } + + private Terms listTermsMatching(String fieldName, IssueQuery query, @Nullable String textQuery, Terms.Order termsOrder, int maxNumberOfTags) { + SearchRequestBuilder requestBuilder = getClient() + .prepareSearch(IssueIndexDefinition.INDEX) + .setTypes(IssueIndexDefinition.TYPE_ISSUE); + + requestBuilder.setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), + createBoolFilter(query))); + + // TODO do not return hits + TermsBuilder aggreg = AggregationBuilders.terms("_ref") .field(fieldName) - .size(pageSize) - .order(Order.term(true)) + .size(maxNumberOfTags) + .order(termsOrder) .minDocCount(1L); - if (query != null) { - aggreg.include(".*" + query + ".*"); + if (textQuery != null) { + aggreg.include(String.format(".*%s.*", textQuery)); } - Terms result = count.addAggregation(aggreg).get().getAggregations().get("_ref"); - return Collections2.transform(result.getBuckets(), new Function() { - @Override - public String apply(Bucket bucket) { - return bucket.getKey(); - } - }); + SearchResponse searchResponse = requestBuilder.addAggregation(aggreg).get(); + return searchResponse.getAggregations().get("_ref"); } - public Map listTagsForComponent(String componentUuid, int pageSize) { - SearchRequestBuilder count = getClient().prepareSearch(IssueIndexDefinition.INDEX) - .setTypes(IssueIndexDefinition.TYPE_ISSUE) - .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), - FilterBuilders.boolFilter() - .must(getAuthorizationFilter(new QueryContext())) - .must(FilterBuilders.missingFilter(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION)) - .must(moduleRootFilter(Arrays.asList(componentUuid))))); - TermsBuilder aggreg = AggregationBuilders.terms("_ref") - .field(IssueIndexDefinition.FIELD_ISSUE_TAGS) - .size(pageSize) - .order(Order.count(false)) - .minDocCount(1L); - Terms result = count.addAggregation(aggreg).get().getAggregations().get("_ref"); + public void deleteClosedIssuesOfProjectBefore(String projectUuid, Date beforeDate) { + FilterBuilder projectFilter = FilterBuilders.boolFilter().must(FilterBuilders.termsFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, projectUuid)); + FilterBuilder dateFilter = FilterBuilders.rangeFilter(IssueIndexDefinition.FIELD_ISSUE_FUNC_CLOSED_AT).lt(beforeDate.getTime()); + QueryBuilder queryBuilder = QueryBuilders.filteredQuery( + QueryBuilders.matchAllQuery(), + FilterBuilders.andFilter(projectFilter, dateFilter) + ); + + getClient().prepareDeleteByQuery(IssueIndexDefinition.INDEX).setQuery(queryBuilder).get(); + } - Map map = Maps.newHashMap(); - for (Bucket bucket : result.getBuckets()) { - map.put(bucket.getKey(), bucket.getDocCount()); + public LinkedHashMap searchForAssignees(IssueQuery query) { + // TODO do not return hits + // TODO what's max size ? + + SearchRequestBuilder esSearch = getClient() + .prepareSearch(IssueIndexDefinition.INDEX) + .setTypes(IssueIndexDefinition.TYPE_ISSUE); + + QueryBuilder esQuery = QueryBuilders.matchAllQuery(); + BoolFilterBuilder esFilter = createBoolFilter(query); + if (esFilter.hasClauses()) { + esSearch.setQuery(QueryBuilders.filteredQuery(esQuery, esFilter)); + } else { + esSearch.setQuery(esQuery); + } + esSearch.addAggregation(AggregationBuilders.terms(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE) + .size(Integer.MAX_VALUE) + .field(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE)); + esSearch.addAggregation(AggregationBuilders.missing("notAssigned") + .field(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE)); + + SearchResponse response = esSearch.get(); + Terms aggregation = (Terms) response.getAggregations().getAsMap().get(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE); + LinkedHashMap result = EsUtils.termsToMap(aggregation); + result.put("_notAssigned_", ((InternalMissing) response.getAggregations().get("notAssigned")).getDocCount()); + return result; + } + + private BoolFilterBuilder createBoolFilter(IssueQuery query) { + BoolFilterBuilder boolFilter = FilterBuilders.boolFilter(); + for (FilterBuilder filter : createFilters(query).values()) { + // TODO Can it be null ? + if (filter != null) { + boolFilter.must(filter); + } } - return map; + return boolFilter; + } + + /** + * TODO used only by tests, so must be replaced by EsTester#countDocuments() + */ + public long countAll() { + return getClient().prepareCount(IssueIndexDefinition.INDEX) + .setTypes(IssueIndexDefinition.TYPE_ISSUE) + .get().getCount(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/AuthorsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/AuthorsAction.java index 3d500c0927c..8096dc5dd62 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/AuthorsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/AuthorsAction.java @@ -20,15 +20,15 @@ package org.sonar.server.issue.ws; import com.google.common.io.Resources; -import org.sonar.api.server.ws.*; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService.NewAction; import org.sonar.api.utils.text.JsonWriter; import org.sonar.server.issue.IssueService; -public class AuthorsAction implements RequestHandler { +public class AuthorsAction implements BaseIssuesWsAction { - private static final String PARAM_PAGE_SIZE = "ps"; - private static final String PARAM_QUERY = "q"; private final IssueService service; public AuthorsAction(IssueService service) { @@ -37,8 +37,8 @@ public class AuthorsAction implements RequestHandler { @Override public void handle(Request request, Response response) throws Exception { - String query = request.param(PARAM_QUERY); - int pageSize = request.mandatoryParamAsInt(PARAM_PAGE_SIZE); + String query = request.param(WebService.Param.TEXT_QUERY); + int pageSize = request.mandatoryParamAsInt(WebService.Param.PAGE_SIZE); JsonWriter json = response.newJsonWriter() .beginObject() @@ -52,17 +52,18 @@ public class AuthorsAction implements RequestHandler { json.endArray().endObject().close(); } - void define(WebService.NewController controller) { + @Override + public void define(WebService.NewController controller) { NewAction action = controller.createAction("authors") .setSince("5.1") .setDescription("Search SCM accounts which match a given query") .setResponseExample(Resources.getResource(this.getClass(), "example-authors.json")) .setHandler(this); - action.createParam(PARAM_QUERY) + action.createParam(WebService.Param.TEXT_QUERY) .setDescription("A pattern to match SCM accounts against") .setExampleValue("luke"); - action.createParam(PARAM_PAGE_SIZE) + action.createParam(WebService.Param.PAGE_SIZE) .setDescription("The size of the list to return") .setExampleValue("25") .setDefaultValue("10"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/BaseIssuesWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/BaseIssuesWsAction.java new file mode 100644 index 00000000000..051a34613c0 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/BaseIssuesWsAction.java @@ -0,0 +1,30 @@ +/* + * 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.issue.ws; + +import org.sonar.api.server.ws.RequestHandler; +import org.sonar.api.server.ws.WebService; + +interface BaseIssuesWsAction extends RequestHandler { + + void define(WebService.NewController controller); + +} + diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ComponentTagsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ComponentTagsAction.java index e642686d0bb..c68e9d00b78 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ComponentTagsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ComponentTagsAction.java @@ -21,7 +21,6 @@ package org.sonar.server.issue.ws; import com.google.common.io.Resources; import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.RequestHandler; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService.NewAction; @@ -34,7 +33,7 @@ import java.util.Map; * List issue tags matching a given query. * @since 5.1 */ -public class ComponentTagsAction implements RequestHandler { +public class ComponentTagsAction implements BaseIssuesWsAction { private final IssueService service; @@ -42,7 +41,8 @@ public class ComponentTagsAction implements RequestHandler { this.service = service; } - void define(WebService.NewController controller) { + @Override + public void define(WebService.NewController controller) { NewAction action = controller.createAction("component_tags") .setHandler(this) .setSince("5.1") diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java index 87340b7eaf1..1c0604c0283 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java @@ -29,7 +29,6 @@ import org.sonar.api.issue.internal.FieldDiffs; import org.sonar.api.server.debt.DebtCharacteristic; import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic; import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.RequestHandler; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.user.User; @@ -54,14 +53,13 @@ import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; import javax.annotation.Nullable; - import java.util.Date; import java.util.List; import java.util.Map; import static com.google.common.collect.Maps.newHashMap; -public class IssueShowAction implements RequestHandler { +public class IssueShowAction implements BaseIssuesWsAction { public static final String SHOW_ACTION = "show"; @@ -94,7 +92,8 @@ public class IssueShowAction implements RequestHandler { this.durations = durations; } - void define(WebService.NewController controller) { + @Override + public void define(WebService.NewController controller) { WebService.NewAction action = controller.createAction(SHOW_ACTION) .setDescription("Detail of issue") .setSince("4.2") diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java index 8efe7e1f039..df9e92da850 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java @@ -42,22 +42,10 @@ public class IssuesWs implements WebService { public static final String DO_ACTION_ACTION = "do_action"; public static final String BULK_CHANGE_ACTION = "bulk_change"; - private final IssueShowAction showAction; - private final SearchAction esSearchAction; - private final TagsAction tagsAction; - private final SetTagsAction setTagsAction; - private final ComponentTagsAction componentTagsAction; - private final AuthorsAction authorsAction; - - - public IssuesWs(IssueShowAction showAction, SearchAction searchAction, TagsAction tagsAction, SetTagsAction setTagsAction, ComponentTagsAction componentTagsAction, - AuthorsAction authorsAction) { - this.showAction = showAction; - this.esSearchAction = searchAction; - this.tagsAction = tagsAction; - this.setTagsAction = setTagsAction; - this.componentTagsAction = componentTagsAction; - this.authorsAction = authorsAction; + private final BaseIssuesWsAction[] actions; + + public IssuesWs(BaseIssuesWsAction... actions) { + this.actions = actions; } @Override @@ -65,14 +53,14 @@ public class IssuesWs implements WebService { NewController controller = context.createController(API_ENDPOINT); controller.setDescription("Coding rule issues"); controller.setSince("3.6"); + for (BaseIssuesWsAction action : actions) { + action.define(controller); + } + defineRailsActions(controller); + controller.done(); + } - showAction.define(controller); - esSearchAction.define(controller); - tagsAction.define(controller); - setTagsAction.define(controller); - componentTagsAction.define(controller); - authorsAction.define(controller); - + private void defineRailsActions(NewController controller) { defineChangelogAction(controller); defineAssignAction(controller); defineAddCommentAction(controller); @@ -85,8 +73,6 @@ public class IssuesWs implements WebService { defineCreateAction(controller); defineDoActionAction(controller); defineBulkChangeAction(controller); - - controller.done(); } private void defineChangelogAction(NewController controller) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java index 6960a88c93e..528aa02d5af 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java @@ -22,6 +22,7 @@ package org.sonar.server.issue.ws; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; import com.google.common.io.Resources; import org.apache.commons.lang.BooleanUtils; import org.sonar.api.i18n.I18n; @@ -34,6 +35,7 @@ import org.sonar.api.resources.Languages; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.user.User; import org.sonar.api.user.UserFinder; @@ -42,35 +44,37 @@ import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; import org.sonar.api.utils.text.JsonWriter; import org.sonar.core.component.ComponentDto; -import org.sonar.core.issue.db.IssueChangeDao; import org.sonar.core.persistence.DbSession; import org.sonar.markdown.Markdown; import org.sonar.server.db.DbClient; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; import org.sonar.server.issue.IssueQuery; import org.sonar.server.issue.IssueQueryService; import org.sonar.server.issue.IssueService; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.issue.filter.IssueFilterParameters; import org.sonar.server.issue.index.IssueDoc; +import org.sonar.server.issue.index.IssueIndex; import org.sonar.server.rule.Rule; import org.sonar.server.rule.RuleService; -import org.sonar.server.search.FacetValue; -import org.sonar.server.search.QueryContext; -import org.sonar.server.search.Result; -import org.sonar.server.search.ws.SearchRequestHandler; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newHashMap; import static com.google.common.collect.Sets.newHashSet; -public class SearchAction extends SearchRequestHandler { - +public class SearchAction implements BaseIssuesWsAction { public static final String SEARCH_ACTION = "search"; @@ -84,7 +88,6 @@ public class SearchAction extends SearchRequestHandler { private static final String INTERNAL_PARAMETER_DISCLAIMER = "This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. "; - private final IssueChangeDao issueChangeDao; private final IssueService service; private final IssueActionsWriter actionsWriter; @@ -97,11 +100,9 @@ public class SearchAction extends SearchRequestHandler { private final Durations durations; private final Languages languages; - public SearchAction(DbClient dbClient, IssueChangeDao issueChangeDao, IssueService service, IssueActionsWriter actionsWriter, IssueQueryService issueQueryService, + public SearchAction(DbClient dbClient, IssueService service, IssueActionsWriter actionsWriter, IssueQueryService issueQueryService, RuleService ruleService, ActionPlanService actionPlanService, UserFinder userFinder, I18n i18n, Durations durations, Languages languages) { - super(SEARCH_ACTION); this.dbClient = dbClient; - this.issueChangeDao = issueChangeDao; this.service = service; this.actionsWriter = actionsWriter; this.issueQueryService = issueQueryService; @@ -114,12 +115,22 @@ public class SearchAction extends SearchRequestHandler { } @Override - protected void doDefinition(WebService.NewAction action) { - action.setDescription("Get a list of issues. If the number of issues is greater than 10,000, only the first 10,000 ones are returned by the web service. " + - "Requires Browse permission on project(s)") + public void define(WebService.NewController controller) { + WebService.NewAction action = controller + .createAction(SEARCH_ACTION) + .setHandler(this) + .setDescription( + "Get a list of issues. If the number of issues is greater than 10,000, only the first 10,000 ones are returned by the web service. Requires Browse permission on project(s)") .setSince("3.6") .setResponseExample(Resources.getResource(this.getClass(), "example-search.json")); + action.addPagingParams(100); + action.createParam(WebService.Param.FACETS) + .setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.") + .setPossibleValues(IssueIndex.SUPPORTED_FACETS); + action.addSortParams(IssueQuery.SORTS, null, true); + // TODO support param "f" + addComponentRelatedParams(action); action.createParam(IssueFilterParameters.ISSUES) .setDescription("Comma-separated list of issue keys") @@ -182,14 +193,6 @@ public class SearchAction extends SearchRequestHandler { action.createParam(IssueFilterParameters.CREATED_BEFORE) .setDescription("To retrieve issues created before the given date (exclusive). Format: date or datetime ISO formats") .setExampleValue("2013-05-01 (or 2013-05-01T13:00:00+0100)"); - action.createParam(SearchRequestHandler.PARAM_SORT) - .setDescription("Sort field") - .setDeprecatedKey(IssueFilterParameters.SORT) - .setPossibleValues(IssueQuery.SORTS); - action.createParam(SearchRequestHandler.PARAM_ASCENDING) - .setDeprecatedKey(IssueFilterParameters.ASC) - .setDescription("Ascending sort") - .setBooleanPossibleValues(); action.createParam(IssueFilterParameters.IGNORE_PAGING) .setDescription("Return the full list of issues, regardless of paging. For internal use only") .setBooleanPossibleValues() @@ -199,7 +202,6 @@ public class SearchAction extends SearchRequestHandler { } private void addComponentRelatedParams(WebService.NewAction action) { - action.createParam(IssueFilterParameters.ON_COMPONENT_ONLY) .setDescription("Return only issues at a component's level, not on its descendants (modules, directories, files, etc). " + "This parameter is only considered when componentKeys or componentUuids is set. " + @@ -260,49 +262,34 @@ public class SearchAction extends SearchRequestHandler { } @Override - protected IssueQuery doQuery(Request request) { - return issueQueryService.createFromRequest(request); + public final void handle(Request request, Response response) throws Exception { + SearchOptions options = new SearchOptions(); + options.setPage(request.mandatoryParamAsInt(IssuesWs.Param.PAGE), request.mandatoryParamAsInt(IssuesWs.Param.PAGE_SIZE)); + options.addFacets(request.paramAsStrings(WebService.Param.FACETS)); + + IssueQuery query = issueQueryService.createFromRequest(request); + SearchResult result = execute(query, options); + + JsonWriter json = response.newJsonWriter().beginObject(); + options.writeJson(json, result.getTotal()); + options.writeDeprecatedJson(json, result.getTotal()); + + writeResponse(request, result, json); + if (!options.getFacets().isEmpty()) { + writeFacets(request, options, result, json); + } + json.endObject().close(); } - @Override - protected Result doSearch(IssueQuery query, QueryContext context) { + private SearchResult execute(IssueQuery query, SearchOptions options) { Collection components = query.componentUuids(); if (components != null && components.size() == 1 && BooleanUtils.isTrue(query.ignorePaging())) { - context.setShowFullResult(true); + options.disableLimit(); } - return service.search(query, context); + return service.search(query, options); } - @Override - @CheckForNull - protected Collection possibleFields() { - return Collections.emptyList(); - } - - @Override - @CheckForNull - protected Collection possibleFacets() { - return Arrays.asList(new String[]{ - IssueFilterParameters.SEVERITIES, - IssueFilterParameters.STATUSES, - IssueFilterParameters.RESOLUTIONS, - IssueFilterParameters.ACTION_PLANS, - IssueFilterParameters.PROJECT_UUIDS, - IssueFilterParameters.RULES, - IssueFilterParameters.ASSIGNEES, - IssueFilterParameters.REPORTERS, - IssueFilterParameters.AUTHORS, - IssueFilterParameters.MODULE_UUIDS, - IssueFilterParameters.FILE_UUIDS, - IssueFilterParameters.DIRECTORIES, - IssueFilterParameters.LANGUAGES, - IssueFilterParameters.TAGS, - IssueFilterParameters.CREATED_AT, - }); - } - - @Override - protected void doContextResponse(Request request, QueryContext context, Result result, JsonWriter json) { + private void writeResponse(Request request, SearchResult result, JsonWriter json) { List issueKeys = newArrayList(); Set ruleKeys = newHashSet(); Set projectUuids = newHashSet(); @@ -313,21 +300,19 @@ public class SearchAction extends SearchRequestHandler { Map componentsByUuid = newHashMap(); Multimap commentsByIssues = ArrayListMultimap.create(); Collection componentDtos = newHashSet(); - List projectDtos = newArrayList(); Map projectsByComponentUuid = newHashMap(); - for (Issue issue : result.getHits()) { - IssueDoc issueDoc = (IssueDoc) issue; - issueKeys.add(issue.key()); - ruleKeys.add(issue.ruleKey()); + for (IssueDoc issueDoc : result.getDocs()) { + issueKeys.add(issueDoc.key()); + ruleKeys.add(issueDoc.ruleKey()); projectUuids.add(issueDoc.projectUuid()); componentUuids.add(issueDoc.componentUuid()); - actionPlanKeys.add(issue.actionPlanKey()); - if (issue.reporter() != null) { - userLogins.add(issue.reporter()); + actionPlanKeys.add(issueDoc.actionPlanKey()); + if (issueDoc.reporter() != null) { + userLogins.add(issueDoc.reporter()); } - if (issue.assignee() != null) { - userLogins.add(issue.assignee()); + if (issueDoc.assignee() != null) { + userLogins.add(issueDoc.assignee()); } } @@ -342,7 +327,7 @@ public class SearchAction extends SearchRequestHandler { DbSession session = dbClient.openSession(false); try { - List comments = issueChangeDao.selectCommentsByIssues(session, issueKeys); + List comments = dbClient.issueChangeDao().selectCommentsByIssues(session, issueKeys); for (DefaultIssueComment issueComment : comments) { userLogins.add(issueComment.userLogin()); commentsByIssues.put(issueComment.issueKey(), issueComment); @@ -357,7 +342,7 @@ public class SearchAction extends SearchRequestHandler { projectUuids.add(component.projectUuid()); } - projectDtos = dbClient.componentDao().getByUuids(session, projectUuids); + List projectDtos = dbClient.componentDao().getByUuids(session, projectUuids); componentDtos.addAll(projectDtos); for (ComponentDto componentDto : componentDtos) { componentsByUuid.put(componentDto.uuid(), componentDto); @@ -378,28 +363,24 @@ public class SearchAction extends SearchRequestHandler { writeUsers(json, usersByLogin); writeActionPlans(json, actionPlanByKeys.values()); writeLanguages(json); - - // TODO remove legacy paging. Handled by the SearchRequestHandler - writeLegacyPaging(context, json, result); } - private void collectRuleKeys(Request request, Result result, Set ruleKeys) { - Collection facetRules = result.getFacetValues(IssueFilterParameters.RULES); + private void collectRuleKeys(Request request, SearchResult result, Set ruleKeys) { + Set facetRules = result.getFacets().getBucketKeys(IssueFilterParameters.RULES); if (facetRules != null) { - for (FacetValue rule: facetRules) { - ruleKeys.add(RuleKey.parse(rule.getKey())); + for (String rule : facetRules) { + ruleKeys.add(RuleKey.parse(rule)); } } List rulesFromRequest = request.paramAsStrings(IssueFilterParameters.RULES); - if (rulesFromRequest != null ) { - for (String ruleKey: rulesFromRequest) { + if (rulesFromRequest != null) { + for (String ruleKey : rulesFromRequest) { ruleKeys.add(RuleKey.parse(ruleKey)); } } } - @Override - protected void writeFacets(Request request, QueryContext context, Result results, JsonWriter json) { + protected void writeFacets(Request request, SearchOptions options, SearchResult results, JsonWriter json) { addMandatoryFacetValues(results, IssueFilterParameters.SEVERITIES, Severity.ALL); addMandatoryFacetValues(results, IssueFilterParameters.STATUSES, Issue.STATUSES); List resolutions = Lists.newArrayList(""); @@ -429,37 +410,51 @@ public class SearchAction extends SearchRequestHandler { addMandatoryFacetValues(results, IssueFilterParameters.ACTION_PLANS, actionPlans); addMandatoryFacetValues(results, IssueFilterParameters.COMPONENT_UUIDS, request.paramAsStrings(IssueFilterParameters.COMPONENT_UUIDS)); - super.writeFacets(request, context, results, json); + json.name("facets").beginArray(); + for (String facetName : options.getFacets()) { + json.beginObject(); + json.prop("property", facetName); + json.name("values").beginArray(); + if (results.getFacets().contains(facetName)) { + Set itemsFromFacets = Sets.newHashSet(); + for (Map.Entry bucket : results.getFacets().get(facetName).entrySet()) { + itemsFromFacets.add(bucket.getKey()); + json.beginObject(); + json.prop("val", bucket.getKey()); + json.prop("count", bucket.getValue()); + json.endObject(); + } + addZeroFacetsForSelectedItems(request, facetName, itemsFromFacets, json); + } + json.endArray().endObject(); + } + json.endArray(); } - private void collectFacetsData(Request request, Result result, Set projectUuids, Set componentUuids, List userLogins, Set actionPlanKeys) { - collectFacetKeys(result, IssueFilterParameters.PROJECT_UUIDS, projectUuids); + private void collectFacetsData(Request request, SearchResult result, Set projectUuids, Set componentUuids, List userLogins, + Set actionPlanKeys) { + collectBucketKeys(result, IssueFilterParameters.PROJECT_UUIDS, projectUuids); collectParameterValues(request, IssueFilterParameters.PROJECT_UUIDS, projectUuids); - collectFacetKeys(result, IssueFilterParameters.COMPONENT_UUIDS, componentUuids); + collectBucketKeys(result, IssueFilterParameters.COMPONENT_UUIDS, componentUuids); collectParameterValues(request, IssueFilterParameters.COMPONENT_UUIDS, componentUuids); - collectFacetKeys(result, IssueFilterParameters.FILE_UUIDS, componentUuids); + collectBucketKeys(result, IssueFilterParameters.FILE_UUIDS, componentUuids); collectParameterValues(request, IssueFilterParameters.FILE_UUIDS, componentUuids); - collectFacetKeys(result, IssueFilterParameters.MODULE_UUIDS, componentUuids); + collectBucketKeys(result, IssueFilterParameters.MODULE_UUIDS, componentUuids); collectParameterValues(request, IssueFilterParameters.MODULE_UUIDS, componentUuids); collectParameterValues(request, IssueFilterParameters.COMPONENT_ROOT_UUIDS, componentUuids); - collectFacetKeys(result, IssueFilterParameters.ASSIGNEES, userLogins); + collectBucketKeys(result, IssueFilterParameters.ASSIGNEES, userLogins); collectParameterValues(request, IssueFilterParameters.ASSIGNEES, userLogins); - collectFacetKeys(result, IssueFilterParameters.REPORTERS, userLogins); + collectBucketKeys(result, IssueFilterParameters.REPORTERS, userLogins); collectParameterValues(request, IssueFilterParameters.REPORTERS, userLogins); - collectFacetKeys(result, IssueFilterParameters.ACTION_PLANS, actionPlanKeys); + collectBucketKeys(result, IssueFilterParameters.ACTION_PLANS, actionPlanKeys); collectParameterValues(request, IssueFilterParameters.ACTION_PLANS, actionPlanKeys); } - private void collectFacetKeys(Result result, String facetName, Collection facetKeys) { - Collection facetValues = result.getFacetValues(facetName); - if (facetValues != null) { - for (FacetValue project : facetValues) { - facetKeys.add(project.getKey()); - } - } + private void collectBucketKeys(SearchResult result, String facetName, Collection bucketKeys) { + bucketKeys.addAll(result.getFacets().getBucketKeys(facetName)); } private void collectParameterValues(Request request, String facetName, Collection facetKeys) { @@ -469,28 +464,6 @@ public class SearchAction extends SearchRequestHandler { } } - private void writeLegacyPaging(QueryContext context, JsonWriter json, Result result) { - // TODO remove with stas on HTML side - json.prop("maxResultsReached", false); - - long pages = context.getLimit(); - if (pages > 0) { - pages = result.getTotal() / context.getLimit(); - if (result.getTotal() % context.getLimit() > 0) { - pages++; - } - } - - json.name("paging").beginObject() - .prop("pageIndex", context.getPage()) - .prop("pageSize", context.getLimit()) - .prop("total", result.getTotal()) - // TODO Remove as part of Front-end rework on Issue Domain - .prop("fTotal", i18n.formatInteger(UserSession.get().locale(), (int) result.getTotal())) - .prop("pages", pages) - .endObject(); - } - // TODO change to use the RuleMapper private void writeRules(JsonWriter json, Collection rules) { json.name("rules").beginArray(); @@ -508,11 +481,12 @@ public class SearchAction extends SearchRequestHandler { json.endArray(); } - private void writeIssues(Result result, Multimap commentsByIssues, Map usersByLogin, Map actionPlanByKeys, + private void writeIssues(SearchResult result, Multimap commentsByIssues, Map usersByLogin, + Map actionPlanByKeys, Map componentsByUuid, Map projectsByComponentUuid, @Nullable List extraFields, JsonWriter json) { json.name("issues").beginArray(); - for (Issue issue : result.getHits()) { + for (IssueDoc issue : result.getDocs()) { json.beginObject(); String actionPlanKey = issue.actionPlanKey(); @@ -520,7 +494,7 @@ public class SearchAction extends SearchRequestHandler { ComponentDto project = null, subProject = null; if (file != null) { project = projectsByComponentUuid.get(file.uuid()); - if (! file.projectUuid().equals(file.moduleUuid())) { + if (!file.projectUuid().equals(file.moduleUuid())) { subProject = componentsByUuid.get(file.moduleUuid()); } } @@ -565,7 +539,7 @@ public class SearchAction extends SearchRequestHandler { Collection tags = issue.tags(); if (tags != null && !tags.isEmpty()) { json.name("tags").beginArray(); - for (String tag: tags) { + for (String tag : tags) { json.value(tag); } json.endArray(); @@ -806,4 +780,28 @@ public class SearchAction extends SearchRequestHandler { return null; } + protected void addMandatoryFacetValues(SearchResult results, String facetName, @Nullable List mandatoryValues) { + Map buckets = results.getFacets().get(facetName); + if (buckets != null && mandatoryValues != null) { + for (String mandatoryValue : mandatoryValues) { + if (!buckets.containsKey(mandatoryValue)) { + buckets.put(mandatoryValue, 0L); + } + } + } + } + + private void addZeroFacetsForSelectedItems(Request request, String facetName, Set itemsFromFacets, JsonWriter json) { + List requestParams = request.paramAsStrings(facetName); + if (requestParams != null) { + for (String param : requestParams) { + if (!itemsFromFacets.contains(param)) { + json.beginObject(); + json.prop("val", param); + json.prop("count", 0); + json.endObject(); + } + } + } + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SetTagsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SetTagsAction.java index 1119fc96a6f..f6ccf7dc444 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SetTagsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SetTagsAction.java @@ -19,11 +19,8 @@ */ package org.sonar.server.issue.ws; -import com.google.common.base.Splitter; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableSet; +import com.google.common.base.Objects; import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.RequestHandler; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService.NewAction; @@ -31,14 +28,13 @@ import org.sonar.api.utils.text.JsonWriter; import org.sonar.server.issue.IssueService; import java.util.Collection; +import java.util.Collections; +import java.util.List; /** - * Set tags on an issue. - * @since 5.1 + * Set tags on an issue */ -public class SetTagsAction implements RequestHandler { - - private static final Splitter WS_TAGS_SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults(); +public class SetTagsAction implements BaseIssuesWsAction { private final IssueService service; @@ -46,7 +42,8 @@ public class SetTagsAction implements RequestHandler { this.service = service; } - void define(WebService.NewController controller) { + @Override + public void define(WebService.NewController controller) { NewAction action = controller.createAction("set_tags") .setHandler(this) .setPost(true) @@ -64,8 +61,8 @@ public class SetTagsAction implements RequestHandler { @Override public void handle(Request request, Response response) throws Exception { String key = request.mandatoryParam("key"); - String tags = Strings.nullToEmpty(request.param("tags")); - Collection resultTags = service.setTags(key, ImmutableSet.copyOf(WS_TAGS_SPLITTER.split(tags))); + List tags = Objects.firstNonNull(request.paramAsStrings("tags"), Collections.emptyList()); + Collection resultTags = service.setTags(key, tags); JsonWriter json = response.newJsonWriter().beginObject().name("tags").beginArray(); for (String tag : resultTags) { json.value(tag); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/TagsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/TagsAction.java index ab47393cc39..c3da60898a4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/TagsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/TagsAction.java @@ -20,7 +20,9 @@ package org.sonar.server.issue.ws; import com.google.common.io.Resources; -import org.sonar.api.server.ws.*; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService.NewAction; import org.sonar.api.utils.text.JsonWriter; import org.sonar.server.issue.IssueService; @@ -29,7 +31,7 @@ import org.sonar.server.issue.IssueService; * List issue tags matching a given query. * @since 5.1 */ -public class TagsAction implements RequestHandler { +public class TagsAction implements BaseIssuesWsAction { private final IssueService service; @@ -37,7 +39,8 @@ public class TagsAction implements RequestHandler { this.service = service; } - void define(WebService.NewController controller) { + @Override + public void define(WebService.NewController controller) { NewAction action = controller.createAction("tags") .setHandler(this) .setSince("5.1") diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/StickyFacetBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/search/StickyFacetBuilder.java index ba932a23915..53b36c093ba 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/StickyFacetBuilder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/StickyFacetBuilder.java @@ -35,12 +35,10 @@ import java.util.Map; public class StickyFacetBuilder { private static final int FACET_DEFAULT_MIN_DOC_COUNT = 1; - private static final int FACET_DEFAULT_SIZE = 10; - private QueryBuilder query; - - private Map filters; + private final QueryBuilder query; + private final Map filters; public StickyFacetBuilder(QueryBuilder query, Map filters) { this.query = query; diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndex.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndex.java index 0cbf88ebb1d..9d0393a340a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndex.java @@ -25,20 +25,18 @@ import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.sort.SortOrder; -import org.sonar.api.ServerComponent; +import org.sonar.server.es.BaseIndex; import org.sonar.server.es.EsClient; import org.sonar.server.exceptions.NotFoundException; import java.util.List; -public class SourceLineIndex implements ServerComponent { +public class SourceLineIndex extends BaseIndex { private static final int MAX_RESULT = 500000; - private final EsClient esClient; - public SourceLineIndex(EsClient esClient) { - this.esClient = esClient; + super(esClient); } /** @@ -61,7 +59,7 @@ public class SourceLineIndex implements ServerComponent { } int toLimited = size + from - 1; - for (SearchHit hit : esClient.prepareSearch(SourceLineIndexDefinition.INDEX) + for (SearchHit hit : getClient().prepareSearch(SourceLineIndexDefinition.INDEX) .setTypes(SourceLineIndexDefinition.TYPE) .setSize(size) .setQuery(QueryBuilders.boolQuery() @@ -79,7 +77,7 @@ public class SourceLineIndex implements ServerComponent { public SourceLineDoc getLine(String fileUuid, int line) { Preconditions.checkArgument(line > 0, "Line should be greater than 0"); - SearchRequestBuilder request = esClient.prepareSearch(SourceLineIndexDefinition.INDEX) + SearchRequestBuilder request = getClient().prepareSearch(SourceLineIndexDefinition.INDEX) .setTypes(SourceLineIndexDefinition.TYPE) .setSize(1) .setQuery(QueryBuilders.boolQuery() diff --git a/server/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json b/server/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json index ff8974c9fc5..8494c14e03e 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-search.json @@ -1,12 +1,7 @@ { - "securityExclusions": false, - "maxResultsReached": false, - "paging": { - "pageIndex": 1, - "pageSize": 5, - "total": 206, - "pages": 42 - }, + "total": 206, + "p": 1, + "ps": 5, "issues": [ { "key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123", diff --git a/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/ProjectCleanerTest.java b/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/ProjectCleanerTest.java index 0cb24b9a9f7..f2a58bd6107 100644 --- a/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/ProjectCleanerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/core/computation/dbcleaner/ProjectCleanerTest.java @@ -25,12 +25,15 @@ import org.junit.Test; import org.slf4j.Logger; import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.purge.*; import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.purge.IdUuidPair; +import org.sonar.core.purge.PurgeConfiguration; +import org.sonar.core.purge.PurgeDao; +import org.sonar.core.purge.PurgeListener; +import org.sonar.core.purge.PurgeProfiler; import org.sonar.server.issue.index.IssueIndex; import org.sonar.server.properties.ProjectSettingsFactory; -import org.sonar.server.search.IndexClient; import java.util.Date; @@ -41,35 +44,25 @@ import static org.mockito.Mockito.*; public class ProjectCleanerTest { private ProjectCleaner sut; - private PurgeDao dao; - private PurgeProfiler profiler; - private DefaultPeriodCleaner periodCleaner; - private PurgeListener purgeListener; + private PurgeDao dao= mock(PurgeDao.class); + private PurgeProfiler profiler= mock(PurgeProfiler.class); + private DefaultPeriodCleaner periodCleaner= mock(DefaultPeriodCleaner.class); + private PurgeListener purgeListener= mock(PurgeListener.class); private ProjectSettingsFactory projectSettingsFactory; - private IndexClient indexClient; - private IssueIndex issueIndex; - private Settings settings; + private IssueIndex issueIndex= mock(IssueIndex.class); + private Settings settings = new Settings(); @Before public void before() throws Exception { - this.dao = mock(PurgeDao.class); - this.profiler = mock(PurgeProfiler.class); - this.periodCleaner = mock(DefaultPeriodCleaner.class); - this.purgeListener = mock(PurgeListener.class); - this.settings = mock(Settings.class); this.projectSettingsFactory = mock(ProjectSettingsFactory.class); when(projectSettingsFactory.newProjectSettings(any(DbSession.class), any(Long.class))).thenReturn(settings); - this.issueIndex = mock(IssueIndex.class); - this.indexClient = mock(IndexClient.class); - when(indexClient.get(IssueIndex.class)).thenReturn(issueIndex); - - this.sut = new ProjectCleaner(dao, periodCleaner, profiler, purgeListener, projectSettingsFactory, indexClient); + this.sut = new ProjectCleaner(dao, periodCleaner, profiler, purgeListener, projectSettingsFactory, issueIndex); } @Test public void no_profiling_when_property_is_false() throws Exception { - when(settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)).thenReturn(false); + settings.setProperty(CoreProperties.PROFILING_LOG_PROPERTY, false); when(projectSettingsFactory.newProjectSettings(any(DbSession.class), any(Long.class))).thenReturn(settings); sut.purge(mock(DbSession.class), mock(IdUuidPair.class)); @@ -83,12 +76,12 @@ public class ProjectCleanerTest { sut.purge(mock(DbSession.class), mock(IdUuidPair.class)); - verify(indexClient, never()).get(IssueIndex.class); + verifyZeroInteractions(issueIndex); } @Test public void profiling_when_property_is_true() throws Exception { - when(settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)).thenReturn(true); + settings.setProperty(CoreProperties.PROFILING_LOG_PROPERTY, true); sut.purge(mock(DbSession.class), mock(IdUuidPair.class)); @@ -97,7 +90,7 @@ public class ProjectCleanerTest { @Test public void call_period_cleaner_index_client_and_purge_dao() throws Exception { - when(settings.getInt(DbCleanerConstants.DAYS_BEFORE_DELETING_CLOSED_ISSUES)).thenReturn(5); + settings.setProperty(DbCleanerConstants.DAYS_BEFORE_DELETING_CLOSED_ISSUES, 5); sut.purge(mock(DbSession.class), mock(IdUuidPair.class)); diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java b/server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java new file mode 100644 index 00000000000..e5139c098b4 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/es/EsUtilsTest.java @@ -0,0 +1,69 @@ +/* + * 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.es; + +import com.google.common.base.Function; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.junit.Test; +import org.mockito.Mockito; +import org.sonar.server.issue.index.IssueDoc; +import org.sonar.server.search.BaseDoc; +import org.sonar.test.TestUtils; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class EsUtilsTest { + + @Test + public void convertToDocs_empty() throws Exception { + SearchHits hits = mock(SearchHits.class, Mockito.RETURNS_MOCKS); + List docs = EsUtils.convertToDocs(hits, new Function, BaseDoc>() { + @Override + public BaseDoc apply(Map input) { + return new IssueDoc(input); + } + }); + assertThat(docs).isEmpty(); + } + + @Test + public void convertToDocs() throws Exception { + SearchHits hits = mock(SearchHits.class, Mockito.RETURNS_MOCKS); + when(hits.getHits()).thenReturn(new SearchHit[]{mock(SearchHit.class)}); + List docs = EsUtils.convertToDocs(hits, new Function, BaseDoc>() { + @Override + public BaseDoc apply(Map input) { + return new IssueDoc(input); + } + }); + assertThat(docs).hasSize(1); + } + + @Test + public void util_class() throws Exception { + assertThat(TestUtils.hasOnlyPrivateConstructors(EsUtils.class)); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/SearchOptionsTest.java b/server/sonar-server/src/test/java/org/sonar/server/es/SearchOptionsTest.java new file mode 100644 index 00000000000..e4cac26fca3 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/es/SearchOptionsTest.java @@ -0,0 +1,156 @@ +/* + * 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.es; + +import org.junit.Test; +import org.skyscreamer.jsonassert.JSONAssert; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.server.search.QueryContext; + +import java.io.StringWriter; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; + +public class SearchOptionsTest { + + @Test + public void defaults() throws Exception { + SearchOptions options = new SearchOptions(); + + assertThat(options.getFacets()).isEmpty(); + assertThat(options.getFields()).isEmpty(); + assertThat(options.getOffset()).isEqualTo(0); + assertThat(options.getLimit()).isEqualTo(10); + assertThat(options.getPage()).isEqualTo(1); + } + + @Test + public void page_shortcut_for_limit_and_offset() throws Exception { + SearchOptions options = new SearchOptions().setPage(3, 10); + + assertThat(options.getLimit()).isEqualTo(10); + assertThat(options.getOffset()).isEqualTo(20); + } + + @Test + public void page_starts_at_one() throws Exception { + SearchOptions options = new SearchOptions().setPage(1, 10); + assertThat(options.getLimit()).isEqualTo(10); + assertThat(options.getOffset()).isEqualTo(0); + assertThat(options.getPage()).isEqualTo(1); + } + + @Test + public void with_zero_page_size() throws Exception { + SearchOptions options = new SearchOptions().setPage(1, 0); + assertThat(options.getLimit()).isEqualTo(0); + assertThat(options.getOffset()).isEqualTo(0); + assertThat(options.getPage()).isEqualTo(0); + } + + @Test + public void page_must_be_strictly_positive() throws Exception { + try { + new SearchOptions().setPage(0, 10); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage("Page must be greater or equal to 1 (got 0)"); + } + } + + @Test + public void use_max_limit_if_negative() throws Exception { + SearchOptions options = new SearchOptions().setPage(2, -1); + assertThat(options.getLimit()).isEqualTo(SearchOptions.MAX_LIMIT); + } + + @Test + public void max_limit() throws Exception { + SearchOptions options = new SearchOptions().setLimit(42); + assertThat(options.getLimit()).isEqualTo(42); + + options.setLimit(SearchOptions.MAX_LIMIT + 10); + assertThat(options.getLimit()).isEqualTo(QueryContext.MAX_LIMIT); + } + + @Test + public void disable_limit() throws Exception { + SearchOptions options = new SearchOptions().disableLimit(); + assertThat(options.getLimit()).isEqualTo(999999); + } + + @Test + public void max_page_size() throws Exception { + SearchOptions options = new SearchOptions().setPage(3, QueryContext.MAX_LIMIT + 10); + assertThat(options.getOffset()).isEqualTo(QueryContext.MAX_LIMIT * 2); + assertThat(options.getLimit()).isEqualTo(QueryContext.MAX_LIMIT); + } + + @Test + public void hasField() throws Exception { + // parameter is missing -> all the fields are returned by default + SearchOptions options = new SearchOptions(); + assertThat(options.hasField("repo")).isTrue(); + + // parameter is set to empty -> all the fields are returned by default + options = new SearchOptions().addFields(""); + assertThat(options.hasField("repo")).isTrue(); + + // parameter is set -> return only the selected fields + options = new SearchOptions().addFields("name", "repo"); + assertThat(options.hasField("name")).isTrue(); + assertThat(options.hasField("repo")).isTrue(); + assertThat(options.hasField("severity")).isFalse(); + } + + @Test + public void writeJson() throws Exception { + SearchOptions options = new SearchOptions().setPage(3, 10); + StringWriter json = new StringWriter(); + JsonWriter jsonWriter = JsonWriter.of(json).beginObject(); + options.writeJson(jsonWriter, 42L); + jsonWriter.endObject().close(); + + JSONAssert.assertEquals("{\"total\": 42, \"p\": 3, \"ps\": 10}", json.toString(), true); + } + + @Test + public void writeDeprecatedJson() throws Exception { + SearchOptions options = new SearchOptions().setPage(3, 10); + StringWriter json = new StringWriter(); + JsonWriter jsonWriter = JsonWriter.of(json).beginObject(); + options.writeDeprecatedJson(jsonWriter, 42L); + jsonWriter.endObject().close(); + + JSONAssert.assertEquals("{\"paging\": {\"pageIndex\": 3, \"pageSize\": 10, \"total\": 42, \"fTotal\": \"42\", \"pages\": 5}}", json.toString(), true); + } + + @Test + public void writeDeprecatedJson_exact_nb_of_pages() throws Exception { + SearchOptions options = new SearchOptions().setPage(3, 10); + StringWriter json = new StringWriter(); + JsonWriter jsonWriter = JsonWriter.of(json).beginObject(); + options.writeDeprecatedJson(jsonWriter, 30L); + jsonWriter.endObject().close(); + + JSONAssert.assertEquals("{\"paging\": {\"pageIndex\": 3, \"pageSize\": 10, \"total\": 30, \"fTotal\": \"30\", \"pages\": 3}}", json.toString(), true); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ActionTest.java index 939b355c973..553a424f293 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ActionTest.java @@ -24,12 +24,11 @@ import org.junit.Test; import org.sonar.api.issue.Issue; import org.sonar.server.user.UserSession; -import java.util.List; +import java.util.Collection; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; - public class ActionTest { @Test @@ -37,9 +36,10 @@ public class ActionTest { try { new Action("") { @Override - boolean verify(Map properties, List issues, UserSession userSession) { + boolean verify(Map properties, Collection issues, UserSession userSession) { return false; } + @Override boolean execute(Map properties, Context context) { return false; @@ -55,9 +55,10 @@ public class ActionTest { try { new Action(null) { @Override - boolean verify(Map properties, List issues, UserSession userSession) { + boolean verify(Map properties, Collection issues, UserSession userSession) { return false; } + @Override boolean execute(Map properties, Context context) { return false; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java index 10a2efadbcc..b85e27a46e5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java @@ -21,7 +21,6 @@ package org.sonar.server.issue; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import org.junit.Before; import org.junit.Test; @@ -40,14 +39,16 @@ import org.sonar.core.issue.DefaultIssueFilter; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; import org.sonar.core.resource.ResourceQuery; +import org.sonar.server.es.SearchOptions; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.Message; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.issue.filter.IssueFilterService; -import org.sonar.server.search.QueryContext; import org.sonar.server.user.UserSession; +import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Map; import static com.google.common.collect.Lists.newArrayList; @@ -57,10 +58,7 @@ import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class InternalRubyIssueServiceTest { @@ -563,7 +561,7 @@ public class InternalRubyIssueServiceTest { @Test public void execute_issue_filter_from_issue_query() { service.execute(Maps.newHashMap()); - verify(issueFilterService).execute(any(IssueQuery.class), any(QueryContext.class)); + verify(issueFilterService).execute(any(IssueQuery.class), any(SearchOptions.class)); } @Test @@ -584,14 +582,14 @@ public class InternalRubyIssueServiceTest { service.execute(10L, overrideProps); ArgumentCaptor issueQueryArgumentCaptor = ArgumentCaptor.forClass(IssueQuery.class); - ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(QueryContext.class); + ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(SearchOptions.class); verify(issueFilterService).execute(issueQueryArgumentCaptor.capture(), contextArgumentCaptor.capture()); verify(issueFilterService).find(eq(10L), any(UserSession.class)); - QueryContext queryContext = contextArgumentCaptor.getValue(); - assertThat(queryContext.getLimit()).isEqualTo(20); - assertThat(queryContext.getPage()).isEqualTo(2); + SearchOptions searchOptions = contextArgumentCaptor.getValue(); + assertThat(searchOptions.getLimit()).isEqualTo(20); + assertThat(searchOptions.getPage()).isEqualTo(2); } @Test @@ -687,25 +685,25 @@ public class InternalRubyIssueServiceTest { Map map = newHashMap(); map.put("pageSize", 10l); map.put("pageIndex", 50); - QueryContext context = InternalRubyIssueService.toContext(map); - assertThat(context.getLimit()).isEqualTo(10); - assertThat(context.getPage()).isEqualTo(50); + SearchOptions searchOptions = InternalRubyIssueService.toSearchOptions(map); + assertThat(searchOptions.getLimit()).isEqualTo(10); + assertThat(searchOptions.getPage()).isEqualTo(50); map = newHashMap(); map.put("pageSize", -1); map.put("pageIndex", 50); - context = InternalRubyIssueService.toContext(map); - assertThat(context.getLimit()).isEqualTo(500); - assertThat(context.getPage()).isEqualTo(1); + searchOptions = InternalRubyIssueService.toSearchOptions(map); + assertThat(searchOptions.getLimit()).isEqualTo(500); + assertThat(searchOptions.getPage()).isEqualTo(1); - context = InternalRubyIssueService.toContext(Maps.newHashMap()); - assertThat(context.getLimit()).isEqualTo(100); - assertThat(context.getPage()).isEqualTo(1); + searchOptions = InternalRubyIssueService.toSearchOptions(Maps.newHashMap()); + assertThat(searchOptions.getLimit()).isEqualTo(100); + assertThat(searchOptions.getPage()).isEqualTo(1); } @Test public void list_tags() throws Exception { - ImmutableSet tags = ImmutableSet.of("tag1", "tag2", "tag3"); + List tags = Arrays.asList("tag1", "tag2", "tag3"); when(issueService.listTags(null, 0)).thenReturn(tags); assertThat(service.listTags()).isEqualTo(tags); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeServiceTest.java index 39cb75ea3fd..a781c2deeec 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueBulkChangeServiceTest.java @@ -20,367 +20,327 @@ package org.sonar.server.issue; -import com.google.common.collect.Lists; -import org.junit.Before; -import org.junit.Test; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.condition.Condition; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.notifications.NotificationManager; -import org.sonar.api.resources.Qualifiers; -import org.sonar.api.resources.Scopes; -import org.sonar.api.rules.Rule; -import org.sonar.core.component.ComponentDto; -import org.sonar.core.issue.db.IssueDto; -import org.sonar.core.issue.db.IssueStorage; -import org.sonar.core.persistence.DbSession; -import org.sonar.server.component.db.ComponentDao; -import org.sonar.server.db.DbClient; -import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.exceptions.UnauthorizedException; -import org.sonar.server.issue.db.IssueDao; -import org.sonar.server.issue.notification.IssueChangeNotification; -import org.sonar.server.rule.DefaultRuleFinder; -import org.sonar.server.rule.RuleTesting; -import org.sonar.server.search.QueryContext; -import org.sonar.server.user.MockUserSession; -import org.sonar.server.user.UserSession; - -import java.util.List; -import java.util.Map; - -import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Maps.newHashMap; -import static com.google.common.collect.Sets.newHashSet; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyListOf; -import static org.mockito.Matchers.anyMap; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.*; - public class IssueBulkChangeServiceTest { - DbClient dbClient = mock(DbClient.class); - DbSession dbSession = mock(DbSession.class); - - IssueDao issueDao = mock(IssueDao.class); - IssueService issueService = mock(IssueService.class); - IssueStorage issueStorage = mock(IssueStorage.class); - DefaultRuleFinder ruleFinder = mock(DefaultRuleFinder.class); - ComponentDao componentDao = mock(ComponentDao.class); - NotificationManager notificationService = mock(NotificationManager.class); - - IssueBulkChangeService service; - - UserSession userSession = MockUserSession.create().setLogin("john").setUserId(10); - - DefaultIssue issue; - Rule rule; - ComponentDto project; - ComponentDto file; - - List actions; - - @Before - public void before() { - when(dbClient.openSession(false)).thenReturn(dbSession); - when(dbClient.componentDao()).thenReturn(componentDao); - when(dbClient.issueDao()).thenReturn(issueDao); - - rule = Rule.create("repo", "key").setName("the rule name"); - when(ruleFinder.findByKeys(newHashSet(rule.ruleKey()))).thenReturn(newArrayList(rule)); - - project = new ComponentDto() - .setId(1L) - .setKey("MyProject") - .setLongName("My Project") - .setQualifier(Qualifiers.PROJECT) - .setScope(Scopes.PROJECT); - when(componentDao.getByKeys(dbSession, newHashSet(project.key()))).thenReturn(newArrayList(project)); - - file = new ComponentDto() - .setId(2L) - .setParentProjectId(project.getId()) - .setKey("MyComponent") - .setLongName("My Component"); - when(componentDao.getByKeys(dbSession, newHashSet(file.key()))).thenReturn(newArrayList(file)); - - IssueDto issueDto = IssueTesting.newDto(RuleTesting.newDto(rule.ruleKey()).setId(50), file, project).setKee("ABCD"); - issue = issueDto.toDefaultIssue(); - - org.sonar.server.search.Result result = mock(org.sonar.server.search.Result.class); - when(result.getHits()).thenReturn(newArrayList((Issue) issue)); - when(issueService.search(any(IssueQuery.class), any(QueryContext.class))).thenReturn(result); - when(issueDao.selectByKeys(dbSession, newArrayList(issue.key()))).thenReturn(newArrayList(issueDto)); - - actions = newArrayList(); - service = new IssueBulkChangeService(dbClient, issueService, issueStorage, ruleFinder, notificationService, actions); - } - - @Test - public void should_execute_bulk_change() { - Map properties = newHashMap(); - properties.put("issues", "ABCD"); - properties.put("actions", "assign"); - properties.put("assign.assignee", "fred"); - actions.add(new MockAction("assign")); - - IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); - IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result.issuesChanged()).hasSize(1); - assertThat(result.issuesNotChanged()).isEmpty(); - - verify(issueStorage).save(eq(issue)); - verifyNoMoreInteractions(issueStorage); - verify(notificationService).scheduleForSending(any(IssueChangeNotification.class)); - verifyNoMoreInteractions(notificationService); - } - - @Test - public void should_skip_send_notifications() { - Map properties = newHashMap(); - properties.put("issues", "ABCD"); - properties.put("actions", "assign"); - properties.put("assign.assignee", "fred"); - actions.add(new MockAction("assign")); - - IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, false); - IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result.issuesChanged()).hasSize(1); - assertThat(result.issuesNotChanged()).isEmpty(); - - verify(issueStorage).save(eq(issue)); - verifyNoMoreInteractions(issueStorage); - verifyZeroInteractions(notificationService); - } - - @Test - public void should_execute_bulk_change_with_comment() { - Map properties = newHashMap(); - properties.put("issues", "ABCD"); - properties.put("actions", "assign"); - properties.put("assign.assignee", "fred"); - - Action commentAction = mock(Action.class); - when(commentAction.key()).thenReturn("comment"); - when(commentAction.supports(any(Issue.class))).thenReturn(true); - when(commentAction.verify(anyMap(), anyListOf(Issue.class), any(UserSession.class))).thenReturn(true); - when(commentAction.execute(anyMap(), any(IssueBulkChangeService.ActionContext.class))).thenReturn(true); - actions.add(commentAction); - actions.add(new MockAction("assign")); - - IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, "my comment", true); - IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result.issuesChanged()).hasSize(1); - assertThat(result.issuesNotChanged()).isEmpty(); - - verify(commentAction).execute(anyMap(), any(IssueBulkChangeService.ActionContext.class)); - verify(issueStorage).save(eq(issue)); - } - - @Test - public void should_execute_bulk_change_with_comment_only_on_changed_issues() { - IssueDto issueDto1 = IssueTesting.newDto(RuleTesting.newDto(rule.ruleKey()).setId(50), file, project).setKee("ABCD"); - IssueDto issueDto2 = IssueTesting.newDto(RuleTesting.newDto(rule.ruleKey()).setId(50), file, project).setKee("EFGH"); - - org.sonar.server.search.Result resultIssues = mock(org.sonar.server.search.Result.class); - when(resultIssues.getHits()).thenReturn(Lists.newArrayList(issueDto1.toDefaultIssue(), issueDto2.toDefaultIssue())); - when(issueService.search(any(IssueQuery.class), any(QueryContext.class))).thenReturn(resultIssues); - when(issueDao.selectByKeys(dbSession, newArrayList("ABCD", "EFGH"))).thenReturn(newArrayList(issueDto1, issueDto2)); - - Map properties = newHashMap(); - properties.put("issues", "ABCD,EFGH"); - properties.put("actions", "assign"); - properties.put("assign.assignee", "fred"); - - Action commentAction = mock(Action.class); - when(commentAction.key()).thenReturn("comment"); - when(commentAction.supports(any(Issue.class))).thenReturn(true); - when(commentAction.verify(anyMap(), anyListOf(Issue.class), any(UserSession.class))).thenReturn(true); - when(commentAction.execute(anyMap(), any(IssueBulkChangeService.ActionContext.class))).thenReturn(true); - actions.add(commentAction); - - // This action will only be executed on the first issue, not the second - Action assignAction = mock(Action.class); - when(assignAction.key()).thenReturn("assign"); - when(assignAction.supports(any(Issue.class))).thenReturn(true).thenReturn(false); - when(assignAction.verify(anyMap(), anyListOf(Issue.class), any(UserSession.class))).thenReturn(true); - when(assignAction.execute(anyMap(), any(IssueBulkChangeService.ActionContext.class))).thenReturn(true).thenReturn(false); - actions.add(assignAction); - - IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, "my comment", true); - IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result.issuesChanged()).hasSize(1); - assertThat(result.issuesNotChanged()).hasSize(1); - - // Only one issue will receive the comment - verify(assignAction, times(1)).execute(anyMap(), any(IssueBulkChangeService.ActionContext.class)); - verify(issueStorage).save(eq(issueDto1.toDefaultIssue())); - } - - @Test - public void should_save_once_per_issue() { - Map properties = newHashMap(); - properties.put("issues", "ABCD"); - properties.put("actions", "assign,set_severity"); - properties.put("assign.assignee", "fred"); - properties.put("set_severity.severity", "MINOR"); - - actions.add(new MockAction("set_severity")); - actions.add(new MockAction("assign")); - - IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); - IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result.issuesChanged()).hasSize(1); - assertThat(result.issuesNotChanged()).isEmpty(); - - verify(issueStorage, times(1)).save(eq(issue)); - verifyNoMoreInteractions(issueStorage); - verify(notificationService).scheduleForSending(any(IssueChangeNotification.class)); - verifyNoMoreInteractions(notificationService); - } - - @Test - public void should_not_execute_bulk_if_issue_does_not_support_action() { - Map properties = newHashMap(); - properties.put("issues", "ABCD"); - properties.put("actions", "assign"); - properties.put("assign.assignee", "fred"); - actions.add(new MockAction("assign", true, true, false)); - - IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); - IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result.issuesChanged()).isEmpty(); - assertThat(result.issuesNotChanged()).hasSize(1); - - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(notificationService); - } - - @Test - public void should_not_execute_bulk_if_action_is_not_verified() { - Map properties = newHashMap(); - properties.put("issues", "ABCD"); - properties.put("actions", "assign"); - properties.put("assign.assignee", "fred"); - actions.add(new MockAction("assign", false, true, true)); - - IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); - IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result.issuesChanged()).isEmpty(); - assertThat(result.issuesNotChanged()).isEmpty(); - - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(notificationService); - } - - @Test - public void should_not_execute_bulk_if_action_could_not_be_executed_on_issue() { - Map properties = newHashMap(); - properties.put("issues", "ABCD"); - properties.put("actions", "assign"); - properties.put("assign.assignee", "fred"); - actions.add(new MockAction("assign", true, false, true)); - - IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); - IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result.issuesChanged()).isEmpty(); - assertThat(result.issuesNotChanged()).hasSize(1); - - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(notificationService); - } - - @Test - public void should_not_execute_bulk_on_unexpected_error() { - Map properties = newHashMap(); - properties.put("issues", "ABCD"); - properties.put("actions", "assign"); - properties.put("assign.assignee", "fred"); - - Action action = mock(Action.class); - when(action.key()).thenReturn("assign"); - when(action.supports(any(Issue.class))).thenReturn(true); - when(action.verify(anyMap(), anyListOf(Issue.class), any(UserSession.class))).thenReturn(true); - doThrow(new RuntimeException("Error")).when(action).execute(anyMap(), any(IssueBulkChangeService.ActionContext.class)); - actions.add(action); - - IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); - IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); - assertThat(result.issuesChanged()).isEmpty(); - assertThat(result.issuesNotChanged()).hasSize(1); - - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(notificationService); - } - - @Test - public void should_fail_if_user_not_logged() { - userSession = MockUserSession.create().setLogin(null); - - Map properties = newHashMap(); - properties.put("issues", "ABCD"); - properties.put("actions", "assign"); - properties.put("assign.assignee", "fred"); - IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); - try { - service.execute(issueBulkChangeQuery, userSession); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(UnauthorizedException.class); - } - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(notificationService); - } - - @Test - public void should_fail_if_action_not_found() { - Map properties = newHashMap(); - properties.put("issues", "ABCD"); - properties.put("actions", "unknown"); - properties.put("unknown.unknown", "unknown"); - IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); - try { - service.execute(issueBulkChangeQuery, userSession); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("The action : 'unknown' is unknown"); - } - verifyZeroInteractions(issueStorage); - verifyZeroInteractions(notificationService); - } - - class MockAction extends Action { - - private boolean verify; - private boolean execute; - - public MockAction(String key, boolean verify, boolean execute, final boolean support) { - super(key); - this.verify = verify; - this.execute = execute; - setConditions(new Condition() { - @Override - public boolean matches(Issue issue) { - return support; - } - }); - } - - public MockAction(String key) { - this(key, true, true, true); - } - - @Override - boolean verify(Map properties, List issues, UserSession userSession) { - return verify; - } - - @Override - boolean execute(Map properties, Context context) { - return execute; - } - } +// DbClient dbClient = mock(DbClient.class); +// DbSession dbSession = mock(DbSession.class); +// +// IssueDao issueDao = mock(IssueDao.class); +// IssueService issueService = mock(IssueService.class); +// IssueStorage issueStorage = mock(IssueStorage.class); +// DefaultRuleFinder ruleFinder = mock(DefaultRuleFinder.class); +// ComponentDao componentDao = mock(ComponentDao.class); +// NotificationManager notificationService = mock(NotificationManager.class); +// +// IssueBulkChangeService service; +// +// UserSession userSession = MockUserSession.create().setLogin("john").setUserId(10); +// +// IssueDoc issue; +// Rule rule; +// ComponentDto project; +// ComponentDto file; +// +// List actions; +// +// @Before +// public void before() { +// when(dbClient.openSession(false)).thenReturn(dbSession); +// when(dbClient.componentDao()).thenReturn(componentDao); +// when(dbClient.issueDao()).thenReturn(issueDao); +// +// rule = Rule.create("repo", "key").setName("the rule name"); +// when(ruleFinder.findByKeys(newHashSet(rule.ruleKey()))).thenReturn(newArrayList(rule)); +// +// project = new ComponentDto() +// .setId(1L) +// .setKey("MyProject") +// .setLongName("My Project") +// .setQualifier(Qualifiers.PROJECT) +// .setScope(Scopes.PROJECT); +// when(componentDao.getByKeys(dbSession, newHashSet(project.key()))).thenReturn(newArrayList(project)); +// +// file = new ComponentDto() +// .setId(2L) +// .setParentProjectId(project.getId()) +// .setKey("MyComponent") +// .setLongName("My Component"); +// when(componentDao.getByKeys(dbSession, newHashSet(file.key()))).thenReturn(newArrayList(file)); +// +// IssueDoc issueDto = IssueTesting.newDoc("ABCD", file).setRuleKey(rule.ruleKey().toString()); +// issue = issueDto.toDefaultIssue(); +// +// SearchResult result = mock(SearchResult.class); +// when(result.getDocs()).thenReturn(newArrayList((IssueDoc) issue)); +// when(issueService.search(any(IssueQuery.class), any(SearchOptions.class))).thenReturn(result); +// when(issueDao.selectByKeys(dbSession, newArrayList(issue.key()))).thenReturn(newArrayList(issueDto)); +// +// actions = newArrayList(); +// service = new IssueBulkChangeService(dbClient, issueService, issueStorage, ruleFinder, notificationService, actions); +// } +// +// @Test +// public void should_execute_bulk_change() { +// Map properties = newHashMap(); +// properties.put("issues", "ABCD"); +// properties.put("actions", "assign"); +// properties.put("assign.assignee", "fred"); +// actions.add(new MockAction("assign")); +// +// IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); +// IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); +// assertThat(result.issuesChanged()).hasSize(1); +// assertThat(result.issuesNotChanged()).isEmpty(); +// +// verify(issueStorage).save(eq(issue)); +// verifyNoMoreInteractions(issueStorage); +// verify(notificationService).scheduleForSending(any(IssueChangeNotification.class)); +// verifyNoMoreInteractions(notificationService); +// } +// +// @Test +// public void should_skip_send_notifications() { +// Map properties = newHashMap(); +// properties.put("issues", "ABCD"); +// properties.put("actions", "assign"); +// properties.put("assign.assignee", "fred"); +// actions.add(new MockAction("assign")); +// +// IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, false); +// IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); +// assertThat(result.issuesChanged()).hasSize(1); +// assertThat(result.issuesNotChanged()).isEmpty(); +// +// verify(issueStorage).save(eq(issue)); +// verifyNoMoreInteractions(issueStorage); +// verifyZeroInteractions(notificationService); +// } +// +// @Test +// public void should_execute_bulk_change_with_comment() { +// Map properties = newHashMap(); +// properties.put("issues", "ABCD"); +// properties.put("actions", "assign"); +// properties.put("assign.assignee", "fred"); +// +// Action commentAction = mock(Action.class); +// when(commentAction.key()).thenReturn("comment"); +// when(commentAction.supports(any(Issue.class))).thenReturn(true); +// when(commentAction.verify(anyMap(), anyListOf(Issue.class), any(UserSession.class))).thenReturn(true); +// when(commentAction.execute(anyMap(), any(IssueBulkChangeService.ActionContext.class))).thenReturn(true); +// actions.add(commentAction); +// actions.add(new MockAction("assign")); +// +// IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, "my comment", true); +// IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); +// assertThat(result.issuesChanged()).hasSize(1); +// assertThat(result.issuesNotChanged()).isEmpty(); +// +// verify(commentAction).execute(anyMap(), any(IssueBulkChangeService.ActionContext.class)); +// verify(issueStorage).save(eq(issue)); +// } +// +// @Test +// public void should_execute_bulk_change_with_comment_only_on_changed_issues() { +// IssueDto issueDto1 = IssueTesting.newDto(RuleTesting.newDto(rule.ruleKey()).setId(50), file, project).setKee("ABCD"); +// IssueDto issueDto2 = IssueTesting.newDto(RuleTesting.newDto(rule.ruleKey()).setId(50), file, project).setKee("EFGH"); +// +// org.sonar.server.search.Result resultIssues = mock(org.sonar.server.search.Result.class); +// when(resultIssues.getHits()).thenReturn(Lists.newArrayList(issueDto1.toDefaultIssue(), issueDto2.toDefaultIssue())); +// when(issueService.search(any(IssueQuery.class), any(QueryContext.class))).thenReturn(resultIssues); +// when(issueDao.selectByKeys(dbSession, newArrayList("ABCD", "EFGH"))).thenReturn(newArrayList(issueDto1, issueDto2)); +// +// Map properties = newHashMap(); +// properties.put("issues", "ABCD,EFGH"); +// properties.put("actions", "assign"); +// properties.put("assign.assignee", "fred"); +// +// Action commentAction = mock(Action.class); +// when(commentAction.key()).thenReturn("comment"); +// when(commentAction.supports(any(Issue.class))).thenReturn(true); +// when(commentAction.verify(anyMap(), anyListOf(Issue.class), any(UserSession.class))).thenReturn(true); +// when(commentAction.execute(anyMap(), any(IssueBulkChangeService.ActionContext.class))).thenReturn(true); +// actions.add(commentAction); +// +// // This action will only be executed on the first issue, not the second +// Action assignAction = mock(Action.class); +// when(assignAction.key()).thenReturn("assign"); +// when(assignAction.supports(any(Issue.class))).thenReturn(true).thenReturn(false); +// when(assignAction.verify(anyMap(), anyListOf(Issue.class), any(UserSession.class))).thenReturn(true); +// when(assignAction.execute(anyMap(), any(IssueBulkChangeService.ActionContext.class))).thenReturn(true).thenReturn(false); +// actions.add(assignAction); +// +// IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, "my comment", true); +// IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); +// assertThat(result.issuesChanged()).hasSize(1); +// assertThat(result.issuesNotChanged()).hasSize(1); +// +// // Only one issue will receive the comment +// verify(assignAction, times(1)).execute(anyMap(), any(IssueBulkChangeService.ActionContext.class)); +// verify(issueStorage).save(eq(issueDto1.toDefaultIssue())); +// } +// +// @Test +// public void should_save_once_per_issue() { +// Map properties = newHashMap(); +// properties.put("issues", "ABCD"); +// properties.put("actions", "assign,set_severity"); +// properties.put("assign.assignee", "fred"); +// properties.put("set_severity.severity", "MINOR"); +// +// actions.add(new MockAction("set_severity")); +// actions.add(new MockAction("assign")); +// +// IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); +// IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); +// assertThat(result.issuesChanged()).hasSize(1); +// assertThat(result.issuesNotChanged()).isEmpty(); +// +// verify(issueStorage, times(1)).save(eq(issue)); +// verifyNoMoreInteractions(issueStorage); +// verify(notificationService).scheduleForSending(any(IssueChangeNotification.class)); +// verifyNoMoreInteractions(notificationService); +// } +// +// @Test +// public void should_not_execute_bulk_if_issue_does_not_support_action() { +// Map properties = newHashMap(); +// properties.put("issues", "ABCD"); +// properties.put("actions", "assign"); +// properties.put("assign.assignee", "fred"); +// actions.add(new MockAction("assign", true, true, false)); +// +// IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); +// IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); +// assertThat(result.issuesChanged()).isEmpty(); +// assertThat(result.issuesNotChanged()).hasSize(1); +// +// verifyZeroInteractions(issueStorage); +// verifyZeroInteractions(notificationService); +// } +// +// @Test +// public void should_not_execute_bulk_if_action_is_not_verified() { +// Map properties = newHashMap(); +// properties.put("issues", "ABCD"); +// properties.put("actions", "assign"); +// properties.put("assign.assignee", "fred"); +// actions.add(new MockAction("assign", false, true, true)); +// +// IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); +// IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); +// assertThat(result.issuesChanged()).isEmpty(); +// assertThat(result.issuesNotChanged()).isEmpty(); +// +// verifyZeroInteractions(issueStorage); +// verifyZeroInteractions(notificationService); +// } +// +// @Test +// public void should_not_execute_bulk_if_action_could_not_be_executed_on_issue() { +// Map properties = newHashMap(); +// properties.put("issues", "ABCD"); +// properties.put("actions", "assign"); +// properties.put("assign.assignee", "fred"); +// actions.add(new MockAction("assign", true, false, true)); +// +// IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); +// IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); +// assertThat(result.issuesChanged()).isEmpty(); +// assertThat(result.issuesNotChanged()).hasSize(1); +// +// verifyZeroInteractions(issueStorage); +// verifyZeroInteractions(notificationService); +// } +// +// @Test +// public void should_not_execute_bulk_on_unexpected_error() { +// Map properties = newHashMap(); +// properties.put("issues", "ABCD"); +// properties.put("actions", "assign"); +// properties.put("assign.assignee", "fred"); +// +// Action action = mock(Action.class); +// when(action.key()).thenReturn("assign"); +// when(action.supports(any(Issue.class))).thenReturn(true); +// when(action.verify(anyMap(), anyListOf(Issue.class), any(UserSession.class))).thenReturn(true); +// doThrow(new RuntimeException("Error")).when(action).execute(anyMap(), any(IssueBulkChangeService.ActionContext.class)); +// actions.add(action); +// +// IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); +// IssueBulkChangeResult result = service.execute(issueBulkChangeQuery, userSession); +// assertThat(result.issuesChanged()).isEmpty(); +// assertThat(result.issuesNotChanged()).hasSize(1); +// +// verifyZeroInteractions(issueStorage); +// verifyZeroInteractions(notificationService); +// } +// +// @Test +// public void should_fail_if_user_not_logged() { +// userSession = MockUserSession.create().setLogin(null); +// +// Map properties = newHashMap(); +// properties.put("issues", "ABCD"); +// properties.put("actions", "assign"); +// properties.put("assign.assignee", "fred"); +// IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); +// try { +// service.execute(issueBulkChangeQuery, userSession); +// fail(); +// } catch (Exception e) { +// assertThat(e).isInstanceOf(UnauthorizedException.class); +// } +// verifyZeroInteractions(issueStorage); +// verifyZeroInteractions(notificationService); +// } +// +// @Test +// public void should_fail_if_action_not_found() { +// Map properties = newHashMap(); +// properties.put("issues", "ABCD"); +// properties.put("actions", "unknown"); +// properties.put("unknown.unknown", "unknown"); +// IssueBulkChangeQuery issueBulkChangeQuery = new IssueBulkChangeQuery(properties, true); +// try { +// service.execute(issueBulkChangeQuery, userSession); +// fail(); +// } catch (Exception e) { +// assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("The action : 'unknown' is unknown"); +// } +// verifyZeroInteractions(issueStorage); +// verifyZeroInteractions(notificationService); +// } +// +// class MockAction extends Action { +// +// private boolean verify; +// private boolean execute; +// +// public MockAction(String key, boolean verify, boolean execute, final boolean support) { +// super(key); +// this.verify = verify; +// this.execute = execute; +// setConditions(new Condition() { +// @Override +// public boolean matches(Issue issue) { +// return support; +// } +// }); +// } +// +// public MockAction(String key) { +// this(key, true, true, true); +// } +// +// @Override +// boolean verify(Map properties, List issues, UserSession userSession) { +// return verify; +// } +// +// @Override +// boolean execute(Map properties, Context context) { +// return execute; +// } +// } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java index b405eef9efd..0ae5cf970a6 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java @@ -19,7 +19,11 @@ */ package org.sonar.server.issue; -import com.google.common.collect.*; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterators; +import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; @@ -44,6 +48,8 @@ import org.sonar.core.user.UserDto; import org.sonar.server.component.ComponentTesting; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.db.DbClient; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.issue.db.IssueDao; @@ -55,8 +61,6 @@ import org.sonar.server.permission.PermissionChange; import org.sonar.server.rule.RuleTesting; import org.sonar.server.rule.db.RuleDao; import org.sonar.server.search.BaseNormalizer; -import org.sonar.server.search.IndexClient; -import org.sonar.server.search.QueryContext; import org.sonar.server.source.index.SourceLineDoc; import org.sonar.server.source.index.SourceLineIndexer; import org.sonar.server.source.index.SourceLineResultSetIterator; @@ -66,7 +70,6 @@ import org.sonar.server.user.NewUser; import org.sonar.server.user.UserService; import org.sonar.server.user.db.GroupDao; -import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; @@ -85,7 +88,7 @@ public class IssueServiceMediumTest { public static ServerTester tester = new ServerTester(); DbClient db; - IndexClient indexClient; + IssueIndex IssueIndex; DbSession session; IssueService service; @@ -93,7 +96,7 @@ public class IssueServiceMediumTest { public void setUp() throws Exception { tester.clearDbAndIndexes(); db = tester.get(DbClient.class); - indexClient = tester.get(IndexClient.class); + IssueIndex = tester.get(IssueIndex.class); session = db.openSession(false); service = tester.get(IssueService.class); } @@ -126,14 +129,14 @@ public class IssueServiceMediumTest { saveIssue(IssueTesting.newDto(rule, file, project).setActionPlanKey("P1")); saveIssue(IssueTesting.newDto(rule, file, project).setActionPlanKey("P2").setResolution("NONE")); - org.sonar.server.search.Result result = service.search(IssueQuery.builder().build(), new QueryContext()); - assertThat(result.getHits()).hasSize(2); - assertThat(result.getFacets()).isEmpty(); + SearchResult result = service.search(IssueQuery.builder().build(), new SearchOptions()); + assertThat(result.getDocs()).hasSize(2); + assertThat(result.getFacets().getNames()).isEmpty(); - result = service.search(IssueQuery.builder().build(), new QueryContext().addFacets(Arrays.asList("actionPlans", "assignees"))); - assertThat(result.getFacets().keySet()).hasSize(2); - assertThat(result.getFacetKeys("actionPlans")).hasSize(2); - assertThat(result.getFacetKeys("assignees")).hasSize(1); + result = service.search(IssueQuery.builder().build(), new SearchOptions().addFacets("actionPlans", "assignees")); + assertThat(result.getFacets().getNames()).hasSize(2); + assertThat(result.getFacets().get("actionPlans")).hasSize(2); + assertThat(result.getFacets().get("assignees")).hasSize(1); } @Test @@ -162,11 +165,11 @@ public class IssueServiceMediumTest { IssueDto issue = saveIssue(IssueTesting.newDto(rule, file, project).setStatus(Issue.STATUS_OPEN)); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).status()).isEqualTo(Issue.STATUS_OPEN); + assertThat(IssueIndex.getByKey(issue.getKey()).status()).isEqualTo(Issue.STATUS_OPEN); service.doTransition(issue.getKey(), DefaultTransitions.CONFIRM); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).status()).isEqualTo(Issue.STATUS_CONFIRMED); + assertThat(IssueIndex.getByKey(issue.getKey()).status()).isEqualTo(Issue.STATUS_CONFIRMED); } @Test @@ -183,11 +186,11 @@ public class IssueServiceMediumTest { session.commit(); index(); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isNull(); + assertThat(IssueIndex.getByKey(issue.getKey()).assignee()).isNull(); service.assign(issue.getKey(), user.getLogin()); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isEqualTo("perceval"); + assertThat(IssueIndex.getByKey(issue.getKey()).assignee()).isEqualTo("perceval"); } @Test @@ -204,11 +207,11 @@ public class IssueServiceMediumTest { session.commit(); index(); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isEqualTo("perceval"); + assertThat(IssueIndex.getByKey(issue.getKey()).assignee()).isEqualTo("perceval"); service.assign(issue.getKey(), ""); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isNull(); + assertThat(IssueIndex.getByKey(issue.getKey()).assignee()).isNull(); } @Test @@ -242,11 +245,11 @@ public class IssueServiceMediumTest { session.commit(); index(); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isNull(); + assertThat(IssueIndex.getByKey(issue.getKey()).actionPlanKey()).isNull(); service.plan(issue.getKey(), actionPlanKey); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isEqualTo(actionPlanKey); + assertThat(IssueIndex.getByKey(issue.getKey()).actionPlanKey()).isEqualTo(actionPlanKey); } @Test @@ -260,11 +263,11 @@ public class IssueServiceMediumTest { db.actionPlanDao().save(new ActionPlanDto().setKey(actionPlanKey).setProjectId(project.getId())); IssueDto issue = saveIssue(IssueTesting.newDto(rule, file, project).setActionPlanKey(actionPlanKey)); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isEqualTo(actionPlanKey); + assertThat(IssueIndex.getByKey(issue.getKey()).actionPlanKey()).isEqualTo(actionPlanKey); service.plan(issue.getKey(), null); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isNull(); + assertThat(IssueIndex.getByKey(issue.getKey()).actionPlanKey()).isNull(); } @Test @@ -292,11 +295,11 @@ public class IssueServiceMediumTest { IssueDto issue = saveIssue(IssueTesting.newDto(rule, file, project).setSeverity(Severity.BLOCKER)); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).severity()).isEqualTo(Severity.BLOCKER); + assertThat(IssueIndex.getByKey(issue.getKey()).severity()).isEqualTo(Severity.BLOCKER); service.setSeverity(issue.getKey(), Severity.MINOR); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).severity()).isEqualTo(Severity.MINOR); + assertThat(IssueIndex.getByKey(issue.getKey()).severity()).isEqualTo(Severity.MINOR); } @Test @@ -311,7 +314,7 @@ public class IssueServiceMediumTest { Issue result = service.createManualIssue(file.key(), manualRule.getKey(), null, "Fix it", Severity.MINOR, 2d); - IssueDoc manualIssue = (IssueDoc) indexClient.get(IssueIndex.class).getByKey(result.key()); + IssueDoc manualIssue = (IssueDoc) IssueIndex.getByKey(result.key()); assertThat(manualIssue.componentUuid()).isEqualTo(file.uuid()); assertThat(manualIssue.projectUuid()).isEqualTo(project.uuid()); assertThat(manualIssue.ruleKey()).isEqualTo(manualRule.getKey()); @@ -337,7 +340,7 @@ public class IssueServiceMediumTest { Issue result = service.createManualIssue(file.key(), manualRule.getKey(), 1, "Fix it", Severity.MINOR, 2d); - IssueDoc manualIssue = (IssueDoc) indexClient.get(IssueIndex.class).getByKey(result.key()); + IssueDoc manualIssue = (IssueDoc) IssueIndex.getByKey(result.key()); assertThat(manualIssue.componentUuid()).isEqualTo(file.uuid()); assertThat(manualIssue.projectUuid()).isEqualTo(project.uuid()); assertThat(manualIssue.ruleKey()).isEqualTo(manualRule.getKey()); @@ -361,7 +364,7 @@ public class IssueServiceMediumTest { Issue result = service.createManualIssue(file.key(), manualRule.getKey(), null, "Fix it", null, 2d); - Issue manualIssue = indexClient.get(IssueIndex.class).getByKey(result.key()); + Issue manualIssue = IssueIndex.getByKey(result.key()); assertThat(manualIssue.severity()).isEqualTo(Severity.MAJOR); } @@ -377,7 +380,7 @@ public class IssueServiceMediumTest { Issue result = service.createManualIssue(file.key(), manualRule.getKey(), null, null, null, 2d); - Issue manualIssue = indexClient.get(IssueIndex.class).getByKey(result.key()); + Issue manualIssue = IssueIndex.getByKey(result.key()); assertThat(manualIssue.message()).isEqualTo("Manual rule name"); } @@ -397,7 +400,7 @@ public class IssueServiceMediumTest { Issue result = service.createManualIssue(file.key(), manualRule.getKey(), 1, "Fix it", Severity.MINOR, 2d); - IssueDoc manualIssue = (IssueDoc) indexClient.get(IssueIndex.class).getByKey(result.key()); + IssueDoc manualIssue = (IssueDoc) IssueIndex.getByKey(result.key()); assertThat(manualIssue.assignee()).isNull(); } @@ -415,7 +418,7 @@ public class IssueServiceMediumTest { Issue result = service.createManualIssue(file.key(), manualRule.getKey(), 1, "Fix it", Severity.MINOR, 2d); - IssueDoc manualIssue = (IssueDoc) indexClient.get(IssueIndex.class).getByKey(result.key()); + IssueDoc manualIssue = (IssueDoc) IssueIndex.getByKey(result.key()); assertThat(manualIssue.assignee()).isNull(); } @@ -496,7 +499,7 @@ public class IssueServiceMediumTest { ComponentDto file = newFile(project); saveIssue(IssueTesting.newDto(rule, file, project)); - List result = service.search(IssueQuery.builder().build(), new QueryContext()).getHits(); + List result = service.search(IssueQuery.builder().build(), new SearchOptions()).getDocs(); assertThat(result).hasSize(1); } @@ -548,15 +551,15 @@ public class IssueServiceMediumTest { IssueDto issue = saveIssue(IssueTesting.newDto(rule, file, project)); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).tags()).isEmpty(); + assertThat(IssueIndex.getByKey(issue.getKey()).tags()).isEmpty(); // Tags are lowercased service.setTags(issue.getKey(), ImmutableSet.of("bug", "Convention")); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).tags()).containsOnly("bug", "convention"); + assertThat(IssueIndex.getByKey(issue.getKey()).tags()).containsOnly("bug", "convention"); // nulls and empty tags are ignored service.setTags(issue.getKey(), Sets.newHashSet("security", null, "", "convention")); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).tags()).containsOnly("security", "convention"); + assertThat(IssueIndex.getByKey(issue.getKey()).tags()).containsOnly("security", "convention"); // tag validation try { @@ -564,14 +567,14 @@ public class IssueServiceMediumTest { } catch (Exception exception) { assertThat(exception).isInstanceOf(IllegalArgumentException.class); } - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).tags()).containsOnly("security", "convention"); + assertThat(IssueIndex.getByKey(issue.getKey()).tags()).containsOnly("security", "convention"); // unchanged tags service.setTags(issue.getKey(), ImmutableSet.of("convention", "security")); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).tags()).containsOnly("security", "convention"); + assertThat(IssueIndex.getByKey(issue.getKey()).tags()).containsOnly("security", "convention"); service.setTags(issue.getKey(), ImmutableSet.of()); - assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).tags()).isEmpty(); + assertThat(IssueIndex.getByKey(issue.getKey()).tags()).isEmpty(); } @Test @@ -585,7 +588,7 @@ public class IssueServiceMediumTest { saveIssue(IssueTesting.newDto(rule, file, project).setTags(ImmutableSet.of("convention", "java8", "bug")).setResolution(Issue.RESOLUTION_FIXED)); saveIssue(IssueTesting.newDto(rule, file, project).setTags(ImmutableSet.of("convention"))); - assertThat(service.listTagsForComponent(project.uuid(), 5)).contains(entry("convention", 3L), entry("bug", 2L), entry("java8", 1L)); + assertThat(service.listTagsForComponent(project.uuid(), 5)).containsOnly(entry("convention", 3L), entry("bug", 2L), entry("java8", 1L)); assertThat(service.listTagsForComponent(project.uuid(), 2)).contains(entry("convention", 3L), entry("bug", 2L)).doesNotContainEntry("java8", 1L); assertThat(service.listTagsForComponent("other", 10)).isEmpty(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterServiceTest.java index bc1dd5ba7f9..363de1c16e5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterServiceTest.java @@ -26,8 +26,6 @@ import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.web.UserRole; import org.sonar.core.issue.DefaultIssueFilter; import org.sonar.core.issue.IssueFilterSerializer; @@ -37,14 +35,15 @@ import org.sonar.core.issue.db.IssueFilterFavouriteDao; import org.sonar.core.issue.db.IssueFilterFavouriteDto; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.user.AuthorizationDao; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.issue.IssueQuery; +import org.sonar.server.issue.index.IssueDoc; import org.sonar.server.issue.index.IssueIndex; -import org.sonar.server.search.QueryContext; -import org.sonar.server.search.Result; import org.sonar.server.user.MockUserSession; import org.sonar.server.user.UserSession; @@ -528,14 +527,14 @@ public class IssueFilterServiceTest { @Test public void should_execute_from_issue_query() { IssueQuery issueQuery = IssueQuery.builder().build(); - QueryContext queryContext = new QueryContext().setPage(2, 50); + SearchOptions searchOptions = new SearchOptions().setPage(2, 50); - Result result = mock(Result.class); - when(result.getHits()).thenReturn(newArrayList((Issue) new DefaultIssue())); + SearchResult result = mock(SearchResult.class); + when(result.getDocs()).thenReturn(newArrayList((IssueDoc) new IssueDoc())); when(result.getTotal()).thenReturn(100L); - when(issueIndex.search(issueQuery, queryContext)).thenReturn(result); + when(issueIndex.search(issueQuery, searchOptions)).thenReturn(result); - IssueFilterService.IssueFilterResult issueFilterResult = service.execute(issueQuery, queryContext); + IssueFilterService.IssueFilterResult issueFilterResult = service.execute(issueQuery, searchOptions); assertThat(issueFilterResult.issues()).hasSize(1); assertThat(issueFilterResult.paging().total()).isEqualTo(100); assertThat(issueFilterResult.paging().pageIndex()).isEqualTo(2); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java index fb39f468d5e..bcb26d6a3e2 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java @@ -32,12 +32,11 @@ import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.KeyValueFormat; import org.sonar.core.component.ComponentDto; import org.sonar.server.component.ComponentTesting; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.issue.IssueQuery; import org.sonar.server.issue.IssueTesting; -import org.sonar.server.search.FacetValue; -import org.sonar.server.search.QueryContext; -import org.sonar.server.search.Result; import org.sonar.server.tester.ServerTester; import org.sonar.server.user.MockUserSession; import org.sonar.server.view.index.ViewDoc; @@ -46,12 +45,15 @@ import org.sonar.server.view.index.ViewIndexer; import javax.annotation.Nullable; import java.util.Arrays; -import java.util.Collection; import java.util.Date; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; /** * As soon as IssueIndex take {@link org.sonar.server.es.EsClient} in its constructor, ServerTester should be replaced by EsTester, it will make this test going faster ! @@ -126,9 +128,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("1", ComponentTesting.newFileDto(project)), IssueTesting.newDoc("2", ComponentTesting.newFileDto(project))); - assertThat(index.search(IssueQuery.builder().issueKeys(newArrayList("1", "2")).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().issueKeys(newArrayList("1")).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().issueKeys(newArrayList("3", "4")).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().issueKeys(newArrayList("1", "2")).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().issueKeys(newArrayList("1")).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().issueKeys(newArrayList("3", "4")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -145,8 +147,8 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE5", subModule), IssueTesting.newDoc("ISSUE6", ComponentTesting.newFileDto(subModule))); - assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())).build(), new QueryContext()).getHits()).hasSize(6); - assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())).build(), new SearchOptions()).getDocs()).hasSize(6); + assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList("unknown")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -159,9 +161,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", ComponentTesting.newFileDto(project)), IssueTesting.newDoc("ISSUE3", ComponentTesting.newFileDto(project2))); - Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("projectUuids"))); - assertThat(result.getFacets()).containsOnlyKeys("projectUuids"); - assertThat(result.getFacets().get("projectUuids")).containsOnly(new FacetValue("ABCD", 2), new FacetValue("EFGH", 1)); + SearchResult result = index.search(IssueQuery.builder().build(), new SearchOptions().addFacets(newArrayList("projectUuids"))); + assertThat(result.getFacets().getNames()).containsOnly("projectUuids"); + assertThat(result.getFacets().get("projectUuids")).containsOnly(entry("ABCD", 2L), entry("EFGH", 1L)); } @Test @@ -177,15 +179,15 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file)); assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())) - .moduleUuids(newArrayList(file.uuid())).build(), new QueryContext()).getHits()).isEmpty(); + .moduleUuids(newArrayList(file.uuid())).build(), new SearchOptions()).getDocs()).isEmpty(); assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())) - .moduleUuids(newArrayList(module.uuid())).build(), new QueryContext()).getHits()).hasSize(1); + .moduleUuids(newArrayList(module.uuid())).build(), new SearchOptions()).getDocs()).hasSize(1); assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())) - .moduleUuids(newArrayList(subModule.uuid())).build(), new QueryContext()).getHits()).hasSize(1); + .moduleUuids(newArrayList(subModule.uuid())).build(), new SearchOptions()).getDocs()).hasSize(1); assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())) - .moduleUuids(newArrayList(project.uuid())).build(), new QueryContext()).getHits()).hasSize(1); + .moduleUuids(newArrayList(project.uuid())).build(), new SearchOptions()).getDocs()).hasSize(1); assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())) - .moduleUuids(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); + .moduleUuids(newArrayList("unknown")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -207,20 +209,20 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE5", subModule), IssueTesting.newDoc("ISSUE6", file3)); - assertThat(index.search(IssueQuery.builder().setContextualized(true).fileUuids(newArrayList(file1.uuid(), file2.uuid(), file3.uuid())).build(), new QueryContext()) - .getHits()).hasSize(3); - assertThat(index.search(IssueQuery.builder().setContextualized(true).fileUuids(newArrayList(file1.uuid())).build(), new QueryContext()) - .getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().setContextualized(true).moduleRootUuids(newArrayList(subModule.uuid())).build(), new QueryContext()) - .getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().setContextualized(true).moduleRootUuids(newArrayList(module.uuid())).build(), new QueryContext()) - .getHits()).hasSize(4); - assertThat(index.search(IssueQuery.builder().setContextualized(true).projectUuids(newArrayList(project.uuid())).build(), new QueryContext()) - .getHits()).hasSize(6); - assertThat(index.search(IssueQuery.builder().setContextualized(true).viewUuids(newArrayList(view)).build(), new QueryContext()) - .getHits()).hasSize(6); - assertThat(index.search(IssueQuery.builder().setContextualized(true).projectUuids(newArrayList("unknown")).build(), new QueryContext()) - .getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().setContextualized(true).fileUuids(newArrayList(file1.uuid(), file2.uuid(), file3.uuid())).build(), new SearchOptions()) + .getDocs()).hasSize(3); + assertThat(index.search(IssueQuery.builder().setContextualized(true).fileUuids(newArrayList(file1.uuid())).build(), new SearchOptions()) + .getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().setContextualized(true).moduleRootUuids(newArrayList(subModule.uuid())).build(), new SearchOptions()) + .getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().setContextualized(true).moduleRootUuids(newArrayList(module.uuid())).build(), new SearchOptions()) + .getDocs()).hasSize(4); + assertThat(index.search(IssueQuery.builder().setContextualized(true).projectUuids(newArrayList(project.uuid())).build(), new SearchOptions()) + .getDocs()).hasSize(6); + assertThat(index.search(IssueQuery.builder().setContextualized(true).viewUuids(newArrayList(view)).build(), new SearchOptions()) + .getDocs()).hasSize(6); + assertThat(index.search(IssueQuery.builder().setContextualized(true).projectUuids(newArrayList("unknown")).build(), new SearchOptions()) + .getDocs()).isEmpty(); } @Test @@ -242,15 +244,15 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE5", subModule), IssueTesting.newDoc("ISSUE6", file3)); - assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); - assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())).build(), new QueryContext()).getHits()).hasSize(6); - assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList(view)).build(), new QueryContext()).getHits()).hasSize(6); - assertThat(index.search(IssueQuery.builder().moduleUuids(newArrayList(module.uuid())).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().moduleUuids(newArrayList(subModule.uuid())).build(), new QueryContext()).getHits()).hasSize(1); // XXX - // Misleading - // ! - assertThat(index.search(IssueQuery.builder().fileUuids(newArrayList(file1.uuid())).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().fileUuids(newArrayList(file1.uuid(), file2.uuid(), file3.uuid())).build(), new QueryContext()).getHits()).hasSize(3); + assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList("unknown")).build(), new SearchOptions()).getDocs()).isEmpty(); + assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())).build(), new SearchOptions()).getDocs()).hasSize(6); + assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList(view)).build(), new SearchOptions()).getDocs()).hasSize(6); + assertThat(index.search(IssueQuery.builder().moduleUuids(newArrayList(module.uuid())).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().moduleUuids(newArrayList(subModule.uuid())).build(), new SearchOptions()).getDocs()).hasSize(1); // XXX + // Misleading + // ! + assertThat(index.search(IssueQuery.builder().fileUuids(newArrayList(file1.uuid())).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().fileUuids(newArrayList(file1.uuid(), file2.uuid(), file3.uuid())).build(), new SearchOptions()).getDocs()).hasSize(3); } @Test @@ -267,9 +269,10 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE4", file2), IssueTesting.newDoc("ISSUE5", file3)); - Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("fileUuids"))); - assertThat(result.getFacets()).containsOnlyKeys("fileUuids"); - assertThat(result.getFacets().get("fileUuids")).containsOnly(new FacetValue("A", 1), new FacetValue("ABCD", 1), new FacetValue("BCDE", 2), new FacetValue("CDEF", 1)); + SearchResult result = index.search(IssueQuery.builder().build(), new SearchOptions().addFacets(newArrayList("fileUuids"))); + assertThat(result.getFacets().getNames()).containsOnly("fileUuids"); + assertThat(result.getFacets().get("fileUuids")) + .containsOnly(entry("A", 1L), entry("ABCD", 1L), entry("BCDE", 2L), entry("CDEF", 1L)); } @Test @@ -282,9 +285,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE1", file1).setDirectoryPath("/src/main/xoo"), IssueTesting.newDoc("ISSUE2", file2).setDirectoryPath("/")); - assertThat(index.search(IssueQuery.builder().directories(newArrayList("/src/main/xoo")).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().directories(newArrayList("/")).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().directories(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().directories(newArrayList("/src/main/xoo")).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().directories(newArrayList("/")).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().directories(newArrayList("unknown")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -297,9 +300,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE1", file1).setDirectoryPath("/src/main/xoo"), IssueTesting.newDoc("ISSUE2", file2).setDirectoryPath("/")); - Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("directories"))); - assertThat(result.getFacets()).containsOnlyKeys("directories"); - assertThat(result.getFacets().get("directories")).containsOnly(new FacetValue("/src/main/xoo", 1), new FacetValue("/", 1)); + SearchResult result = index.search(IssueQuery.builder().build(), new SearchOptions().addFacets(newArrayList("directories"))); + assertThat(result.getFacets().getNames()).containsOnly("directories"); + assertThat(result.getFacets().get("directories")).containsOnly(entry("/src/main/xoo", 1L), entry("/", 1L)); } @Test @@ -322,10 +325,10 @@ public class IssueIndexMediumTest { String view2 = "CDEF"; indexView(view2, newArrayList(project2.uuid())); - assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList(view1)).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList(view2)).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList(view1, view2)).build(), new QueryContext()).getHits()).hasSize(3); - assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList(view1)).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList(view2)).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList(view1, view2)).build(), new SearchOptions()).getDocs()).hasSize(3); + assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList("unknown")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -337,9 +340,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE1", file).setSeverity(Severity.INFO), IssueTesting.newDoc("ISSUE2", file).setSeverity(Severity.MAJOR)); - assertThat(index.search(IssueQuery.builder().severities(newArrayList(Severity.INFO, Severity.MAJOR)).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().severities(newArrayList(Severity.INFO)).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().severities(newArrayList(Severity.BLOCKER)).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().severities(newArrayList(Severity.INFO, Severity.MAJOR)).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().severities(newArrayList(Severity.INFO)).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().severities(newArrayList(Severity.BLOCKER)).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -352,9 +355,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file).setSeverity(Severity.INFO), IssueTesting.newDoc("ISSUE3", file).setSeverity(Severity.MAJOR)); - Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("severities"))); - assertThat(result.getFacets()).containsOnlyKeys("severities"); - assertThat(result.getFacets().get("severities")).containsOnly(new FacetValue("INFO", 2), new FacetValue("MAJOR", 1)); + SearchResult result = index.search(IssueQuery.builder().build(), new SearchOptions().addFacets(newArrayList("severities"))); + assertThat(result.getFacets().getNames()).containsOnly("severities"); + assertThat(result.getFacets().get("severities")).containsOnly(entry("INFO", 2L), entry("MAJOR", 1L)); } @Test @@ -366,9 +369,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE1", file).setStatus(Issue.STATUS_CLOSED), IssueTesting.newDoc("ISSUE2", file).setStatus(Issue.STATUS_OPEN)); - assertThat(index.search(IssueQuery.builder().statuses(newArrayList(Issue.STATUS_CLOSED, Issue.STATUS_OPEN)).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().statuses(newArrayList(Issue.STATUS_CLOSED)).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().statuses(newArrayList(Issue.STATUS_CONFIRMED)).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().statuses(newArrayList(Issue.STATUS_CLOSED, Issue.STATUS_OPEN)).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().statuses(newArrayList(Issue.STATUS_CLOSED)).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().statuses(newArrayList(Issue.STATUS_CONFIRMED)).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -381,9 +384,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file).setStatus(Issue.STATUS_CLOSED), IssueTesting.newDoc("ISSUE3", file).setStatus(Issue.STATUS_OPEN)); - Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("statuses"))); - assertThat(result.getFacets()).containsOnlyKeys("statuses"); - assertThat(result.getFacets().get("statuses")).containsOnly(new FacetValue("CLOSED", 2), new FacetValue("OPEN", 1)); + SearchResult result = index.search(IssueQuery.builder().build(), new SearchOptions().addFacets(newArrayList("statuses"))); + assertThat(result.getFacets().getNames()).containsOnly("statuses"); + assertThat(result.getFacets().get("statuses")).containsOnly(entry("CLOSED", 2L), entry("OPEN", 1L)); } @Test @@ -395,10 +398,10 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE1", file).setResolution(Issue.RESOLUTION_FALSE_POSITIVE), IssueTesting.newDoc("ISSUE2", file).setResolution(Issue.RESOLUTION_FIXED)); - assertThat(index.search(IssueQuery.builder().resolutions(newArrayList(Issue.RESOLUTION_FALSE_POSITIVE, Issue.RESOLUTION_FIXED)).build(), new QueryContext()).getHits()) + assertThat(index.search(IssueQuery.builder().resolutions(newArrayList(Issue.RESOLUTION_FALSE_POSITIVE, Issue.RESOLUTION_FIXED)).build(), new SearchOptions()).getDocs()) .hasSize(2); - assertThat(index.search(IssueQuery.builder().resolutions(newArrayList(Issue.RESOLUTION_FALSE_POSITIVE)).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().resolutions(newArrayList(Issue.RESOLUTION_REMOVED)).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().resolutions(newArrayList(Issue.RESOLUTION_FALSE_POSITIVE)).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().resolutions(newArrayList(Issue.RESOLUTION_REMOVED)).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -411,9 +414,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file).setResolution(Issue.RESOLUTION_FALSE_POSITIVE), IssueTesting.newDoc("ISSUE3", file).setResolution(Issue.RESOLUTION_FIXED)); - Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("resolutions"))); - assertThat(result.getFacets()).containsOnlyKeys("resolutions"); - assertThat(result.getFacets().get("resolutions")).containsOnly(new FacetValue("FALSE-POSITIVE", 2), new FacetValue("FIXED", 1)); + SearchResult result = index.search(IssueQuery.builder().build(), new SearchOptions().addFacets(newArrayList("resolutions"))); + assertThat(result.getFacets().getNames()).containsOnly("resolutions"); + assertThat(result.getFacets().get("resolutions")).containsOnly(entry("FALSE-POSITIVE", 2L), entry("FIXED", 1L)); } @Test @@ -426,9 +429,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file).setStatus(Issue.STATUS_OPEN).setResolution(null), IssueTesting.newDoc("ISSUE3", file).setStatus(Issue.STATUS_OPEN).setResolution(null)); - assertThat(index.search(IssueQuery.builder().resolved(true).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().resolved(false).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().resolved(null).build(), new QueryContext()).getHits()).hasSize(3); + assertThat(index.search(IssueQuery.builder().resolved(true).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().resolved(false).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().resolved(null).build(), new SearchOptions()).getDocs()).hasSize(3); } @Test @@ -440,10 +443,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE1", file).setActionPlanKey("plan1"), IssueTesting.newDoc("ISSUE2", file).setActionPlanKey("plan2")); - assertThat(index.search(IssueQuery.builder().actionPlans(newArrayList("plan1")).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().actionPlans(newArrayList("plan1", "plan2")).build(), new - QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().actionPlans(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().actionPlans(newArrayList("plan1")).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().actionPlans(newArrayList("plan1", "plan2")).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().actionPlans(newArrayList("unknown")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -455,9 +457,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE1", file).setActionPlanKey("plan1"), IssueTesting.newDoc("ISSUE2", file).setActionPlanKey("plan2")); - Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("actionPlans"))); - assertThat(result.getFacets()).containsOnlyKeys("actionPlans"); - assertThat(result.getFacets().get("actionPlans")).containsOnly(new FacetValue("plan1", 1), new FacetValue("plan2", 1)); + SearchResult result = index.search(IssueQuery.builder().build(), new SearchOptions().addFacets(newArrayList("actionPlans"))); + assertThat(result.getFacets().getNames()).containsOnly("actionPlans"); + assertThat(result.getFacets().get("actionPlans")).containsOnly(entry("plan1", 1L), entry("plan2", 1L)); } @Test @@ -470,9 +472,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file).setActionPlanKey(null), IssueTesting.newDoc("ISSUE3", file).setActionPlanKey(null)); - assertThat(index.search(IssueQuery.builder().planned(true).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().planned(false).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().planned(null).build(), new QueryContext()).getHits()).hasSize(3); + assertThat(index.search(IssueQuery.builder().planned(true).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().planned(false).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().planned(null).build(), new SearchOptions()).getDocs()).hasSize(3); } @Test @@ -483,8 +485,8 @@ public class IssueIndexMediumTest { indexIssues(IssueTesting.newDoc("ISSUE1", file).setRuleKey(ruleKey.toString())); - assertThat(index.search(IssueQuery.builder().rules(newArrayList(ruleKey)).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().rules(newArrayList(RuleKey.of("rule", "without issue"))).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().rules(newArrayList(ruleKey)).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().rules(newArrayList(RuleKey.of("rule", "without issue"))).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -495,9 +497,9 @@ public class IssueIndexMediumTest { indexIssues(IssueTesting.newDoc("ISSUE1", file).setRuleKey(ruleKey.toString()).setLanguage("xoo")); - assertThat(index.search(IssueQuery.builder().languages(newArrayList("xoo")).build(), new - QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().languages(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().languages(newArrayList("xoo")).build(), + new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().languages(newArrayList("unknown")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -508,9 +510,9 @@ public class IssueIndexMediumTest { indexIssues(IssueTesting.newDoc("ISSUE1", file).setRuleKey(ruleKey.toString()).setLanguage("xoo")); - Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("languages"))); - assertThat(result.getFacets()).containsOnlyKeys("languages"); - assertThat(result.getFacets().get("languages")).containsOnly(new FacetValue("xoo", 1)); + SearchResult result = index.search(IssueQuery.builder().build(), new SearchOptions().addFacets(newArrayList("languages"))); + assertThat(result.getFacets().getNames()).containsOnly("languages"); + assertThat(result.getFacets().get("languages")).containsOnly(entry("xoo", 1L)); } @Test @@ -523,9 +525,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file).setAssignee("simon"), IssueTesting.newDoc("ISSUE3", file).setAssignee(null)); - assertThat(index.search(IssueQuery.builder().assignees(newArrayList("steph")).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().assignees(newArrayList("steph", "simon")).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().assignees(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().assignees(newArrayList("steph")).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().assignees(newArrayList("steph", "simon")).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().assignees(newArrayList("unknown")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -539,9 +541,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE3", file).setAssignee("simon"), IssueTesting.newDoc("ISSUE4", file).setAssignee(null)); - Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("assignees"))); - assertThat(result.getFacets()).containsOnlyKeys("assignees"); - assertThat(result.getFacets().get("assignees")).containsOnly(new FacetValue("steph", 1), new FacetValue("simon", 2), new FacetValue("", 1)); + SearchResult result = index.search(IssueQuery.builder().build(), new SearchOptions().addFacets(newArrayList("assignees"))); + assertThat(result.getFacets().getNames()).containsOnly("assignees"); + assertThat(result.getFacets().get("assignees")).containsOnly(entry("steph", 1L), entry("simon", 2L), entry("", 1L)); } @Test @@ -554,9 +556,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file).setAssignee(null), IssueTesting.newDoc("ISSUE3", file).setAssignee(null)); - assertThat(index.search(IssueQuery.builder().assigned(true).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().assigned(false).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().assigned(null).build(), new QueryContext()).getHits()).hasSize(3); + assertThat(index.search(IssueQuery.builder().assigned(true).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().assigned(false).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().assigned(null).build(), new SearchOptions()).getDocs()).hasSize(3); } @Test @@ -568,9 +570,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE1", file).setReporter("fabrice"), IssueTesting.newDoc("ISSUE2", file).setReporter("stephane")); - assertThat(index.search(IssueQuery.builder().reporters(newArrayList("fabrice", "stephane")).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().reporters(newArrayList("fabrice")).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().reporters(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().reporters(newArrayList("fabrice", "stephane")).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().reporters(newArrayList("fabrice")).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().reporters(newArrayList("unknown")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -583,9 +585,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file).setAuthorLogin("simon"), IssueTesting.newDoc("ISSUE3", file).setAssignee(null)); - assertThat(index.search(IssueQuery.builder().authors(newArrayList("steph")).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().authors(newArrayList("steph", "simon")).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().authors(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().authors(newArrayList("steph")).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().authors(newArrayList("steph", "simon")).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().authors(newArrayList("unknown")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -599,9 +601,9 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE3", file).setAuthorLogin("simon"), IssueTesting.newDoc("ISSUE4", file).setAuthorLogin(null)); - Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("authors"))); - assertThat(result.getFacets()).containsOnlyKeys("authors"); - assertThat(result.getFacets().get("authors")).containsOnly(new FacetValue("steph", 1), new FacetValue("simon", 2)); + SearchResult result = index.search(IssueQuery.builder().build(), new SearchOptions().addFacets(newArrayList("authors"))); + assertThat(result.getFacets().getNames()).containsOnly("authors"); + assertThat(result.getFacets().get("authors")).containsOnly(entry("steph", 1L), entry("simon", 2L)); } @Test @@ -613,10 +615,10 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE1", file).setFuncCreationDate(DateUtils.parseDate("2014-09-20")), IssueTesting.newDoc("ISSUE2", file).setFuncCreationDate(DateUtils.parseDate("2014-09-23"))); - assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-19")).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-20")).build(), new QueryContext()).getHits()).hasSize(2); - assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-21")).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-25")).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-19")).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-20")).build(), new SearchOptions()).getDocs()).hasSize(2); + assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-21")).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-25")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -628,10 +630,10 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE1", file).setFuncCreationDate(DateUtils.parseDate("2014-09-20")), IssueTesting.newDoc("ISSUE2", file).setFuncCreationDate(DateUtils.parseDate("2014-09-23"))); - assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-19")).build(), new QueryContext()).getHits()).isEmpty(); - assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-20")).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-21")).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-25")).build(), new QueryContext()).getHits()).hasSize(2); + assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-19")).build(), new SearchOptions()).getDocs()).isEmpty(); + assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-20")).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-21")).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2014-09-25")).build(), new SearchOptions()).getDocs()).hasSize(2); } @Test @@ -641,121 +643,114 @@ public class IssueIndexMediumTest { indexIssues(IssueTesting.newDoc("ISSUE1", file).setFuncCreationDate(DateUtils.parseDate("2014-09-20"))); - assertThat(index.search(IssueQuery.builder().createdAt(DateUtils.parseDate("2014-09-20")).build(), new QueryContext()).getHits()).hasSize(1); - assertThat(index.search(IssueQuery.builder().createdAt(DateUtils.parseDate("2014-09-21")).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().createdAt(DateUtils.parseDate("2014-09-20")).build(), new SearchOptions()).getDocs()).hasSize(1); + assertThat(index.search(IssueQuery.builder().createdAt(DateUtils.parseDate("2014-09-21")).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test public void facet_on_created_at_with_less_than_20_days() throws Exception { - QueryContext queryContext = fixtureForCreatedAtFacet(); - - Collection createdAt = index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-01")).createdBefore(DateUtils.parseDate("2014-09-08")).build(), - queryContext).getFacets().get("createdAt"); - assertThat(createdAt).hasSize(8) - .containsOnly( - new FacetValue("2014-08-31T01:00:00+0000", 0), - new FacetValue("2014-09-01T01:00:00+0000", 2), - new FacetValue("2014-09-02T01:00:00+0000", 1), - new FacetValue("2014-09-03T01:00:00+0000", 0), - new FacetValue("2014-09-04T01:00:00+0000", 0), - new FacetValue("2014-09-05T01:00:00+0000", 1), - new FacetValue("2014-09-06T01:00:00+0000", 0), - new FacetValue("2014-09-07T01:00:00+0000", 0)); + SearchOptions options = fixtureForCreatedAtFacet(); + + IssueQuery query = IssueQuery.builder() + .createdAfter(DateUtils.parseDate("2014-09-01")) + .createdBefore(DateUtils.parseDate("2014-09-08")) + .checkAuthorization(false) + .build(); + SearchResult result = index.search(query, options); + Map buckets = result.getFacets().get("createdAt"); + assertThat(buckets).containsOnly( + entry("2014-08-31T01:00:00+0000", 0L), + entry("2014-09-01T01:00:00+0000", 2L), + entry("2014-09-02T01:00:00+0000", 1L), + entry("2014-09-03T01:00:00+0000", 0L), + entry("2014-09-04T01:00:00+0000", 0L), + entry("2014-09-05T01:00:00+0000", 1L), + entry("2014-09-06T01:00:00+0000", 0L), + entry("2014-09-07T01:00:00+0000", 0L)); } @Test public void facet_on_created_at_with_less_than_20_weeks() throws Exception { - QueryContext queryContext = fixtureForCreatedAtFacet(); + SearchOptions SearchOptions = fixtureForCreatedAtFacet(); - Collection createdAt = index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-01")).createdBefore(DateUtils.parseDate("2014-09-21")).build(), - queryContext).getFacets().get("createdAt"); - assertThat(createdAt).hasSize(4) - .containsOnly( - new FacetValue("2014-08-25T01:00:00+0000", 0), - new FacetValue("2014-09-01T01:00:00+0000", 4), - new FacetValue("2014-09-08T01:00:00+0000", 0), - new FacetValue("2014-09-15T01:00:00+0000", 1)); + Map createdAt = index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-01")).createdBefore(DateUtils.parseDate("2014-09-21")).build(), + SearchOptions).getFacets().get("createdAt"); + assertThat(createdAt).containsOnly( + entry("2014-08-25T01:00:00+0000", 0L), + entry("2014-09-01T01:00:00+0000", 4L), + entry("2014-09-08T01:00:00+0000", 0L), + entry("2014-09-15T01:00:00+0000", 1L)); } @Test public void facet_on_created_at_with_less_than_20_months() throws Exception { - QueryContext queryContext = fixtureForCreatedAtFacet(); - - Collection createdAt = index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-01")).createdBefore(DateUtils.parseDate("2015-01-19")).build(), - queryContext).getFacets().get("createdAt"); - assertThat(createdAt).hasSize(6) - .containsOnly( - new FacetValue("2014-08-01T01:00:00+0000", 0), - new FacetValue("2014-09-01T01:00:00+0000", 5), - new FacetValue("2014-10-01T01:00:00+0000", 0), - new FacetValue("2014-11-01T01:00:00+0000", 0), - new FacetValue("2014-12-01T01:00:00+0000", 0), - new FacetValue("2015-01-01T01:00:00+0000", 1)); + SearchOptions SearchOptions = fixtureForCreatedAtFacet(); + + Map createdAt = index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2014-09-01")).createdBefore(DateUtils.parseDate("2015-01-19")).build(), + SearchOptions).getFacets().get("createdAt"); + assertThat(createdAt).containsOnly( + entry("2014-08-01T01:00:00+0000", 0L), + entry("2014-09-01T01:00:00+0000", 5L), + entry("2014-10-01T01:00:00+0000", 0L), + entry("2014-11-01T01:00:00+0000", 0L), + entry("2014-12-01T01:00:00+0000", 0L), + entry("2015-01-01T01:00:00+0000", 1L)); } @Test public void facet_on_created_at_with_more_than_20_months() throws Exception { - - QueryContext queryContext = fixtureForCreatedAtFacet(); - - Collection createdAt = index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2011-01-01")).createdBefore(DateUtils.parseDate("2016-01-01")).build(), - queryContext).getFacets().get("createdAt"); - assertThat(createdAt).hasSize(6) - .containsOnly( - new FacetValue("2011-01-01T01:00:00+0000", 1), - new FacetValue("2012-01-01T01:00:00+0000", 0), - new FacetValue("2013-01-01T01:00:00+0000", 0), - new FacetValue("2014-01-01T01:00:00+0000", 5), - new FacetValue("2015-01-01T01:00:00+0000", 1), - new FacetValue("2016-01-01T01:00:00+0000", 0)); + SearchOptions SearchOptions = fixtureForCreatedAtFacet(); + + Map createdAt = index.search(IssueQuery.builder().createdAfter(DateUtils.parseDate("2011-01-01")).createdBefore(DateUtils.parseDate("2016-01-01")).build(), + SearchOptions).getFacets().get("createdAt"); + assertThat(createdAt).containsOnly( + entry("2011-01-01T01:00:00+0000", 1L), + entry("2012-01-01T01:00:00+0000", 0L), + entry("2013-01-01T01:00:00+0000", 0L), + entry("2014-01-01T01:00:00+0000", 5L), + entry("2015-01-01T01:00:00+0000", 1L), + entry("2016-01-01T01:00:00+0000", 0L)); } @Test public void facet_on_created_at_with_bounds_outside_of_data() throws Exception { - - QueryContext queryContext = fixtureForCreatedAtFacet(); - - Collection createdAt = index.search(IssueQuery.builder() - .createdAfter(DateUtils.parseDate("2009-01-01")) - .createdBefore(DateUtils.parseDate("2016-01-01")) - .build(), - queryContext).getFacets().get("createdAt"); - assertThat(createdAt).hasSize(8) - .containsOnly( - new FacetValue("2009-01-01T01:00:00+0000", 0), - new FacetValue("2010-01-01T01:00:00+0000", 0), - new FacetValue("2011-01-01T01:00:00+0000", 1), - new FacetValue("2012-01-01T01:00:00+0000", 0), - new FacetValue("2013-01-01T01:00:00+0000", 0), - new FacetValue("2014-01-01T01:00:00+0000", 5), - new FacetValue("2015-01-01T01:00:00+0000", 1), - new FacetValue("2016-01-01T01:00:00+0000", 0)); - + SearchOptions options = fixtureForCreatedAtFacet(); + + Map createdAt = index.search(IssueQuery.builder() + .createdAfter(DateUtils.parseDate("2009-01-01")) + .createdBefore(DateUtils.parseDate("2016-01-01")) + .build(), options).getFacets().get("createdAt"); + assertThat(createdAt).containsOnly( + entry("2009-01-01T01:00:00+0000", 0L), + entry("2010-01-01T01:00:00+0000", 0L), + entry("2011-01-01T01:00:00+0000", 1L), + entry("2012-01-01T01:00:00+0000", 0L), + entry("2013-01-01T01:00:00+0000", 0L), + entry("2014-01-01T01:00:00+0000", 5L), + entry("2015-01-01T01:00:00+0000", 1L), + entry("2016-01-01T01:00:00+0000", 0L)); } @Test public void facet_on_created_at_without_start_bound() throws Exception { - - QueryContext queryContext = fixtureForCreatedAtFacet(); - - Collection createdAt = index.search(IssueQuery.builder() - .createdBefore(DateUtils.parseDate("2016-01-01")).build(), - queryContext).getFacets().get("createdAt"); - assertThat(createdAt).hasSize(6) - .containsOnly( - new FacetValue("2011-01-01T01:00:00+0000", 1), - new FacetValue("2012-01-01T01:00:00+0000", 0), - new FacetValue("2013-01-01T01:00:00+0000", 0), - new FacetValue("2014-01-01T01:00:00+0000", 5), - new FacetValue("2015-01-01T01:00:00+0000", 1), - new FacetValue("2016-01-01T01:00:00+0000", 0)); + SearchOptions SearchOptions = fixtureForCreatedAtFacet(); + + Map createdAt = index.search(IssueQuery.builder().createdBefore(DateUtils.parseDate("2016-01-01")).build(), + SearchOptions).getFacets().get("createdAt"); + assertThat(createdAt).containsOnly( + entry("2011-01-01T01:00:00+0000", 1L), + entry("2012-01-01T01:00:00+0000", 0L), + entry("2013-01-01T01:00:00+0000", 0L), + entry("2014-01-01T01:00:00+0000", 5L), + entry("2015-01-01T01:00:00+0000", 1L), + entry("2016-01-01T01:00:00+0000", 0L)); } - protected QueryContext fixtureForCreatedAtFacet() { + protected SearchOptions fixtureForCreatedAtFacet() { ComponentDto project = ComponentTesting.newProjectDto(); ComponentDto file = ComponentTesting.newFileDto(project); @@ -769,8 +764,7 @@ public class IssueIndexMediumTest { indexIssues(issue0, issue1, issue2, issue3, issue4, issue5, issue6); - QueryContext queryContext = new QueryContext().addFacets(Arrays.asList("createdAt")); - return queryContext; + return new SearchOptions().addFacets("createdAt"); } @Test @@ -783,16 +777,16 @@ public class IssueIndexMediumTest { IssueQuery.Builder query = IssueQuery.builder(); // There are 12 issues in total, with 10 issues per page, the page 2 should only contain 2 elements - Result result = index.search(query.build(), new QueryContext().setPage(2, 10)); - assertThat(result.getHits()).hasSize(2); + SearchResult result = index.search(query.build(), new SearchOptions().setPage(2, 10)); + assertThat(result.getDocs()).hasSize(2); assertThat(result.getTotal()).isEqualTo(12); - result = index.search(IssueQuery.builder().build(), new QueryContext().setOffset(0).setLimit(5)); - assertThat(result.getHits()).hasSize(5); + result = index.search(IssueQuery.builder().build(), new SearchOptions().setOffset(0).setLimit(5)); + assertThat(result.getDocs()).hasSize(5); assertThat(result.getTotal()).isEqualTo(12); - result = index.search(IssueQuery.builder().build(), new QueryContext().setOffset(2).setLimit(0)); - assertThat(result.getHits()).hasSize(0); + result = index.search(IssueQuery.builder().build(), new SearchOptions().setOffset(2).setLimit(0)); + assertThat(result.getDocs()).hasSize(0); assertThat(result.getTotal()).isEqualTo(12); } @@ -808,8 +802,8 @@ public class IssueIndexMediumTest { indexIssues(issues.toArray(new IssueDoc[] {})); IssueQuery.Builder query = IssueQuery.builder(); - Result result = index.search(query.build(), new QueryContext().setMaxLimit()); - assertThat(result.getHits()).hasSize(500); + SearchResult result = index.search(query.build(), new SearchOptions().setLimit(Integer.MAX_VALUE)); + assertThat(result.getDocs()).hasSize(SearchOptions.MAX_LIMIT); } @Test @@ -823,16 +817,16 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE3", file).setStatus(Issue.STATUS_REOPENED)); IssueQuery.Builder query = IssueQuery.builder().sort(IssueQuery.SORT_BY_STATUS).asc(true); - Result result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits().get(0).status()).isEqualTo(Issue.STATUS_CLOSED); - assertThat(result.getHits().get(1).status()).isEqualTo(Issue.STATUS_OPEN); - assertThat(result.getHits().get(2).status()).isEqualTo(Issue.STATUS_REOPENED); + SearchResult result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs().get(0).status()).isEqualTo(Issue.STATUS_CLOSED); + assertThat(result.getDocs().get(1).status()).isEqualTo(Issue.STATUS_OPEN); + assertThat(result.getDocs().get(2).status()).isEqualTo(Issue.STATUS_REOPENED); query = IssueQuery.builder().sort(IssueQuery.SORT_BY_STATUS).asc(false); - result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits().get(0).status()).isEqualTo(Issue.STATUS_REOPENED); - assertThat(result.getHits().get(1).status()).isEqualTo(Issue.STATUS_OPEN); - assertThat(result.getHits().get(2).status()).isEqualTo(Issue.STATUS_CLOSED); + result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs().get(0).status()).isEqualTo(Issue.STATUS_REOPENED); + assertThat(result.getDocs().get(1).status()).isEqualTo(Issue.STATUS_OPEN); + assertThat(result.getDocs().get(2).status()).isEqualTo(Issue.STATUS_CLOSED); } @Test @@ -848,20 +842,20 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE5", file).setSeverity(Severity.MAJOR)); IssueQuery.Builder query = IssueQuery.builder().sort(IssueQuery.SORT_BY_SEVERITY).asc(true); - Result result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits().get(0).severity()).isEqualTo(Severity.INFO); - assertThat(result.getHits().get(1).severity()).isEqualTo(Severity.MINOR); - assertThat(result.getHits().get(2).severity()).isEqualTo(Severity.MAJOR); - assertThat(result.getHits().get(3).severity()).isEqualTo(Severity.CRITICAL); - assertThat(result.getHits().get(4).severity()).isEqualTo(Severity.BLOCKER); + SearchResult result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs().get(0).severity()).isEqualTo(Severity.INFO); + assertThat(result.getDocs().get(1).severity()).isEqualTo(Severity.MINOR); + assertThat(result.getDocs().get(2).severity()).isEqualTo(Severity.MAJOR); + assertThat(result.getDocs().get(3).severity()).isEqualTo(Severity.CRITICAL); + assertThat(result.getDocs().get(4).severity()).isEqualTo(Severity.BLOCKER); query = IssueQuery.builder().sort(IssueQuery.SORT_BY_SEVERITY).asc(false); - result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits().get(0).severity()).isEqualTo(Severity.BLOCKER); - assertThat(result.getHits().get(1).severity()).isEqualTo(Severity.CRITICAL); - assertThat(result.getHits().get(2).severity()).isEqualTo(Severity.MAJOR); - assertThat(result.getHits().get(3).severity()).isEqualTo(Severity.MINOR); - assertThat(result.getHits().get(4).severity()).isEqualTo(Severity.INFO); + result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs().get(0).severity()).isEqualTo(Severity.BLOCKER); + assertThat(result.getDocs().get(1).severity()).isEqualTo(Severity.CRITICAL); + assertThat(result.getDocs().get(2).severity()).isEqualTo(Severity.MAJOR); + assertThat(result.getDocs().get(3).severity()).isEqualTo(Severity.MINOR); + assertThat(result.getDocs().get(4).severity()).isEqualTo(Severity.INFO); } @Test @@ -874,16 +868,16 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file).setAssignee("simon")); IssueQuery.Builder query = IssueQuery.builder().sort(IssueQuery.SORT_BY_ASSIGNEE).asc(true); - Result result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits()).hasSize(2); - assertThat(result.getHits().get(0).assignee()).isEqualTo("simon"); - assertThat(result.getHits().get(1).assignee()).isEqualTo("steph"); + SearchResult result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs()).hasSize(2); + assertThat(result.getDocs().get(0).assignee()).isEqualTo("simon"); + assertThat(result.getDocs().get(1).assignee()).isEqualTo("steph"); query = IssueQuery.builder().sort(IssueQuery.SORT_BY_ASSIGNEE).asc(false); - result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits()).hasSize(2); - assertThat(result.getHits().get(0).assignee()).isEqualTo("steph"); - assertThat(result.getHits().get(1).assignee()).isEqualTo("simon"); + result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs()).hasSize(2); + assertThat(result.getDocs().get(0).assignee()).isEqualTo("steph"); + assertThat(result.getDocs().get(1).assignee()).isEqualTo("simon"); } @Test @@ -896,16 +890,16 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file).setFuncCreationDate(DateUtils.parseDate("2014-09-24"))); IssueQuery.Builder query = IssueQuery.builder().sort(IssueQuery.SORT_BY_CREATION_DATE).asc(true); - Result result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits()).hasSize(2); - assertThat(result.getHits().get(0).creationDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); - assertThat(result.getHits().get(1).creationDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); + SearchResult result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs()).hasSize(2); + assertThat(result.getDocs().get(0).creationDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); + assertThat(result.getDocs().get(1).creationDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); query = IssueQuery.builder().sort(IssueQuery.SORT_BY_CREATION_DATE).asc(false); - result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits()).hasSize(2); - assertThat(result.getHits().get(0).creationDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); - assertThat(result.getHits().get(1).creationDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); + result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs()).hasSize(2); + assertThat(result.getDocs().get(0).creationDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); + assertThat(result.getDocs().get(1).creationDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); } @Test @@ -918,16 +912,16 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE2", file).setFuncUpdateDate(DateUtils.parseDate("2014-09-24"))); IssueQuery.Builder query = IssueQuery.builder().sort(IssueQuery.SORT_BY_UPDATE_DATE).asc(true); - Result result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits()).hasSize(2); - assertThat(result.getHits().get(0).updateDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); - assertThat(result.getHits().get(1).updateDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); + SearchResult result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs()).hasSize(2); + assertThat(result.getDocs().get(0).updateDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); + assertThat(result.getDocs().get(1).updateDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); query = IssueQuery.builder().sort(IssueQuery.SORT_BY_UPDATE_DATE).asc(false); - result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits()).hasSize(2); - assertThat(result.getHits().get(0).updateDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); - assertThat(result.getHits().get(1).updateDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); + result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs()).hasSize(2); + assertThat(result.getDocs().get(0).updateDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); + assertThat(result.getDocs().get(1).updateDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); } @Test @@ -941,18 +935,18 @@ public class IssueIndexMediumTest { IssueTesting.newDoc("ISSUE3", file).setFuncCloseDate(null)); IssueQuery.Builder query = IssueQuery.builder().sort(IssueQuery.SORT_BY_CLOSE_DATE).asc(true); - Result result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits()).hasSize(3); - assertThat(result.getHits().get(0).closeDate()).isNull(); - assertThat(result.getHits().get(1).closeDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); - assertThat(result.getHits().get(2).closeDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); + SearchResult result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs()).hasSize(3); + assertThat(result.getDocs().get(0).closeDate()).isNull(); + assertThat(result.getDocs().get(1).closeDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); + assertThat(result.getDocs().get(2).closeDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); query = IssueQuery.builder().sort(IssueQuery.SORT_BY_CLOSE_DATE).asc(false); - result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits()).hasSize(3); - assertThat(result.getHits().get(0).closeDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); - assertThat(result.getHits().get(1).closeDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); - assertThat(result.getHits().get(2).closeDate()).isNull(); + result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs()).hasSize(3); + assertThat(result.getDocs().get(0).closeDate()).isEqualTo(DateUtils.parseDate("2014-09-24")); + assertThat(result.getDocs().get(1).closeDate()).isEqualTo(DateUtils.parseDate("2014-09-23")); + assertThat(result.getDocs().get(2).closeDate()).isNull(); } @Test @@ -975,25 +969,25 @@ public class IssueIndexMediumTest { // ascending sort -> F1 then F2. Line "0" first. IssueQuery.Builder query = IssueQuery.builder().sort(IssueQuery.SORT_BY_FILE_LINE).asc(true); - Result result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits()).hasSize(6); - assertThat(result.getHits().get(0).key()).isEqualTo("F1_1"); - assertThat(result.getHits().get(1).key()).isEqualTo("F1_2"); - assertThat(result.getHits().get(2).key()).isEqualTo("F1_3"); - assertThat(result.getHits().get(3).key()).isEqualTo("F2_1"); - assertThat(result.getHits().get(4).key()).isEqualTo("F2_2"); - assertThat(result.getHits().get(5).key()).isEqualTo("F2_3"); + SearchResult result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs()).hasSize(6); + assertThat(result.getDocs().get(0).key()).isEqualTo("F1_1"); + assertThat(result.getDocs().get(1).key()).isEqualTo("F1_2"); + assertThat(result.getDocs().get(2).key()).isEqualTo("F1_3"); + assertThat(result.getDocs().get(3).key()).isEqualTo("F2_1"); + assertThat(result.getDocs().get(4).key()).isEqualTo("F2_2"); + assertThat(result.getDocs().get(5).key()).isEqualTo("F2_3"); // descending sort -> F2 then F1 query = IssueQuery.builder().sort(IssueQuery.SORT_BY_FILE_LINE).asc(false); - result = index.search(query.build(), new QueryContext()); - assertThat(result.getHits()).hasSize(6); - assertThat(result.getHits().get(0).key()).isEqualTo("F2_3"); - assertThat(result.getHits().get(1).key()).isEqualTo("F2_2"); - assertThat(result.getHits().get(2).key()).isEqualTo("F2_1"); - assertThat(result.getHits().get(3).key()).isEqualTo("F1_3"); - assertThat(result.getHits().get(4).key()).isEqualTo("F1_2"); - assertThat(result.getHits().get(5).key()).isEqualTo("F1_1"); + result = index.search(query.build(), new SearchOptions()); + assertThat(result.getDocs()).hasSize(6); + assertThat(result.getDocs().get(0).key()).isEqualTo("F2_3"); + assertThat(result.getDocs().get(1).key()).isEqualTo("F2_2"); + assertThat(result.getDocs().get(2).key()).isEqualTo("F2_1"); + assertThat(result.getDocs().get(3).key()).isEqualTo("F1_3"); + assertThat(result.getDocs().get(4).key()).isEqualTo("F1_2"); + assertThat(result.getDocs().get(5).key()).isEqualTo("F1_1"); } @Test @@ -1013,22 +1007,20 @@ public class IssueIndexMediumTest { // project3 can be seen by nobody indexIssue(IssueTesting.newDoc("ISSUE3", file3), null, null); - IssueQuery.Builder query = IssueQuery.builder(); - MockUserSession.set().setUserGroups("sonar-users"); - assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(1); + assertThat(index.search(IssueQuery.builder().build(), new SearchOptions()).getDocs()).hasSize(1); MockUserSession.set().setUserGroups("sonar-admins"); - assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(1); + assertThat(index.search(IssueQuery.builder().build(), new SearchOptions()).getDocs()).hasSize(1); MockUserSession.set().setUserGroups("sonar-users", "sonar-admins"); - assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(2); + assertThat(index.search(IssueQuery.builder().build(), new SearchOptions()).getDocs()).hasSize(2); MockUserSession.set().setUserGroups("another group"); - assertThat(index.search(query.build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().build(), new SearchOptions()).getDocs()).isEmpty(); MockUserSession.set().setUserGroups("sonar-users", "sonar-admins"); - assertThat(index.search(query.projectUuids(newArrayList(project3.uuid())).build(), new QueryContext()).getHits()).isEmpty(); + assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList(project3.uuid())).build(), new SearchOptions()).getDocs()).isEmpty(); } @Test @@ -1046,19 +1038,18 @@ public class IssueIndexMediumTest { indexIssue(IssueTesting.newDoc("ISSUE2", file2), null, "max"); indexIssue(IssueTesting.newDoc("ISSUE3", file3), null, null); - IssueQuery.Builder query = IssueQuery.builder(); - MockUserSession.set().setLogin("john"); - assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(1); + + assertThat(index.search(IssueQuery.builder().build(), new SearchOptions()).getDocs()).hasSize(1); MockUserSession.set().setLogin("max"); - assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(1); + assertThat(index.search(IssueQuery.builder().build(), new SearchOptions()).getDocs()).hasSize(1); MockUserSession.set().setLogin("another guy"); - assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(0); + assertThat(index.search(IssueQuery.builder().build(), new SearchOptions()).getDocs()).hasSize(0); MockUserSession.set().setLogin("john"); - assertThat(index.search(query.projectUuids(newArrayList(project3.key())).build(), new QueryContext()).getHits()).hasSize(0); + assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList(project3.key())).build(), new SearchOptions()).getDocs()).hasSize(0); } @Test @@ -1075,7 +1066,7 @@ public class IssueIndexMediumTest { IssueQuery.Builder query = IssueQuery.builder(); MockUserSession.set().setLogin("john").setUserGroups("sonar-users"); - assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(1); + assertThat(index.search(query.build(), new SearchOptions()).getDocs()).hasSize(1); } @Test @@ -1091,17 +1082,22 @@ public class IssueIndexMediumTest { // Issue assigned to julien should not be returned as the issue is closed IssueTesting.newDoc("ISSUE5", file).setAssignee("julien").setStatus(Issue.STATUS_CLOSED)); - List results = index.listAssignees(IssueQuery.builder().statuses(newArrayList(Issue.STATUS_OPEN)).build()); - + LinkedHashMap results = index.searchForAssignees(IssueQuery.builder().statuses(newArrayList(Issue.STATUS_OPEN)).build()); assertThat(results).hasSize(3); - assertThat(results.get(0).getKey()).isEqualTo("steph"); - assertThat(results.get(0).getValue()).isEqualTo(2); - assertThat(results.get(1).getKey()).isEqualTo("simon"); - assertThat(results.get(1).getValue()).isEqualTo(1); + Iterator> buckets = results.entrySet().iterator(); - assertThat(results.get(2).getKey()).isEqualTo("_notAssigned_"); - assertThat(results.get(2).getValue()).isEqualTo(1); + Map.Entry bucket = buckets.next(); + assertThat(bucket.getKey()).isEqualTo("steph"); + assertThat(bucket.getValue()).isEqualTo(2L); + + bucket = buckets.next(); + assertThat(bucket.getKey()).isEqualTo("simon"); + assertThat(bucket.getValue()).isEqualTo(1L); + + bucket = buckets.next(); + assertThat(bucket.getKey()).isEqualTo("_notAssigned_"); + assertThat(bucket.getValue()).isEqualTo(1L); } @Test @@ -1123,9 +1119,9 @@ public class IssueIndexMediumTest { index.deleteClosedIssuesOfProjectBefore(project.uuid(), yesterday); // ASSERT - List issues = index.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())).build(), new QueryContext()).getHits(); + List issues = index.search(IssueQuery.builder().projectUuids(newArrayList(project.uuid())).build(), new SearchOptions()).getDocs(); List dates = newArrayList(); - for (Issue issue : issues) { + for (IssueDoc issue : issues) { dates.add(issue.closeDate()); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/AuthorsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/AuthorsActionTest.java index 33acb9a906c..c10978bb6ee 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/AuthorsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/AuthorsActionTest.java @@ -31,7 +31,6 @@ import org.sonar.server.ws.WsTester; import java.util.Arrays; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -47,9 +46,7 @@ public class AuthorsActionTest { @Before public void setUp() throws Exception { - tester = new WsTester(new IssuesWs(mock(IssueShowAction.class), new SearchAction(null, null, null, null, null, null, null, null, null, null, null), - mock(TagsAction.class), mock(SetTagsAction.class), mock(ComponentTagsAction.class), - new AuthorsAction(service))); + tester = new WsTester(new IssuesWs(new AuthorsAction(service))); controller = tester.controller("api/issues"); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/ComponentTagsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/ComponentTagsActionTest.java index 31bb581a730..5ea8d46672d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/ComponentTagsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/ComponentTagsActionTest.java @@ -49,10 +49,7 @@ public class ComponentTagsActionTest { @Before public void setUp() { componentTagsAction = new ComponentTagsAction(service); - tester = new WsTester( - new IssuesWs(new IssueShowAction(null, null, null, null, null, null, null, null, null, null, null), - new SearchAction(null, null, null, null, null, null, null, null, null, null,null), - new TagsAction(null), new SetTagsAction(null), componentTagsAction, new AuthorsAction(null))); + tester = new WsTester(new IssuesWs(componentTagsAction)); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java index 5dd1de61d0b..d730f222275 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java @@ -31,7 +31,6 @@ import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.DefaultIssueComment; import org.sonar.api.issue.internal.FieldDiffs; -import org.sonar.api.resources.Languages; import org.sonar.api.rule.RuleKey; import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic; import org.sonar.api.user.User; @@ -41,7 +40,6 @@ import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; import org.sonar.core.component.ComponentDto; import org.sonar.core.issue.DefaultActionPlan; -import org.sonar.core.issue.db.IssueChangeDao; import org.sonar.core.issue.workflow.Transition; import org.sonar.core.persistence.DbSession; import org.sonar.core.user.DefaultUser; @@ -49,7 +47,11 @@ import org.sonar.server.component.ComponentTesting; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.db.DbClient; import org.sonar.server.debt.DebtModelService; -import org.sonar.server.issue.*; +import org.sonar.server.issue.ActionService; +import org.sonar.server.issue.IssueChangelog; +import org.sonar.server.issue.IssueChangelogService; +import org.sonar.server.issue.IssueCommentService; +import org.sonar.server.issue.IssueService; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.rule.Rule; import org.sonar.server.rule.RuleService; @@ -141,11 +143,7 @@ public class IssueShowActionTest { tester = new WsTester(new IssuesWs( new IssueShowAction(dbClient, issueService, issueChangelogService, commentService, - new IssueActionsWriter(issueService, actionService), actionPlanService, userFinder, debtModel, ruleService, i18n, durations), - new SearchAction(mock(DbClient.class), mock(IssueChangeDao.class), mock(IssueService.class), mock(IssueActionsWriter.class), mock(IssueQueryService.class), - mock(RuleService.class), - mock(ActionPlanService.class), mock(UserFinder.class), mock(I18n.class), mock(Durations.class), mock(Languages.class)), - new TagsAction(null), new SetTagsAction(null), new ComponentTagsAction(null), new AuthorsAction(null) + new IssueActionsWriter(issueService, actionService), actionPlanService, userFinder, debtModel, ruleService, i18n, durations) )); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueTagsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueTagsActionTest.java index 45f97790bde..9519396292b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueTagsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueTagsActionTest.java @@ -47,10 +47,7 @@ public class IssueTagsActionTest { @Before public void setUp() { tagsAction = new TagsAction(service); - tester = new WsTester( - new IssuesWs(new IssueShowAction(null, null, null, null, null, null, null, null, null, null, null), - new SearchAction(null, null, null, null, null, null, null, null, null, null,null), - tagsAction, new SetTagsAction(null), new ComponentTagsAction(null), new AuthorsAction(null))); + tester = new WsTester(new IssuesWs(tagsAction)); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java index f3d0d3e73f9..e47f7fac894 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java @@ -19,242 +19,32 @@ */ package org.sonar.server.issue.ws; -import org.junit.Before; import org.junit.Test; -import org.sonar.api.i18n.I18n; -import org.sonar.api.resources.Languages; -import org.sonar.api.server.ws.RailsHandler; import org.sonar.api.server.ws.WebService; -import org.sonar.api.user.UserFinder; -import org.sonar.api.utils.Durations; -import org.sonar.core.issue.db.IssueChangeDao; -import org.sonar.server.db.DbClient; -import org.sonar.server.debt.DebtModelService; -import org.sonar.server.issue.IssueChangelogService; -import org.sonar.server.issue.IssueCommentService; -import org.sonar.server.issue.IssueQueryService; -import org.sonar.server.issue.IssueService; -import org.sonar.server.issue.actionplan.ActionPlanService; -import org.sonar.server.rule.RuleService; -import org.sonar.server.ws.WsTester; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; public class IssuesWsTest { - IssueShowAction showAction; - - WsTester tester; - - @Before - public void setUp() throws Exception { - IssueChangelogService issueChangelogService = mock(IssueChangelogService.class); - IssueActionsWriter actionsWriter = mock(IssueActionsWriter.class); - DebtModelService debtModelService = mock(DebtModelService.class); - I18n i18n = mock(I18n.class); - Durations durations = mock(Durations.class); - - showAction = new IssueShowAction(mock(DbClient.class), mock(IssueService.class), issueChangelogService, mock(IssueCommentService.class), actionsWriter, - mock(ActionPlanService.class), mock(UserFinder.class), - debtModelService, mock(RuleService.class), i18n, durations); - SearchAction searchAction = new SearchAction(mock(DbClient.class), mock(IssueChangeDao.class), mock(IssueService.class), mock(IssueActionsWriter.class), - mock(IssueQueryService.class), mock(RuleService.class), - mock(ActionPlanService.class), mock(UserFinder.class), mock(I18n.class), mock(Durations.class), mock(Languages.class)); - tester = new WsTester(new IssuesWs(showAction, searchAction, new TagsAction(null), new SetTagsAction(null), new ComponentTagsAction(null), new AuthorsAction(null))); - } - @Test - public void define_controller() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); + public void define_actions() throws Exception { + BaseIssuesWsAction action1 = mock(BaseIssuesWsAction.class); + BaseIssuesWsAction action2 = mock(BaseIssuesWsAction.class); + IssuesWs ws = new IssuesWs(action1, action2); + WebService.Context context = new WebService.Context(); + ws.define(context); + + WebService.Controller controller = context.controller("api/issues"); assertThat(controller).isNotNull(); assertThat(controller.description()).isNotEmpty(); assertThat(controller.since()).isEqualTo("3.6"); - assertThat(controller.actions()).hasSize(18); - } - - @Test - public void define_show_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("show"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("4.2"); - assertThat(action.isPost()).isFalse(); - assertThat(action.isInternal()).isTrue(); - assertThat(action.handler()).isSameAs(showAction); - assertThat(action.responseExampleAsString()).isNotEmpty(); - assertThat(action.params()).hasSize(1); - - WebService.Param key = action.param("key"); - assertThat(key).isNotNull(); - assertThat(key.description()).isNotNull(); - assertThat(key.isRequired()).isFalse(); - } - - @Test - public void define_changelog_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("changelog"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.responseExampleAsString()).isNotEmpty(); - assertThat(action.params()).hasSize(2); - } - - @Test - public void define_assign_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("assign"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("3.6"); - assertThat(action.isPost()).isTrue(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.handler()).isInstanceOf(RailsHandler.class); - assertThat(action.params()).hasSize(3); - } - - @Test - public void define_add_comment_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("add_comment"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("3.6"); - assertThat(action.isPost()).isTrue(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.handler()).isInstanceOf(RailsHandler.class); - assertThat(action.params()).hasSize(3); - } - - @Test - public void define_delete_comment_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("delete_comment"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("3.6"); - assertThat(action.isPost()).isTrue(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.handler()).isInstanceOf(RailsHandler.class); - assertThat(action.params()).hasSize(1); - } - - @Test - public void define_edit_comment_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("edit_comment"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("3.6"); - assertThat(action.isPost()).isTrue(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.handler()).isInstanceOf(RailsHandler.class); - assertThat(action.params()).hasSize(3); - } - - @Test - public void define_change_severity_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("set_severity"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("3.6"); - assertThat(action.isPost()).isTrue(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.handler()).isInstanceOf(RailsHandler.class); - assertThat(action.params()).hasSize(3); - } - - @Test - public void define_plan_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("plan"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("3.6"); - assertThat(action.isPost()).isTrue(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.handler()).isInstanceOf(RailsHandler.class); - assertThat(action.params()).hasSize(3); - } - - @Test - public void define_do_transition_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("do_transition"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("3.6"); - assertThat(action.isPost()).isTrue(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.handler()).isInstanceOf(RailsHandler.class); - assertThat(action.params()).hasSize(3); - } - - @Test - public void define_transitions_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("transitions"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("3.6"); - assertThat(action.isPost()).isFalse(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.handler()).isInstanceOf(RailsHandler.class); - assertThat(action.responseExampleAsString()).isNotEmpty(); - assertThat(action.params()).hasSize(1); - } - - @Test - public void define_create_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("create"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("3.6"); - assertThat(action.isPost()).isTrue(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.params()).hasSize(6); - } - - @Test - public void define_do_action_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - - WebService.Action action = controller.action("do_action"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("3.6"); - assertThat(action.isPost()).isTrue(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.params()).hasSize(3); - } - - @Test - public void define_bulk_change_action() throws Exception { - WebService.Controller controller = tester.controller("api/issues"); - WebService.Action action = controller.action("bulk_change"); - assertThat(action).isNotNull(); - assertThat(action.handler()).isNotNull(); - assertThat(action.since()).isEqualTo("3.7"); - assertThat(action.isPost()).isTrue(); - assertThat(action.isInternal()).isFalse(); - assertThat(action.handler()).isInstanceOf(RailsHandler.class); - assertThat(action.params()).hasSize(9); + assertThat(controller.actions()).isNotEmpty(); + verify(action1).define(any(WebService.NewController.class)); + verify(action2).define(any(WebService.NewController.class)); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java index 5a50b56856c..324210099d5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.sonar.api.resources.Qualifiers; import org.sonar.api.rule.RuleStatus; import org.sonar.api.security.DefaultGroups; +import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.DateUtils; import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; @@ -158,13 +159,13 @@ public class SearchActionComponentsMediumTest { wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION) .setParam(IssueFilterParameters.PROJECT_UUIDS, project1.uuid()) - .setParam(SearchAction.PARAM_FACETS, "projectUuids") + .setParam(WebService.Param.FACETS, "projectUuids") .execute() .assertJson(this.getClass(), "display_sticky_project_facet.json", false); wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION) .setParam(IssueFilterParameters.COMPONENT_UUIDS, project1.uuid()) - .setParam(SearchAction.PARAM_FACETS, "projectUuids") + .setParam(WebService.Param.FACETS, "projectUuids") .execute() .assertJson(this.getClass(), "display_non_sticky_project_facet.json", false); } @@ -242,7 +243,7 @@ public class SearchActionComponentsMediumTest { wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION) .setParam(IssueFilterParameters.COMPONENT_UUIDS, project.uuid()) .setParam(IssueFilterParameters.FILE_UUIDS, file1.uuid() + "," + file3.uuid()) - .setParam(SearchAction.PARAM_FACETS, "fileUuids") + .setParam(WebService.Param.FACETS, "fileUuids") .execute() .assertJson(this.getClass(), "display_file_facet.json", false); } @@ -349,7 +350,7 @@ public class SearchActionComponentsMediumTest { wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION) .setParam(IssueFilterParameters.COMPONENT_UUIDS, project.uuid()) .setParam(IssueFilterParameters.MODULE_UUIDS, subModule1.uuid() + "," + subModule3.uuid()) - .setParam(SearchAction.PARAM_FACETS, "moduleUuids") + .setParam(WebService.Param.FACETS, "moduleUuids") .execute() .assertJson(this.getClass(), "display_module_facet.json", false); } @@ -368,7 +369,7 @@ public class SearchActionComponentsMediumTest { MockUserSession.set().setLogin("john"); WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION) .setParam("resolved", "false") - .setParam(SearchAction.PARAM_FACETS, "directories") + .setParam(WebService.Param.FACETS, "directories") .execute(); result.assertJson(this.getClass(), "display_directory_facet.json", false); } @@ -469,7 +470,7 @@ public class SearchActionComponentsMediumTest { wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION) .setParam(IssueFilterParameters.AUTHORS, "leia") - .setParam(SearchAction.PARAM_FACETS, "authors") + .setParam(WebService.Param.FACETS, "authors") .execute() .assertJson(this.getClass(), "search_by_authors.json", false); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java index 8111464b588..d1bf870bfdc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java @@ -88,7 +88,7 @@ public class SearchActionMediumTest { assertThat(show.isPost()).isFalse(); assertThat(show.isInternal()).isFalse(); assertThat(show.responseExampleAsString()).isNotEmpty(); - assertThat(show.params()).hasSize(39); + assertThat(show.params()).hasSize(38); } @Test @@ -367,7 +367,7 @@ public class SearchActionMediumTest { MockUserSession.set().setLogin("john"); WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION) .setParam("resolved", "false") - .setParam(SearchAction.PARAM_FACETS, "statuses,severities,resolutions,projectUuids,rules,fileUuids,assignees,languages,actionPlans") + .setParam(WebService.Param.FACETS, "statuses,severities,resolutions,projectUuids,rules,fileUuids,assignees,languages,actionPlans") .execute(); result.assertJson(this.getClass(), "display_facets.json", false); } @@ -393,7 +393,7 @@ public class SearchActionMediumTest { .setParam("resolved", "false") .setParam("severities", "MAJOR,MINOR") .setParam("languages", "xoo,polop,palap") - .setParam(SearchAction.PARAM_FACETS, "statuses,severities,resolutions,projectUuids,rules,fileUuids,assignees,languages,actionPlans") + .setParam(WebService.Param.FACETS, "statuses,severities,resolutions,projectUuids,rules,fileUuids,assignees,languages,actionPlans") .execute(); result.assertJson(this.getClass(), "display_zero_facets.json", false); } @@ -451,8 +451,8 @@ public class SearchActionMediumTest { tester.get(IssueIndexer.class).indexAll(); WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION); - request.setParam(SearchAction.PARAM_PAGE, "2"); - request.setParam(SearchAction.PARAM_PAGE_SIZE, "9"); + request.setParam(WebService.Param.PAGE, "2"); + request.setParam(WebService.Param.PAGE_SIZE, "9"); WsTester.Result result = request.execute(); result.assertJson(this.getClass(), "paging.json", false); @@ -472,8 +472,8 @@ public class SearchActionMediumTest { tester.get(IssueIndexer.class).indexAll(); WsTester.TestRequest request = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION); - request.setParam(SearchAction.PARAM_PAGE, "1"); - request.setParam(SearchAction.PARAM_PAGE_SIZE, "-1"); + request.setParam(WebService.Param.PAGE, "1"); + request.setParam(WebService.Param.PAGE_SIZE, "-1"); WsTester.Result result = request.execute(); result.assertJson(this.getClass(), "paging_with_page_size_to_minus_one.json", false); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java index 2dd3a9f2bf3..7ccfb597930 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java @@ -19,6 +19,7 @@ */ package org.sonar.server.issue.ws; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import org.junit.Before; import org.junit.Test; @@ -38,19 +39,14 @@ import static org.mockito.Mockito.when; public class SetTagsActionTest { @Mock - private IssueService service; - - private SetTagsAction setTagsAction; - - private WsTester tester; + IssueService service; + SetTagsAction sut; + WsTester tester; @Before public void setUp() { - setTagsAction = new SetTagsAction(service); - tester = new WsTester( - new IssuesWs(new IssueShowAction(null, null, null, null, null, null, null, null, null, null, null), - new SearchAction(null, null, null, null, null, null, null, null, null, null,null), - new TagsAction(null), setTagsAction, new ComponentTagsAction(null), new AuthorsAction(null))); + sut = new SetTagsAction(service); + tester = new WsTester(new IssuesWs(sut)); } @Test @@ -60,7 +56,7 @@ public class SetTagsActionTest { assertThat(action.responseExampleAsString()).isNull(); assertThat(action.isPost()).isTrue(); assertThat(action.isInternal()).isFalse(); - assertThat(action.handler()).isEqualTo(setTagsAction); + assertThat(action.handler()).isEqualTo(sut); assertThat(action.params()).hasSize(2); Param query = action.param("key"); @@ -76,9 +72,9 @@ public class SetTagsActionTest { @Test public void should_set_tags() throws Exception { - when(service.setTags("polop", ImmutableSet.of("palap"))).thenReturn(ImmutableSet.of("palap")); + when(service.setTags("polop", ImmutableList.of("palap"))).thenReturn(ImmutableSet.of("palap")); tester.newPostRequest("api/issues", "set_tags").setParam("key", "polop").setParam("tags", "palap").execute() .assertJson("{\"tags\":[\"palap\"]}"); - verify(service).setTags("polop", ImmutableSet.of("palap")); + verify(service).setTags("polop", ImmutableList.of("palap")); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/view/index/ViewIndexerMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/view/index/ViewIndexerMediumTest.java index ff806db5e70..3f80f02f695 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/view/index/ViewIndexerMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/view/index/ViewIndexerMediumTest.java @@ -34,16 +34,18 @@ import org.sonar.core.rule.RuleDto; import org.sonar.server.component.ComponentTesting; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.db.DbClient; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; import org.sonar.server.issue.IssueQuery; import org.sonar.server.issue.IssueTesting; import org.sonar.server.issue.db.IssueDao; +import org.sonar.server.issue.index.IssueDoc; import org.sonar.server.issue.index.IssueIndex; import org.sonar.server.issue.index.IssueIndexer; import org.sonar.server.permission.InternalPermissionService; import org.sonar.server.permission.PermissionChange; import org.sonar.server.rule.RuleTesting; import org.sonar.server.rule.db.RuleDao; -import org.sonar.server.search.QueryContext; import org.sonar.server.tester.ServerTester; import org.sonar.server.user.MockUserSession; @@ -97,7 +99,9 @@ public class ViewIndexerMediumTest { indexer.index(viewUuid); // Execute issue query on view -> 1 issue on view - assertThat(tester.get(IssueIndex.class).search(IssueQuery.builder().viewUuids(newArrayList(viewUuid)).build(), new QueryContext()).getHits()).hasSize(1); + SearchResult docs = tester.get(IssueIndex.class).search(IssueQuery.builder().viewUuids(newArrayList(viewUuid)).build(), + new SearchOptions()); + assertThat(docs.getDocs()).hasSize(1); // Add a project to the view and index it again ComponentDto project2 = addProjectWithIssue(rule); @@ -107,7 +111,7 @@ public class ViewIndexerMediumTest { indexer.index(viewUuid); // Execute issue query on view -> issue of project2 are well taken into account : the cache has been cleared - assertThat(tester.get(IssueIndex.class).search(IssueQuery.builder().viewUuids(newArrayList(viewUuid)).build(), new QueryContext()).getHits()).hasSize(2); + assertThat(tester.get(IssueIndex.class).search(IssueQuery.builder().viewUuids(newArrayList(viewUuid)).build(), new SearchOptions()).getDocs()).hasSize(2); } private ComponentDto addProjectWithIssue(RuleDto rule) { diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/default_page_size_is_100.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/default_page_size_is_100.json index 0fc07a00b50..a4dc9a53ed5 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/default_page_size_is_100.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/default_page_size_is_100.json @@ -2,7 +2,6 @@ "total": 0, "p": 1, "ps": 100, - "maxResultsReached": false, "paging": { "pageIndex": 1, "pageSize": 100, diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/deprecated_paging.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/deprecated_paging.json index f67485819f6..1ecb54f2622 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/deprecated_paging.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/deprecated_paging.json @@ -1,5 +1,4 @@ { - "maxResultsReached": false, "paging": { "pageIndex": 2, "pageSize": 9, diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/empty_result.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/empty_result.json index 4d91f3e640f..787b0028a29 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/empty_result.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/empty_result.json @@ -2,7 +2,6 @@ "total": 0, "p": 1, "ps": 100, - "maxResultsReached": false, "paging": { "pageIndex": 1, "pageSize": 100, diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue.json index ece88f7ed43..ee02f787d29 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/issue.json @@ -1,5 +1,4 @@ { - "maxResultsReached": false, "total": 1, "p": 1, "ps": 100, diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java index b236a18b366..a766d024592 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java @@ -26,6 +26,7 @@ import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; import org.sonar.api.issue.internal.DefaultIssueComment; import org.sonar.api.issue.internal.FieldDiffs; +import org.sonar.core.persistence.DaoComponent; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; @@ -43,7 +44,7 @@ import static com.google.common.collect.Maps.newHashMap; /** * @since 3.6 */ -public class IssueChangeDao implements BatchComponent, ServerComponent { +public class IssueChangeDao implements DaoComponent, BatchComponent, ServerComponent { private final MyBatis mybatis; diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java index ac41ee8639e..3d4690c2f0e 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java @@ -23,7 +23,6 @@ import org.apache.ibatis.annotations.Param; import org.sonar.core.rule.RuleDto; import javax.annotation.Nullable; -import java.util.Collection; import java.util.Date; import java.util.List; @@ -31,7 +30,7 @@ public interface IssueMapper { IssueDto selectByKey(String key); - List selectByKeys(Collection keys); + List selectByKeys(List keys); List selectByActionPlan(String actionPlan); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java index f8bffedc019..faa0f05fb56 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java @@ -35,7 +35,11 @@ import javax.annotation.concurrent.Immutable; import java.io.IOException; import java.net.URL; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Defines a web service. Note that contrary to the deprecated {@link org.sonar.api.web.Webservice} @@ -117,8 +121,7 @@ public interface WebService extends ServerExtension { private void register(NewController newController) { if (controllers.containsKey(newController.path)) { throw new IllegalStateException( - String.format("The web service '%s' is defined multiple times", newController.path) - ); + String.format("The web service '%s' is defined multiple times", newController.path)); } controllers.put(newController.path, new Controller(newController)); } @@ -177,8 +180,7 @@ public interface WebService extends ServerExtension { public NewAction createAction(String actionKey) { if (actions.containsKey(actionKey)) { throw new IllegalStateException( - String.format("The action '%s' is defined multiple times in the web service '%s'", actionKey, path) - ); + String.format("The action '%s' is defined multiple times in the web service '%s'", actionKey, path)); } NewAction action = new NewAction(actionKey); actions.put(actionKey, action); @@ -194,8 +196,7 @@ public interface WebService extends ServerExtension { private Controller(NewController newController) { if (newController.actions.isEmpty()) { throw new IllegalStateException( - String.format("At least one action must be declared in the web service '%s'", newController.path) - ); + String.format("At least one action must be declared in the web service '%s'", newController.path)); } this.path = newController.path; this.description = newController.description; @@ -306,8 +307,7 @@ public interface WebService extends ServerExtension { public NewParam createParam(String paramKey) { if (newParams.containsKey(paramKey)) { throw new IllegalStateException( - String.format("The parameter '%s' is defined multiple times in the action '%s'", paramKey, key) - ); + String.format("The parameter '%s' is defined multiple times in the action '%s'", paramKey, key)); } NewParam newParam = new NewParam(paramKey); newParams.put(paramKey, newParam); @@ -322,6 +322,52 @@ public interface WebService extends ServerExtension { createParam(paramKey).setDescription(description); return this; } + + /** + * Add predefined parameters related to pagination of results. + */ + public NewAction addPagingParams(int defaultPageSize) { + createParam(Param.PAGE) + .setDescription("1-based page number") + .setExampleValue("42") + .setDeprecatedKey("pageIndex") + .setDefaultValue("1"); + + createParam(Param.PAGE_SIZE) + .setDescription("Page size. Must be greater than 0.") + .setExampleValue("20") + .setDeprecatedKey("pageSize") + .setDefaultValue(String.valueOf(defaultPageSize)); + return this; + } + + /** + * Creates the parameter {@link org.sonar.api.server.ws.WebService.Param#FIELDS}, which is + * used to restrict the number of fields returned in JSON response. + */ + public NewAction addFieldsParam(Collection possibleValues) { + createParam(Param.FIELDS) + .setDescription("Comma-separated list of the fields to be returned in response. All the fields are returned by default.") + .setPossibleValues(possibleValues); + return this; + } + + /** + * Add predefined parameters related to sorting of results. + */ + public NewAction addSortParams(Collection possibleValues, @Nullable V defaultValue, boolean defaultAscending) { + createParam(Param.SORT) + .setDescription("Sort field") + .setDeprecatedKey("sort") + .setDefaultValue(defaultValue) + .setPossibleValues(possibleValues); + + createParam(Param.ASCENDING) + .setDescription("Ascending sort") + .setBooleanPossibleValues() + .setDefaultValue(defaultAscending); + return this; + } } @Immutable @@ -531,6 +577,14 @@ public interface WebService extends ServerExtension { @Immutable class Param { + public static final String TEXT_QUERY = "q"; + public static final String PAGE = "p"; + public static final String PAGE_SIZE = "ps"; + public static final String FIELDS = "f"; + public static final String SORT = "s"; + public static final String ASCENDING = "asc"; + public static final String FACETS = "facets"; + private final String key, deprecatedKey, description, exampleValue, defaultValue; private final boolean required; private final Set possibleValues; diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java index 550b959019f..9226ea1af60 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/ws/WebServiceTest.java @@ -25,6 +25,7 @@ import org.sonar.api.rule.RuleStatus; import java.net.MalformedURLException; import java.net.URL; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -257,15 +258,18 @@ public class WebServiceTest { @Override public void define(Context context) { NewController newController = context.createController("api/rule"); - NewAction create = newController.createAction("create").setHandler(mock(RequestHandler.class)); - create.createParam("key").setDescription("Key of the new rule"); - create.createParam("severity").setDefaultValue("MAJOR").setPossibleValues("INFO", "MAJOR", "BLOCKER"); + NewAction newAction = newController.createAction("create").setHandler(mock(RequestHandler.class)); + newAction.createParam("key").setDescription("Key of the new rule"); + newAction.createParam("severity").setDefaultValue("MAJOR").setPossibleValues("INFO", "MAJOR", "BLOCKER"); + newAction.addPagingParams(20); + newAction.addFieldsParam(Arrays.asList("name", "severity")); + newAction.addSortParams(Arrays.asList("name", "updatedAt", "severity"), "updatedAt", false); newController.done(); } }.define(context); WebService.Action action = context.controller("api/rule").action("create"); - assertThat(action.params()).hasSize(2); + assertThat(action.params()).hasSize(7); assertThat(action.param("key").key()).isEqualTo("key"); assertThat(action.param("key").description()).isEqualTo("Key of the new rule"); @@ -275,6 +279,16 @@ public class WebServiceTest { assertThat(action.param("severity").description()).isNull(); assertThat(action.param("severity").defaultValue()).isEqualTo("MAJOR"); assertThat(action.param("severity").possibleValues()).containsOnly("INFO", "MAJOR", "BLOCKER"); + + // predefined fields + assertThat(action.param("p").defaultValue()).isEqualTo("1"); + assertThat(action.param("p").description()).isNotEmpty(); + assertThat(action.param("ps").defaultValue()).isEqualTo("20"); + assertThat(action.param("ps").description()).isNotEmpty(); + assertThat(action.param("f").possibleValues()).containsOnly("name", "severity"); + assertThat(action.param("s").possibleValues()).containsOnly("name", "severity", "updatedAt"); + assertThat(action.param("s").description()).isNotEmpty(); + assertThat(action.param("asc").defaultValue()).isEqualTo("false"); } @Test -- cgit v1.2.3 From 3dcf41ab1127fe59b943c1e79f173fcbd45e9e6c Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 9 Feb 2015 12:19:59 +0100 Subject: Add missing jdbcType in UserMapper --- sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sonar-core') diff --git a/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml b/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml index 16b370594d7..42a16e13139 100644 --- a/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml @@ -135,11 +135,13 @@ INSERT INTO users (login, name, email, active, scm_accounts, salt, crypted_password, created_at, updated_at) - VALUES (#{login}, #{name}, #{email}, #{active}, #{scmAccounts}, #{salt}, #{cryptedPassword}, #{createdAt}, #{updatedAt}) + VALUES (#{login,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{active,jdbcType=BOOLEAN}, #{scmAccounts,jdbcType=VARCHAR}, + #{salt,jdbcType=VARCHAR}, #{cryptedPassword,jdbcType=VARCHAR}, #{createdAt,jdbcType=BIGINT}, #{updatedAt,jdbcType=BIGINT}) - UPDATE users set name=#{name}, email=#{email}, active=#{active}, scm_accounts=#{scmAccounts}, salt=#{salt}, crypted_password=#{cryptedPassword}, updated_at=#{updatedAt} + UPDATE users set name=#{name,jdbcType=VARCHAR}, email=#{email,jdbcType=VARCHAR}, active=#{active,jdbcType=BOOLEAN}, scm_accounts=#{scmAccounts,jdbcType=VARCHAR}, + salt=#{salt,jdbcType=VARCHAR}, crypted_password=#{cryptedPassword,jdbcType=BIGINT}, updated_at=#{updatedAt,jdbcType=BIGINT} WHERE login = #{login} -- cgit v1.2.3 From 5879a094d888e93a7ee4cbe4ed9d4c0ce0c23ac0 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Wed, 4 Feb 2015 09:24:23 +0100 Subject: SONAR-5183 timezones - migrate issues table from timestamp to long --- .../server/db/migrations/DatabaseMigrations.java | 3 +- .../org/sonar/server/db/migrations/SqlUtil.java | 1 + .../db/migrations/v51/FeedIssuesLongDates.java | 65 ++++ .../server/issue/index/IssueResultSetIterator.java | 39 ++- .../db/migrations/v51/FeedIssuesLongDatesTest.java | 81 +++++ .../return_only_manual_severity.xml | 6 +- .../sonar/server/batch/IssuesActionTest/shared.xml | 6 +- .../v51/FeedIssuesLongDatesTest/before.xml | 34 ++ .../v51/FeedIssuesLongDatesTest/schema.sql | 9 + .../should_insert_new_issues-result.xml | 6 +- .../should_update_issues-result.xml | 6 +- .../should_update_issues.xml | 4 +- .../issue/db/IssueDaoTest/find_by_action_plan.xml | 6 +- .../server/issue/db/IssueDaoTest/get_by_key.xml | 6 +- .../server/issue/db/IssueDaoTest/insert-result.xml | 6 +- .../select_non_closed_issues_by_module_uuid.xml | 36 +- .../select_non_closed_issues_by_project_uuid.xml | 36 +- .../server/issue/db/IssueDaoTest/update-result.xml | 6 +- .../server/issue/index/IssueIndexerTest/index.xml | 4 +- .../extract_directory_path.xml | 12 +- .../index/IssueResultSetIteratorTest/shared.xml | 8 +- .../db/migrate/778_add_issues_long_dates.rb | 30 ++ .../db/migrate/779_feed_issues_long_dates.rb | 29 ++ .../db/migrate/780_rename_issues_long_dates.rb | 36 ++ .../java/org/sonar/batch/DefaultTimeMachine.java | 4 +- .../should_insert_new_issues-result.xml | 6 +- .../should_resolve_conflicts_on_updates-result.xml | 4 +- .../should_resolve_conflicts_on_updates.xml | 4 +- .../should_update_issues-result.xml | 6 +- .../ScanIssueStorageTest/should_update_issues.xml | 4 +- .../org/sonar/core/issue/db/BatchIssueDto.java | 22 +- .../java/org/sonar/core/issue/db/IssueDao.java | 7 +- .../java/org/sonar/core/issue/db/IssueDto.java | 383 +++++++++++---------- .../java/org/sonar/core/issue/db/IssueMapper.java | 4 +- .../sonar/core/persistence/DatabaseVersion.java | 2 +- .../main/java/org/sonar/core/purge/PurgeDao.java | 14 +- .../java/org/sonar/core/purge/PurgeMapper.java | 7 +- .../org/sonar/core/issue/db/IssueMapper.xml | 104 +++--- .../org/sonar/core/persistence/rows-h2.sql | 3 + .../org/sonar/core/persistence/schema-h2.ddl | 6 +- .../resources/org/sonar/core/purge/PurgeMapper.xml | 4 +- .../should_find_by_project.xml | 18 +- .../select_issue_changelog_by_module.xml | 30 +- ...gelog_by_module_are_sorted_by_creation_date.xml | 6 +- .../db/IssueDaoTest/find_rules_by_component.xml | 18 +- .../IssueDaoTest/find_severities_by_component.xml | 18 +- .../issue/db/IssueDaoTest/should_select_by_key.xml | 6 +- .../should_select_issue_and_component_ids.xml | 18 +- .../should_select_non_closed_issues_by_module.xml | 30 +- ..._closed_issues_by_module_on_removed_project.xml | 24 +- .../issue/db/IssueMapperTest/testInsert-result.xml | 6 +- .../issue/db/IssueMapperTest/testUpdate-result.xml | 6 +- .../IssueStatsDaoTest/should_select_assignees.xml | 18 +- .../should_insert_new_issues-result.xml | 6 +- .../should_resolve_conflicts_on_updates-result.xml | 4 +- .../should_resolve_conflicts_on_updates.xml | 4 +- .../should_update_issues-result.xml | 6 +- .../db/IssueStorageTest/should_update_issues.xml | 4 +- .../multi-modules-with-issues.xml | 12 +- .../should_create_database_with_issues.xml | 12 +- .../PurgeCommandsTest/shouldDeleteResource.xml | 6 +- ...able_resources_without_last_snapshot-result.xml | 16 +- .../disable_resources_without_last_snapshot.xml | 10 +- .../purge/PurgeDaoTest/shouldDeleteProject.xml | 12 +- .../should_delete_all_closed_issues-result.xml | 16 +- .../should_delete_all_closed_issues.xml | 16 +- .../should_delete_old_closed_issues-result.xml | 16 +- .../should_delete_old_closed_issues.xml | 16 +- .../main/java/org/sonar/api/utils/DateUtils.java | 1 + .../java/org/sonar/api/utils/DateUtilsTest.java | 9 +- 70 files changed, 885 insertions(+), 538 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDates.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest/before.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest/schema.sql create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/778_add_issues_long_dates.rb create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/779_feed_issues_long_dates.rb create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/780_rename_issues_long_dates.rb (limited to 'sonar-core') diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java index 656564d46e8..d61f389ca5e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java @@ -85,6 +85,7 @@ public interface DatabaseMigrations { FeedAnalysisReportsLongDates.class, UpdateProjectsModuleUuidPath.class, FeedIssueComponentUuids.class, - FeedSnapshotsLongDates.class + FeedSnapshotsLongDates.class, + FeedIssuesLongDates.class ); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SqlUtil.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SqlUtil.java index 1dcf7b0339a..0a02368b43d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SqlUtil.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/SqlUtil.java @@ -94,6 +94,7 @@ public class SqlUtil { return rs.wasNull() ? null : s; } + @CheckForNull public static Date getDate(ResultSet rs, int columnIndex) throws SQLException { Timestamp t = rs.getTimestamp(columnIndex); return rs.wasNull() ? null : new Date(t.getTime()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDates.java new file mode 100644 index 00000000000..5734005ed6f --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDates.java @@ -0,0 +1,65 @@ +/* + * 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.db.migrations.v51; + +import org.sonar.api.utils.System2; +import org.sonar.core.persistence.Database; +import org.sonar.server.db.migrations.BaseDataChange; +import org.sonar.server.db.migrations.MassUpdate; +import org.sonar.server.db.migrations.Select; +import org.sonar.server.db.migrations.SqlStatement; + +import java.sql.SQLException; + +public class FeedIssuesLongDates extends BaseDataChange { + + private final System2 system2; + + public FeedIssuesLongDates(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + public void execute(Context context) throws SQLException { + final long now = system2.now(); + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate + .select("SELECT i.issue_creation_date, i.issue_update_date, i.issue_close_date, i.id FROM issues i WHERE issue_creation_date_ms IS NULL"); + massUpdate + .update("UPDATE issues SET issue_creation_date_ms=?, issue_update_date_ms=?, issue_close_date_ms=? WHERE id=?"); + massUpdate.rowPluralName("issues"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + for (int i = 1; i <= 3; i++) { + update.setLong(i, row.getDate(i) == null ? null : Math.min(now, row.getDate(i).getTime())); + } + + Long id = row.getLong(4); + update.setLong(4, id); + + return true; + } + }); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueResultSetIterator.java index b305f35c257..41f6db28ede 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueResultSetIterator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueResultSetIterator.java @@ -38,6 +38,9 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; +import static org.sonar.api.utils.DateUtils.longToDate; +import static org.sonar.server.db.migrations.SqlUtil.getLong; + /** * Scrolls over table ISSUES and reads documents to populate * the issues index @@ -87,6 +90,10 @@ class IssueResultSetIterator extends ResultSetIterator { private static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); + private IssueResultSetIterator(PreparedStatement stmt) throws SQLException { + super(stmt); + } + static IssueResultSetIterator create(DbClient dbClient, Connection connection, long afterDate) { try { String sql = afterDate > 0L ? SQL_AFTER_DATE : SQL_ALL; @@ -100,8 +107,16 @@ class IssueResultSetIterator extends ResultSetIterator { } } - private IssueResultSetIterator(PreparedStatement stmt) throws SQLException { - super(stmt); + @CheckForNull + private static String extractDirPath(@Nullable String filePath) { + if (filePath != null) { + int lastSlashIndex = CharMatcher.anyOf("/").lastIndexIn(filePath); + if (lastSlashIndex > 0) { + return filePath.substring(0, lastSlashIndex); + } + return "/"; + } + return null; } @Override @@ -124,12 +139,12 @@ class IssueResultSetIterator extends ResultSetIterator { doc.setResolution(rs.getString(10)); doc.setSeverity(rs.getString(11)); doc.setStatus(rs.getString(12)); - doc.setDebt(SqlUtil.getLong(rs, 13)); + doc.setDebt(getLong(rs, 13)); doc.setReporter(rs.getString(14)); doc.setAuthorLogin(rs.getString(15)); - doc.setFuncCloseDate(SqlUtil.getDate(rs, 16)); - doc.setFuncCreationDate(SqlUtil.getDate(rs, 17)); - doc.setFuncUpdateDate(SqlUtil.getDate(rs, 18)); + doc.setFuncCloseDate(longToDate(getLong(rs, 16))); + doc.setFuncCreationDate(longToDate(getLong(rs, 17))); + doc.setFuncUpdateDate(longToDate(getLong(rs, 18))); String ruleRepo = rs.getString(19); String ruleKey = rs.getString(20); doc.setRuleKey(RuleKey.of(ruleRepo, ruleKey).toString()); @@ -143,16 +158,4 @@ class IssueResultSetIterator extends ResultSetIterator { doc.setTags(ImmutableList.copyOf(TAGS_SPLITTER.split(tags == null ? "" : tags))); return doc; } - - @CheckForNull - private static String extractDirPath(@Nullable String filePath) { - if (filePath != null) { - int lastSlashIndex = CharMatcher.anyOf("/").lastIndexIn(filePath); - if (lastSlashIndex > 0) { - return filePath.substring(0, lastSlashIndex); - } - return "/"; - } - return null; - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest.java new file mode 100644 index 00000000000..e0d3c6564e5 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest.java @@ -0,0 +1,81 @@ +/* + * 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.db.migrations.v51; + +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.db.migrations.DatabaseMigration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.api.utils.DateUtils.parseDate; + +public class FeedIssuesLongDatesTest { + @ClassRule + public static DbTester db = new DbTester().schema(FeedIssuesLongDatesTest.class, "schema.sql"); + + @Test + public void execute() throws Exception { + db.prepareDbUnit(getClass(), "before.xml"); + + DatabaseMigration migration = new FeedIssuesLongDates(db.database(), System2.INSTANCE); + migration.execute(); + + int count = db + .countSql("select count(*) from issues where " + + "issue_creation_date_ms is not null " + + "and issue_update_date_ms is not null " + + "and issue_close_date_ms is not null"); + assertThat(count).isEqualTo(2); + } + + @Test + public void take_now_if_date_in_the_future() throws Exception { + db.prepareDbUnit(getClass(), "before.xml"); + System2 system2 = mock(System2.class); + when(system2.now()).thenReturn(0L); + + DatabaseMigration migration = new FeedIssuesLongDates(db.database(), mock(System2.class)); + migration.execute(); + + int count = db + .countSql("select count(*) from issues where " + + "issue_creation_date_ms = 0"); + assertThat(count).isEqualTo(1); + } + + @Test + public void take_snapshot_date_if_in_the_past() throws Exception { + db.prepareDbUnit(getClass(), "before.xml"); + long snapshotTime = parseDate("2014-09-25").getTime(); + + DatabaseMigration migration = new FeedIssuesLongDates(db.database(), System2.INSTANCE); + migration.execute(); + + int count = db + .countSql("select count(*) from issues where " + + "issue_creation_date_ms=" + snapshotTime); + assertThat(count).isEqualTo(1); + } +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/return_only_manual_severity.xml b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/return_only_manual_severity.xml index c6aae18497d..e9067a6ca97 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/return_only_manual_severity.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/return_only_manual_severity.xml @@ -28,9 +28,9 @@ assignee="john" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml index 12bf28931c0..cdeb8b848f7 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml @@ -31,9 +31,9 @@ assignee="john" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest/before.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest/before.xml new file mode 100644 index 00000000000..96b11d118ae --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest/before.xml @@ -0,0 +1,34 @@ + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest/schema.sql new file mode 100644 index 00000000000..ab749851864 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedIssuesLongDatesTest/schema.sql @@ -0,0 +1,9 @@ +CREATE TABLE "ISSUES" ( + "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "ISSUE_CREATION_DATE" TIMESTAMP, + "ISSUE_CREATION_DATE_MS" BIGINT, + "ISSUE_CLOSE_DATE" TIMESTAMP, + "ISSUE_CLOSE_DATE_MS" BIGINT, + "ISSUE_UPDATE_DATE" TIMESTAMP, + "ISSUE_UPDATE_DATE_MS" BIGINT, +); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_insert_new_issues-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_insert_new_issues-result.xml index 88c1fd25494..40fc2fb5a22 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_insert_new_issues-result.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ServerIssueStorageTest/should_insert_new_issues-result.xml @@ -16,9 +16,9 @@ issue_attributes="foo=bar" tags="[null]" action_plan_key="[null]" - issue_creation_date="2013-05-18" - issue_update_date="2013-05-18" - issue_close_date="2013-05-18" + issue_creation_date="1368828000000" + issue_update_date="1368828000000" + issue_close_date="1368828000000" /> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/find_by_action_plan.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/find_by_action_plan.xml index 6d676713435..a052d421f66 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/find_by_action_plan.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/find_by_action_plan.xml @@ -19,9 +19,9 @@ action_plan_key="AP-1" author_login="karadoc" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1400000000000" /> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml index fef3faebe50..154a7a67883 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml @@ -18,9 +18,9 @@ assignee="perceval" author_login="karadoc" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1450000000000" /> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml index c661584c6d3..8827644b2c8 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml @@ -19,9 +19,9 @@ assignee="karadoc" issue_attributes="JIRA=FOO-1234" tags="[null]" - issue_creation_date="2013-05-18" - issue_update_date="2013-05-19" - issue_close_date="2013-05-20" + issue_creation_date="1368828000000" + issue_update_date="1368914400000" + issue_close_date="1369000800000" created_at="1400000000000" updated_at="1450000000000" action_plan_key="current_sprint" diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_module_uuid.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_module_uuid.xml index 7ea5e75f18c..05aa79b26ad 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_module_uuid.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_module_uuid.xml @@ -19,9 +19,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -45,9 +45,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -71,9 +71,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -97,9 +97,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -123,9 +123,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -149,9 +149,9 @@ assignee="john" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_project_uuid.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_project_uuid.xml index 7ea5e75f18c..05aa79b26ad 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_project_uuid.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_project_uuid.xml @@ -19,9 +19,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -45,9 +45,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -71,9 +71,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -97,9 +97,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -123,9 +123,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -149,9 +149,9 @@ assignee="john" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml index ba77751eb07..d5959c63659 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml @@ -19,9 +19,9 @@ assignee="karadoc" issue_attributes="JIRA=FOO-1234" tags="[null]" - issue_creation_date="2013-05-18" - issue_update_date="2013-05-19" - issue_close_date="2013-05-20" + issue_creation_date="1368828000000" + issue_update_date="1368914400000" + issue_close_date="1369000800000" created_at="1400000000000" updated_at="1450000000000" action_plan_key="current_sprint" diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIndexerTest/index.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIndexerTest/index.xml index fc04143b522..4b946cdf306 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIndexerTest/index.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueIndexerTest/index.xml @@ -29,8 +29,8 @@ action_plan_key="[null]" created_at="1000000000" updated_at="2000000000" - issue_creation_date="2005-05-12 00:00:00.0" - issue_update_date="2013-05-18 00:00:00.0" + issue_creation_date="1115848800000" + issue_update_date="1368828000000" issue_close_date="[null]" /> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueResultSetIteratorTest/extract_directory_path.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueResultSetIteratorTest/extract_directory_path.xml index fc425638e3d..427e7e2d00b 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueResultSetIteratorTest/extract_directory_path.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueResultSetIteratorTest/extract_directory_path.xml @@ -29,8 +29,8 @@ tags="tag1,tag2,tag3" created_at="1400000000000" updated_at="1400000000000" - issue_creation_date="2005-05-12 00:00:00.0" - issue_update_date="2013-01-01 00:00:00.0" + issue_creation_date="1115848800000" + issue_update_date="1356994800000" issue_close_date="[null]"/> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueResultSetIteratorTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueResultSetIteratorTest/shared.xml index e3e8b7c52f2..4a3f129d314 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueResultSetIteratorTest/shared.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/index/IssueResultSetIteratorTest/shared.xml @@ -28,8 +28,8 @@ tags="tag1,tag2,tag3" created_at="1400000000000" updated_at="1400000000000" - issue_creation_date="2005-05-12 00:00:00.0" - issue_update_date="2013-01-01 00:00:00.0" + issue_creation_date="1115848800000" + issue_update_date="1356994800000" issue_close_date="[null]"/> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/778_add_issues_long_dates.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/778_add_issues_long_dates.rb new file mode 100644 index 00000000000..55492e8b421 --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/778_add_issues_long_dates.rb @@ -0,0 +1,30 @@ +# +# 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. +# + +# +# SonarQube 5.1 +# +class AddIssuesLongDates < ActiveRecord::Migration + def self.up + add_column 'issues', :issue_creation_date_ms, :big_integer, :null => true + add_column 'issues', :issue_update_date_ms, :big_integer, :null => true + add_column 'issues', :issue_close_date_ms, :big_integer, :null => true + end +end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/779_feed_issues_long_dates.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/779_feed_issues_long_dates.rb new file mode 100644 index 00000000000..33c6db66b87 --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/779_feed_issues_long_dates.rb @@ -0,0 +1,29 @@ +# +# 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. +# + +# +# SonarQube 5.1 +# +class FeedIssuesLongDates < ActiveRecord::Migration + def self.up + execute_java_migration('org.sonar.server.db.migrations.v51.FeedIssuesLongDates') + end +end + diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/780_rename_issues_long_dates.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/780_rename_issues_long_dates.rb new file mode 100644 index 00000000000..2d53e7cb59f --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/780_rename_issues_long_dates.rb @@ -0,0 +1,36 @@ +# +# 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. +# + +# +# SonarQube 5.1 +# +class RenameIssuesLongDates < ActiveRecord::Migration + def self.up + remove_index 'issues', :name => 'issues_creation_date' + remove_column 'issues', 'issue_creation_date' + remove_column 'issues', 'issue_update_date' + remove_column 'issues', 'issue_close_date' + rename_column 'issues', 'issue_creation_date_ms', 'issue_creation_date' + rename_column 'issues', 'issue_update_date_ms', 'issue_update_date' + rename_column 'issues', 'issue_close_date_ms', 'issue_close_date' + add_index 'issues', 'issue_creation_date', :name => 'issues_creation_date' + end +end + diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java index d026bfbb596..79d61dd2f74 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java @@ -37,11 +37,9 @@ import org.sonar.batch.index.DefaultIndex; import javax.annotation.Nullable; import javax.persistence.Query; - import java.util.*; import static org.sonar.api.utils.DateUtils.dateToLong; -import static org.sonar.api.utils.DateUtils.longToDate; public class DefaultTimeMachine implements TimeMachine { @@ -69,7 +67,7 @@ public class DefaultTimeMachine implements TimeMachine { Integer characteristicId = model.getCharacteristicId(); Characteristic characteristic = techDebtModel.characteristicById(characteristicId); Measure measure = toMeasure(model, metricById.get(model.getMetricId()), characteristic); - measure.setDate(longToDate((Long) object[1])); + measure.setDate(new Date((Long) object[1])); result.add(measure); } return result; diff --git a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_insert_new_issues-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_insert_new_issues-result.xml index 458ce2553c2..badb286c2e1 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_insert_new_issues-result.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_insert_new_issues-result.xml @@ -16,9 +16,9 @@ issue_attributes="foo=bar" tags="[null]" action_plan_key="[null]" - issue_creation_date="2013-05-18" - issue_update_date="2013-05-18" - issue_close_date="2013-05-18" + issue_creation_date="1368828000000" + issue_update_date="1368828000000" + issue_close_date="1368828000000" /> diff --git a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_resolve_conflicts_on_updates.xml b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_resolve_conflicts_on_updates.xml index 0ddfc4586ab..773cc50e1ae 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_resolve_conflicts_on_updates.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_resolve_conflicts_on_updates.xml @@ -27,8 +27,8 @@ action_plan_key="[null]" created_at="1400000000000" updated_at="1500000000000" - issue_creation_date="2005-05-12 00:00:00.0" - issue_update_date="2013-05-18 00:00:00.0" + issue_creation_date="1115848800000" + issue_update_date="1368828000000" issue_close_date="[null]" /> diff --git a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_update_issues-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_update_issues-result.xml index ba65e9df145..5e2490d198d 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_update_issues-result.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/issue/ScanIssueStorageTest/should_update_issues-result.xml @@ -21,9 +21,9 @@ issue_attributes="foo=bar" tags="[null]" action_plan_key="[null]" - issue_creation_date="2013-05-18 00:00:00.0" - issue_update_date="2013-05-18 00:00:00.0" - issue_close_date="2013-05-18 00:00:00.0" + issue_creation_date="1368828000000" + issue_update_date="1368828000000" + issue_close_date="1368828000000" /> diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java b/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java index 4bdb30e5e82..85ee2e47bcb 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java @@ -20,8 +20,14 @@ package org.sonar.core.issue.db; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + import java.util.Date; +import static org.sonar.api.utils.DateUtils.dateToLong; +import static org.sonar.api.utils.DateUtils.longToDate; + public class BatchIssueDto { private String kee; @@ -36,7 +42,7 @@ public class BatchIssueDto { private String componentKey; private String ruleKey; private String ruleRepo; - private Date creationDate; + private Long creationDate; public String getAssigneeLogin() { return assigneeLogin; @@ -146,12 +152,22 @@ public class BatchIssueDto { return this; } + @CheckForNull public Date getCreationDate() { + return longToDate(creationDate); + } + + public BatchIssueDto setCreationDate(@Nullable Date creationDate) { + this.creationDate = dateToLong(creationDate); + return this; + } + + public Long getCreationTime() { return creationDate; } - public BatchIssueDto setCreationDate(Date creationDate) { - this.creationDate = creationDate; + public BatchIssueDto setCreationTime(Long time) { + this.creationDate = time; return this; } } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java index 7363ea8426f..240650e9d21 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java @@ -30,10 +30,11 @@ import org.sonar.core.rule.RuleDto; import javax.annotation.CheckForNull; import javax.annotation.Nullable; - import java.util.Date; import java.util.List; +import static org.sonar.api.utils.DateUtils.dateToLong; + /** * @since 3.6 */ @@ -67,12 +68,12 @@ public class IssueDao implements BatchComponent, ServerComponent { // TODO replace by aggregation in IssueIndex public List findRulesByComponent(String componentKey, @Nullable Date createdAtOrAfter, DbSession session) { - return mapper(session).findRulesByComponent(componentKey, createdAtOrAfter); + return mapper(session).findRulesByComponent(componentKey, dateToLong(createdAtOrAfter)); } // TODO replace by aggregation in IssueIndex public List findSeveritiesByComponent(String componentKey, @Nullable Date createdAtOrAfter, DbSession session) { - return mapper(session).findSeveritiesByComponent(componentKey, createdAtOrAfter); + return mapper(session).findSeveritiesByComponent(componentKey, dateToLong(createdAtOrAfter)); } protected IssueMapper mapper(DbSession session) { diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java index d683a64cc11..9b726f4b4e3 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java @@ -37,10 +37,14 @@ import org.sonar.core.rule.RuleDto; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import java.io.Serializable; import java.util.Collection; import java.util.Date; +import static org.sonar.api.utils.DateUtils.dateToLong; +import static org.sonar.api.utils.DateUtils.longToDate; + /** * @since 3.6 */ @@ -72,10 +76,10 @@ public final class IssueDto implements Serializable { private long createdAt; private long updatedAt; - // functional dates - private Date issueCreationDate; - private Date issueUpdateDate; - private Date issueCloseDate; + // functional dates stored as Long + private Long issueCreationDate; + private Long issueUpdateDate; + private Long issueCloseDate; /** * Temporary date used only during scan @@ -93,6 +97,97 @@ public final class IssueDto implements Serializable { private String filePath; private String tags; + /** + * On batch side, component keys and uuid are useless + */ + public static IssueDto toDtoForComputationInsert(DefaultIssue issue, int ruleId, long now) { + return new IssueDto() + .setKee(issue.key()) + .setLine(issue.line()) + .setMessage(issue.message()) + .setEffortToFix(issue.effortToFix()) + .setDebt(issue.debtInMinutes()) + .setResolution(issue.resolution()) + .setStatus(issue.status()) + .setSeverity(issue.severity()) + .setManualSeverity(issue.manualSeverity()) + .setChecksum(issue.checksum()) + .setReporter(issue.reporter()) + .setAssignee(issue.assignee()) + .setRuleId(ruleId) + .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule()) + .setTags(issue.tags()) + .setComponentUuid(issue.componentUuid()) + .setComponentKey(issue.componentKey()) + .setModuleUuid(issue.moduleUuid()) + .setModuleUuidPath(issue.moduleUuidPath()) + .setComponentUuid(issue.componentUuid()) + .setProjectUuid(issue.projectUuid()) + .setProjectKey(issue.projectKey()) + .setActionPlanKey(issue.actionPlanKey()) + .setIssueAttributes(KeyValueFormat.format(issue.attributes())) + .setAuthorLogin(issue.authorLogin()) + .setIssueCreationDate(issue.creationDate()) + .setIssueCloseDate(issue.closeDate()) + .setIssueUpdateDate(issue.updateDate()) + .setSelectedAt(issue.selectedAt()) + + // technical dates + .setCreatedAt(now) + .setUpdatedAt(now); + } + + /** + * On server side, we need component keys and uuid + */ + public static IssueDto toDtoForServerInsert(DefaultIssue issue, ComponentDto component, ComponentDto project, int ruleId, long now) { + return toDtoForComputationInsert(issue, ruleId, now) + .setComponent(component) + .setProject(project); + } + + public static IssueDto toDtoForUpdate(DefaultIssue issue, long now) { + // Invariant fields, like key and rule, can't be updated + return new IssueDto() + .setKee(issue.key()) + .setLine(issue.line()) + .setMessage(issue.message()) + .setEffortToFix(issue.effortToFix()) + .setDebt(issue.debtInMinutes()) + .setResolution(issue.resolution()) + .setStatus(issue.status()) + .setSeverity(issue.severity()) + .setChecksum(issue.checksum()) + .setManualSeverity(issue.manualSeverity()) + .setReporter(issue.reporter()) + .setAssignee(issue.assignee()) + .setActionPlanKey(issue.actionPlanKey()) + .setIssueAttributes(KeyValueFormat.format(issue.attributes())) + .setAuthorLogin(issue.authorLogin()) + .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule()) + .setTags(issue.tags()) + .setComponentUuid(issue.componentUuid()) + .setComponentKey(issue.componentKey()) + .setModuleUuid(issue.moduleUuid()) + .setModuleUuidPath(issue.moduleUuidPath()) + .setProjectUuid(issue.projectUuid()) + .setProjectKey(issue.projectKey()) + .setIssueCreationDate(issue.creationDate()) + .setIssueCloseDate(issue.closeDate()) + .setIssueUpdateDate(issue.updateDate()) + .setSelectedAt(issue.selectedAt()) + + // technical date + .setUpdatedAt(now); + } + + public static IssueDto createFor(Project project, RuleDto rule) { + return new IssueDto() + .setProjectUuid(project.getUuid()) + .setRuleId(rule.getId()) + .setKee(Uuids.create()); + } + public String getKey() { return getKee(); } @@ -134,15 +229,6 @@ public final class IssueDto implements Serializable { return ruleId; } - public IssueDto setRule(RuleDto rule) { - Preconditions.checkNotNull(rule.getId(), "Rule must be persisted."); - this.ruleId = rule.getId(); - this.ruleKey = rule.getRuleKey(); - this.ruleRepo = rule.getRepositoryKey(); - this.language = rule.getLanguage(); - return this; - } - /** * please use setRule(RuleDto rule) */ @@ -288,129 +374,107 @@ public final class IssueDto implements Serializable { return this; } + /** + * Technical date + */ public long getCreatedAt() { return createdAt; } - /** - * Technical date - */ public IssueDto setCreatedAt(long createdAt) { this.createdAt = createdAt; return this; } + /** + * Technical date + */ public long getUpdatedAt() { return updatedAt; } - /** - * Technical date - */ public IssueDto setUpdatedAt(long updatedAt) { this.updatedAt = updatedAt; return this; } - public Date getIssueCreationDate() { + public Long getIssueCreationTime() { return issueCreationDate; } - public IssueDto setIssueCreationDate(@Nullable Date d) { - this.issueCreationDate = d; + public IssueDto setIssueCreationTime(Long time) { + this.issueCreationDate = time; return this; } - public Date getIssueUpdateDate() { - return issueUpdateDate; + public Date getIssueCreationDate() { + return longToDate(issueCreationDate); } - public IssueDto setIssueUpdateDate(@Nullable Date d) { - this.issueUpdateDate = d; + public IssueDto setIssueCreationDate(@Nullable Date d) { + this.issueCreationDate = dateToLong(d); return this; } - public Date getIssueCloseDate() { - return issueCloseDate; + public Long getIssueUpdateTime() { + return issueUpdateDate; } - public IssueDto setIssueCloseDate(@Nullable Date d) { - this.issueCloseDate = d; + public IssueDto setIssueUpdateTime(Long time) { + this.issueUpdateDate = time; return this; } - public String getRule() { - return ruleKey; - } - - public String getRuleRepo() { - return ruleRepo; - } - - public RuleKey getRuleKey() { - return RuleKey.of(ruleRepo, ruleKey); + public Date getIssueUpdateDate() { + return longToDate(issueUpdateDate); } - public String getLanguage() { - return language; + public IssueDto setIssueUpdateDate(@Nullable Date d) { + this.issueUpdateDate = dateToLong(d); + return this; } - public String getComponentKey() { - return componentKey; + public Long getIssueCloseTime() { + return issueCloseDate; } - /** - * Can be null on Views or Devs - */ - @CheckForNull - public String getComponentUuid() { - return componentUuid; + public IssueDto setIssueCloseTime(Long time) { + this.issueCloseDate = time; + return this; } - @CheckForNull - public String getModuleUuid() { - return moduleUuid; + public Date getIssueCloseDate() { + return longToDate(issueCloseDate); } - @CheckForNull - public String getModuleUuidPath() { - return moduleUuidPath; + public IssueDto setIssueCloseDate(@Nullable Date d) { + this.issueCloseDate = dateToLong(d); + return this; } - /** - * Used by the issue tracking mechanism, but it should used the component uuid instead - */ - public String getProjectKey() { - return projectKey; + public String getRule() { + return ruleKey; } - /** - * Can be null on Views or Devs - */ - @CheckForNull - public String getProjectUuid() { - return projectUuid; + public IssueDto setRule(RuleDto rule) { + Preconditions.checkNotNull(rule.getId(), "Rule must be persisted."); + this.ruleId = rule.getId(); + this.ruleKey = rule.getRuleKey(); + this.ruleRepo = rule.getRepositoryKey(); + this.language = rule.getLanguage(); + return this; } - @CheckForNull - public Long getSelectedAt() { - return selectedAt; + public String getRuleRepo() { + return ruleRepo; } - public IssueDto setSelectedAt(@Nullable Long d) { - this.selectedAt = d; - return this; + public RuleKey getRuleKey() { + return RuleKey.of(ruleRepo, ruleKey); } - /** - * Should only be used to persist in E/S - *

- * Please use {@link #setRule(org.sonar.core.rule.RuleDto)} instead - */ - public IssueDto setRuleKey(String repo, String rule) { - this.ruleRepo = repo; - this.ruleKey = rule; - return this; + public String getLanguage() { + return language; } /** @@ -423,6 +487,10 @@ public final class IssueDto implements Serializable { return this; } + public String getComponentKey() { + return componentKey; + } + /** * Should only be used to persist in E/S *

@@ -433,6 +501,14 @@ public final class IssueDto implements Serializable { return this; } + /** + * Can be null on Views or Devs + */ + @CheckForNull + public String getComponentUuid() { + return componentUuid; + } + /** * Should only be used to persist in E/S *

@@ -443,6 +519,11 @@ public final class IssueDto implements Serializable { return this; } + @CheckForNull + public String getModuleUuid() { + return moduleUuid; + } + /** * Should only be used to persist in E/S *

@@ -453,6 +534,11 @@ public final class IssueDto implements Serializable { return this; } + @CheckForNull + public String getModuleUuidPath() { + return moduleUuidPath; + } + /** * Should only be used to persist in E/S *

@@ -463,6 +549,13 @@ public final class IssueDto implements Serializable { return this; } + /** + * Used by the issue tracking mechanism, but it should used the component uuid instead + */ + public String getProjectKey() { + return projectKey; + } + /** * Should only be used to persist in E/S *

@@ -473,6 +566,14 @@ public final class IssueDto implements Serializable { return this; } + /** + * Can be null on Views or Devs + */ + @CheckForNull + public String getProjectUuid() { + return projectUuid; + } + /** * Should only be used to persist in E/S *

@@ -483,6 +584,27 @@ public final class IssueDto implements Serializable { return this; } + @CheckForNull + public Long getSelectedAt() { + return selectedAt; + } + + public IssueDto setSelectedAt(@Nullable Long d) { + this.selectedAt = d; + return this; + } + + /** + * Should only be used to persist in E/S + *

+ * Please use {@link #setRule(org.sonar.core.rule.RuleDto)} instead + */ + public IssueDto setRuleKey(String repo, String rule) { + this.ruleRepo = repo; + this.ruleKey = rule; + return this; + } + /** * Should only be used to persist in E/S *

@@ -529,90 +651,6 @@ public final class IssueDto implements Serializable { return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); } - /** - * On batch side, component keys and uuid are useless - */ - public static IssueDto toDtoForComputationInsert(DefaultIssue issue, int ruleId, long now) { - return new IssueDto() - .setKee(issue.key()) - .setLine(issue.line()) - .setMessage(issue.message()) - .setEffortToFix(issue.effortToFix()) - .setDebt(issue.debtInMinutes()) - .setResolution(issue.resolution()) - .setStatus(issue.status()) - .setSeverity(issue.severity()) - .setManualSeverity(issue.manualSeverity()) - .setChecksum(issue.checksum()) - .setReporter(issue.reporter()) - .setAssignee(issue.assignee()) - .setRuleId(ruleId) - .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule()) - .setTags(issue.tags()) - .setComponentUuid(issue.componentUuid()) - .setComponentKey(issue.componentKey()) - .setModuleUuid(issue.moduleUuid()) - .setModuleUuidPath(issue.moduleUuidPath()) - .setComponentUuid(issue.componentUuid()) - .setProjectUuid(issue.projectUuid()) - .setProjectKey(issue.projectKey()) - .setActionPlanKey(issue.actionPlanKey()) - .setIssueAttributes(KeyValueFormat.format(issue.attributes())) - .setAuthorLogin(issue.authorLogin()) - .setIssueCreationDate(issue.creationDate()) - .setIssueCloseDate(issue.closeDate()) - .setIssueUpdateDate(issue.updateDate()) - .setSelectedAt(issue.selectedAt()) - - // technical dates - .setCreatedAt(now) - .setUpdatedAt(now); - } - - /** - * On server side, we need component keys and uuid - */ - public static IssueDto toDtoForServerInsert(DefaultIssue issue, ComponentDto component, ComponentDto project, int ruleId, long now) { - return toDtoForComputationInsert(issue, ruleId, now) - .setComponent(component) - .setProject(project); - } - - public static IssueDto toDtoForUpdate(DefaultIssue issue, long now) { - // Invariant fields, like key and rule, can't be updated - return new IssueDto() - .setKee(issue.key()) - .setLine(issue.line()) - .setMessage(issue.message()) - .setEffortToFix(issue.effortToFix()) - .setDebt(issue.debtInMinutes()) - .setResolution(issue.resolution()) - .setStatus(issue.status()) - .setSeverity(issue.severity()) - .setChecksum(issue.checksum()) - .setManualSeverity(issue.manualSeverity()) - .setReporter(issue.reporter()) - .setAssignee(issue.assignee()) - .setActionPlanKey(issue.actionPlanKey()) - .setIssueAttributes(KeyValueFormat.format(issue.attributes())) - .setAuthorLogin(issue.authorLogin()) - .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule()) - .setTags(issue.tags()) - .setComponentUuid(issue.componentUuid()) - .setComponentKey(issue.componentKey()) - .setModuleUuid(issue.moduleUuid()) - .setModuleUuidPath(issue.moduleUuidPath()) - .setProjectUuid(issue.projectUuid()) - .setProjectKey(issue.projectKey()) - .setIssueCreationDate(issue.creationDate()) - .setIssueCloseDate(issue.closeDate()) - .setIssueUpdateDate(issue.updateDate()) - .setSelectedAt(issue.selectedAt()) - - // technical date - .setUpdatedAt(now); - } - public DefaultIssue toDefaultIssue() { DefaultIssue issue = new DefaultIssue(); issue.setKey(kee); @@ -639,17 +677,10 @@ public final class IssueDto implements Serializable { issue.setActionPlanKey(actionPlanKey); issue.setAuthorLogin(authorLogin); issue.setNew(false); - issue.setCreationDate(issueCreationDate); - issue.setCloseDate(issueCloseDate); - issue.setUpdateDate(issueUpdateDate); + issue.setCreationDate(longToDate(issueCreationDate)); + issue.setCloseDate(longToDate(issueCloseDate)); + issue.setUpdateDate(longToDate(issueUpdateDate)); issue.setSelectedAt(selectedAt); return issue; } - - public static IssueDto createFor(Project project, RuleDto rule) { - return new IssueDto() - .setProjectUuid(project.getUuid()) - .setRuleId(rule.getId()) - .setKee(Uuids.create()); - } } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java index 3d4690c2f0e..2d97a4160d7 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java @@ -34,9 +34,9 @@ public interface IssueMapper { List selectByActionPlan(String actionPlan); - List findRulesByComponent(@Param("componentKey") String componentKey, @Nullable @Param("createdAt") Date createdAtOrAfter); + List findRulesByComponent(@Param("componentKey") String componentKey, @Nullable @Param("createdAt") Long createdAtOrAfter); - List findSeveritiesByComponent(@Param("componentKey") String componentKey, @Nullable @Param("createdAt") Date createdAtOrAfter); + List findSeveritiesByComponent(@Param("componentKey") String componentKey, @Nullable @Param("createdAt") Long createdAtOrAfter); void insert(IssueDto issue); diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java index 6b07dd2255e..b2d44f173e4 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java @@ -33,7 +33,7 @@ import java.util.List; */ public class DatabaseVersion implements BatchComponent, ServerComponent { - public static final int LAST_VERSION = 777; + public static final int LAST_VERSION = 780; /** * List of all the tables.n diff --git a/sonar-core/src/main/java/org/sonar/core/purge/PurgeDao.java b/sonar-core/src/main/java/org/sonar/core/purge/PurgeDao.java index 65685305942..0e507c10dd6 100644 --- a/sonar-core/src/main/java/org/sonar/core/purge/PurgeDao.java +++ b/sonar-core/src/main/java/org/sonar/core/purge/PurgeDao.java @@ -37,6 +37,8 @@ import java.util.Collections; import java.util.Date; import java.util.List; +import static org.sonar.api.utils.DateUtils.dateToLong; + /** * @since 2.14 */ @@ -82,8 +84,8 @@ public class PurgeDao { private void deleteOldClosedIssues(PurgeConfiguration conf, PurgeMapper mapper) { Date toDate = conf.maxLiveDateOfClosedIssues(); - mapper.deleteOldClosedIssueChanges(conf.rootProjectIdUuid().getUuid(), toDate); - mapper.deleteOldClosedIssues(conf.rootProjectIdUuid().getUuid(), toDate); + mapper.deleteOldClosedIssueChanges(conf.rootProjectIdUuid().getUuid(), dateToLong(toDate)); + mapper.deleteOldClosedIssues(conf.rootProjectIdUuid().getUuid(), dateToLong(toDate)); } private void deleteAbortedBuilds(ResourceDto project, PurgeCommands commands) { @@ -91,7 +93,7 @@ public class PurgeDao { LOG.info("<- Delete aborted builds"); PurgeSnapshotQuery query = PurgeSnapshotQuery.create() .setIslast(false) - .setStatus(new String[]{"U"}) + .setStatus(new String[] {"U"}) .setRootProjectId(project.getId()); commands.deleteSnapshots(query); } @@ -100,7 +102,7 @@ public class PurgeDao { private boolean hasAbortedBuilds(Long projectId, PurgeCommands commands) { PurgeSnapshotQuery query = PurgeSnapshotQuery.create() .setIslast(false) - .setStatus(new String[]{"U"}) + .setStatus(new String[] {"U"}) .setResourceId(projectId); return !commands.selectSnapshotIds(query).isEmpty(); } @@ -111,7 +113,7 @@ public class PurgeDao { .setResourceId(project.getId()) .setIslast(false) .setNotPurged(true) - ); + ); for (final Long projectSnapshotId : projectSnapshotIds) { LOG.info("<- Clean snapshot " + projectSnapshotId); if (!ArrayUtils.isEmpty(scopesWithoutHistoricalData)) { @@ -195,7 +197,7 @@ public class PurgeDao { mapper.setSnapshotIsLastToFalse(componentId); mapper.deleteFileSourcesByUuid(componentIdUuid.getUuid()); mapper.disableResource(componentId); - mapper.resolveResourceIssuesNotAlreadyResolved(componentIdUuid.getUuid(), new Date(system2.now()), system2.now()); + mapper.resolveResourceIssuesNotAlreadyResolved(componentIdUuid.getUuid(), system2.now()); } public PurgeDao deleteSnapshots(PurgeSnapshotQuery query) { diff --git a/sonar-core/src/main/java/org/sonar/core/purge/PurgeMapper.java b/sonar-core/src/main/java/org/sonar/core/purge/PurgeMapper.java index c73cc86dd82..643cb15c4a9 100644 --- a/sonar-core/src/main/java/org/sonar/core/purge/PurgeMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/purge/PurgeMapper.java @@ -22,7 +22,6 @@ package org.sonar.core.purge; import org.apache.ibatis.annotations.Param; import javax.annotation.Nullable; -import java.util.Date; import java.util.List; public interface PurgeMapper { @@ -59,7 +58,7 @@ public interface PurgeMapper { void disableResource(long resourceId); - void resolveResourceIssuesNotAlreadyResolved(@Param("componentUuid") String componentUuid, @Param("date") Date date, @Param("dateAsLong") Long dateAsLong); + void resolveResourceIssuesNotAlreadyResolved(@Param("componentUuid") String componentUuid, @Param("dateAsLong") Long dateAsLong); void deleteResourceIndex(@Param("resourceIds") List resourceIds); @@ -95,9 +94,9 @@ public interface PurgeMapper { void deleteComponentIssues(@Param("componentUuids") List componentUuids); - void deleteOldClosedIssueChanges(@Param("projectUuid") String projectUuid, @Nullable @Param("toDate") Date toDate); + void deleteOldClosedIssueChanges(@Param("projectUuid") String projectUuid, @Nullable @Param("toDate") Long toDate); - void deleteOldClosedIssues(@Param("projectUuid") String projectUuid, @Nullable @Param("toDate") Date toDate); + void deleteOldClosedIssues(@Param("projectUuid") String projectUuid, @Nullable @Param("toDate") Long toDate); void deleteFileSourcesByProjectUuid(String rootProjectUuid); diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml index 8217084291c..a59f402fdec 100644 --- a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml @@ -23,9 +23,9 @@ i.author_login as authorLogin, i.tags as tagsString, i.issue_attributes as issueAttributes, - i.issue_creation_date as issueCreationDate, - i.issue_update_date as issueUpdateDate, - i.issue_close_date as issueCloseDate, + i.issue_creation_date as issueCreationTime, + i.issue_update_date as issueUpdateTime, + i.issue_close_date as issueCloseTime, i.created_at as createdAt, i.updated_at as updatedAt, r.plugin_rule_key as ruleKey, @@ -53,13 +53,13 @@ i.assignee as assignee - i.issue_creation_date as issueCreationDate + i.issue_creation_date as issueCreationTime - i.issue_update_date as issueUpdateDate + i.issue_update_date as issueUpdateTime - i.issue_close_date as issueCloseDate + i.issue_close_date as issueCloseTime @@ -77,7 +77,7 @@ #{tagsString,jdbcType=VARCHAR}, #{resolution,jdbcType=VARCHAR}, #{checksum,jdbcType=VARCHAR}, #{reporter,jdbcType=VARCHAR}, #{assignee,jdbcType=VARCHAR}, #{authorLogin,jdbcType=VARCHAR}, #{issueAttributes,jdbcType=VARCHAR}, - #{issueCreationDate,jdbcType=TIMESTAMP},#{issueUpdateDate,jdbcType=TIMESTAMP}, #{issueCloseDate,jdbcType=TIMESTAMP}, + #{issueCreationTime,jdbcType=BIGINT},#{issueUpdateTime,jdbcType=BIGINT}, #{issueCloseTime,jdbcType=BIGINT}, #{createdAt,jdbcType=BIGINT}, #{updatedAt,jdbcType=BIGINT}, #{componentUuid,jdbcType=VARCHAR}, #{projectUuid,jdbcType=VARCHAR}) @@ -87,26 +87,26 @@ --> update issues set - action_plan_key=#{actionPlanKey}, - severity=#{severity}, - manual_severity=#{manualSeverity}, - message=#{message}, - line=#{line}, - effort_to_fix=#{effortToFix}, - technical_debt=#{debt}, - status=#{status}, - resolution=#{resolution}, - checksum=#{checksum}, - reporter=#{reporter}, - assignee=#{assignee}, - author_login=#{authorLogin}, - tags=#{tagsString}, - project_uuid=#{projectUuid}, - issue_attributes=#{issueAttributes}, - issue_creation_date=#{issueCreationDate}, - issue_update_date=#{issueUpdateDate}, - issue_close_date=#{issueCloseDate}, - updated_at=#{updatedAt} + action_plan_key=#{actionPlanKey,jdbcType=VARCHAR}, + severity=#{severity,jdbcType=VARCHAR}, + manual_severity=#{manualSeverity,jdbcType=BOOLEAN}, + message=#{message,jdbcType=VARCHAR}, + line=#{line,jdbcType=INTEGER}, + effort_to_fix=#{effortToFix,jdbcType=DOUBLE}, + technical_debt=#{debt,jdbcType=INTEGER}, + status=#{status,jdbcType=VARCHAR}, + resolution=#{resolution,jdbcType=VARCHAR}, + checksum=#{checksum,jdbcType=VARCHAR}, + reporter=#{reporter,jdbcType=VARCHAR}, + assignee=#{assignee,jdbcType=VARCHAR}, + author_login=#{authorLogin,jdbcType=VARCHAR}, + tags=#{tagsString,jdbcType=VARCHAR}, + project_uuid=#{projectUuid,jdbcType=VARCHAR}, + issue_attributes=#{issueAttributes,jdbcType=VARCHAR}, + issue_creation_date=#{issueCreationTime,jdbcType=BIGINT}, + issue_update_date=#{issueUpdateTime,jdbcType=BIGINT}, + issue_close_date=#{issueCloseTime,jdbcType=BIGINT}, + updated_at=#{updatedAt,jdbcType=BIGINT} where kee = #{kee} @@ -115,26 +115,26 @@ --> update issues set - action_plan_key=#{actionPlanKey}, - severity=#{severity}, - manual_severity=#{manualSeverity}, - message=#{message}, - line=#{line}, - effort_to_fix=#{effortToFix}, - technical_debt=#{debt}, - status=#{status}, - resolution=#{resolution}, - checksum=#{checksum}, - reporter=#{reporter}, - assignee=#{assignee}, - author_login=#{authorLogin}, - tags=#{tagsString}, - project_uuid=#{projectUuid}, - issue_attributes=#{issueAttributes}, - issue_creation_date=#{issueCreationDate}, - issue_update_date=#{issueUpdateDate}, - issue_close_date=#{issueCloseDate}, - updated_at=#{updatedAt} + action_plan_key=#{actionPlanKey,jdbcType=VARCHAR}, + severity=#{severity,jdbcType=VARCHAR}, + manual_severity=#{manualSeverity,jdbcType=BOOLEAN}, + message=#{message,jdbcType=VARCHAR}, + line=#{line,jdbcType=INTEGER}, + effort_to_fix=#{effortToFix,jdbcType=DOUBLE}, + technical_debt=#{debt,jdbcType=INTEGER}, + status=#{status,jdbcType=VARCHAR}, + resolution=#{resolution,jdbcType=VARCHAR}, + checksum=#{checksum,jdbcType=VARCHAR}, + reporter=#{reporter,jdbcType=VARCHAR}, + assignee=#{assignee,jdbcType=VARCHAR}, + author_login=#{authorLogin,jdbcType=VARCHAR}, + tags=#{tagsString,jdbcType=VARCHAR}, + project_uuid=#{projectUuid,jdbcType=VARCHAR}, + issue_attributes=#{issueAttributes,jdbcType=VARCHAR}, + issue_creation_date=#{issueCreationTime,jdbcType=BIGINT}, + issue_update_date=#{issueUpdateTime,jdbcType=BIGINT}, + issue_close_date=#{issueCloseTime,jdbcType=BIGINT}, + updated_at=#{updatedAt,jdbcType=BIGINT} where kee = #{kee} and updated_at <= #{selectedAt} @@ -170,9 +170,9 @@ i.author_login as authorLogin, i.tags as tagsString, i.issue_attributes as issueAttributes, - i.issue_creation_date as issueCreationDate, - i.issue_update_date as issueUpdateDate, - i.issue_close_date as issueCloseDate, + i.issue_creation_date as issueCreationTime, + i.issue_update_date as issueUpdateTime, + i.issue_close_date as issueCloseTime, i.created_at as createdAt, i.updated_at as updatedAt, r.plugin_rule_key as ruleKey, @@ -198,7 +198,7 @@ i.resolution as resolution, i.checksum as checksum, i.assignee as assigneeLogin, - i.issue_creation_date as creationDate, + i.issue_creation_date as creationTime, r.plugin_rule_key as ruleKey, r.plugin_name as ruleRepo, component.kee as componentKey @@ -220,7 +220,7 @@ i.resolution as resolution, i.checksum as checksum, i.assignee as assigneeLogin, - i.issue_creation_date as creationDate, + i.issue_creation_date as creationTime, r.plugin_rule_key as ruleKey, r.plugin_name as ruleRepo, component.kee as componentKey diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql index 51201ffe081..92e9f0aff0c 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql @@ -305,6 +305,9 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('773'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('775'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('776'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('777'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('778'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('779'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('780'); INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482', null, null); ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl index 06133faa160..202f896611e 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl @@ -460,9 +460,9 @@ CREATE TABLE "ISSUES" ( "ACTION_PLAN_KEY" VARCHAR(50) NULL, "ISSUE_ATTRIBUTES" VARCHAR(4000), "TAGS" VARCHAR(4000), - "ISSUE_CREATION_DATE" TIMESTAMP, - "ISSUE_CLOSE_DATE" TIMESTAMP, - "ISSUE_UPDATE_DATE" TIMESTAMP, + "ISSUE_CREATION_DATE" BIGINT, + "ISSUE_CLOSE_DATE" BIGINT, + "ISSUE_UPDATE_DATE" BIGINT, "CREATED_AT" BIGINT, "UPDATED_AT" BIGINT ); diff --git a/sonar-core/src/main/resources/org/sonar/core/purge/PurgeMapper.xml b/sonar-core/src/main/resources/org/sonar/core/purge/PurgeMapper.xml index 2bd8f7e9aae..38d83162163 100644 --- a/sonar-core/src/main/resources/org/sonar/core/purge/PurgeMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/purge/PurgeMapper.xml @@ -177,8 +177,8 @@ - UPDATE issues SET status='CLOSED',resolution='REMOVED',updated_at=#{dateAsLong},issue_close_date=#{date}, - issue_update_date=#{date} + UPDATE issues SET status='CLOSED',resolution='REMOVED',updated_at=#{dateAsLong},issue_close_date=#{dateAsLong}, + issue_update_date=#{dateAsLong} WHERE component_uuid=#{componentUuid} AND resolution IS NULL diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanStatsDaoTest/should_find_by_project.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanStatsDaoTest/should_find_by_project.xml index e945efb6ab0..574573ee9f7 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanStatsDaoTest/should_find_by_project.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/ActionPlanStatsDaoTest/should_find_by_project.xml @@ -21,9 +21,9 @@ author_login="[null]" issue_attributes="JIRA=FOO-1234" action_plan_key="ABC" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1400000000000" /> @@ -46,9 +46,9 @@ author_login="[null]" issue_attributes="JIRA=FOO-1234" action_plan_key="ABC" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1400000000000" /> @@ -71,9 +71,9 @@ author_login="[null]" issue_attributes="JIRA=FOO-1234" action_plan_key="ABC" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1400000000000" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/select_issue_changelog_by_module.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/select_issue_changelog_by_module.xml index 6b43a944834..fd4ee9c31d0 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/select_issue_changelog_by_module.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/select_issue_changelog_by_module.xml @@ -24,9 +24,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -86,9 +86,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -124,9 +124,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -162,9 +162,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -200,9 +200,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/select_issue_changelog_by_module_are_sorted_by_creation_date.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/select_issue_changelog_by_module_are_sorted_by_creation_date.xml index 88ce001bcc3..b019869a390 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/select_issue_changelog_by_module_are_sorted_by_creation_date.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/select_issue_changelog_by_module_are_sorted_by_creation_date.xml @@ -21,9 +21,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_rules_by_component.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_rules_by_component.xml index 3b0a6b64d2b..9d97be65b71 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_rules_by_component.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_rules_by_component.xml @@ -19,9 +19,9 @@ assignee="perceval" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1400000000000" /> @@ -44,9 +44,9 @@ assignee="perceval" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-17" - issue_update_date="2013-04-17" - issue_close_date="2013-04-17" + issue_creation_date="1366149600000" + issue_update_date="1366149600000" + issue_close_date="1366149600000" created_at="1400000000000" updated_at="1400000000000" /> @@ -71,9 +71,9 @@ assignee="perceval" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-18" - issue_update_date="2013-04-18" - issue_close_date="2013-04-18" + issue_creation_date="1366236000000" + issue_update_date="1366236000000" + issue_close_date="1366236000000" created_at="1400000000000" updated_at="1400000000000" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_severities_by_component.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_severities_by_component.xml index 19d8d5c7105..054f2e5b877 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_severities_by_component.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/find_severities_by_component.xml @@ -19,9 +19,9 @@ assignee="perceval" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1400000000000" /> @@ -44,9 +44,9 @@ assignee="perceval" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-17" - issue_update_date="2013-04-17" - issue_close_date="2013-04-17" + issue_creation_date="1366149600000" + issue_update_date="1366149600000" + issue_close_date="1366149600000" created_at="1400000000000" updated_at="1400000000000" /> @@ -71,9 +71,9 @@ assignee="perceval" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1400000000000" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_key.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_key.xml index 4faf9b16db0..080269cef46 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_key.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_key.xml @@ -18,9 +18,9 @@ assignee="perceval" author_login="karadoc" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1400000000000" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_issue_and_component_ids.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_issue_and_component_ids.xml index a8a8d8eafa3..014ca75c7d5 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_issue_and_component_ids.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_issue_and_component_ids.xml @@ -19,9 +19,9 @@ assignee="perceval" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1400000000000" /> @@ -44,9 +44,9 @@ assignee="perceval" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1400000000000" /> @@ -71,9 +71,9 @@ assignee="perceval" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="1400000000000" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_non_closed_issues_by_module.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_non_closed_issues_by_module.xml index 06824c52449..3a0b7ee023d 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_non_closed_issues_by_module.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_non_closed_issues_by_module.xml @@ -19,9 +19,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -45,9 +45,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -71,9 +71,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -97,9 +97,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -123,9 +123,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_non_closed_issues_by_module_on_removed_project.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_non_closed_issues_by_module_on_removed_project.xml index 53906e7d6e5..b7b17a65dd3 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_non_closed_issues_by_module_on_removed_project.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_non_closed_issues_by_module_on_removed_project.xml @@ -39,9 +39,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -65,9 +65,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -91,9 +91,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> @@ -117,9 +117,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="1400000000000" updated_at="[null]" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testInsert-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testInsert-result.xml index 19384f8380a..bbc47e84e7f 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testInsert-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testInsert-result.xml @@ -19,9 +19,9 @@ assignee="karadoc" issue_attributes="JIRA=FOO-1234" tags="[null]" - issue_creation_date="2013-05-18" - issue_update_date="2013-05-19" - issue_close_date="2013-05-20" + issue_creation_date="1368828000000" + issue_update_date="1368914400000" + issue_close_date="1369000800000" created_at="1400000000000" updated_at="1500000000000" action_plan_key="current_sprint" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml index 5cf1714d9bb..148d47a907b 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueMapperTest/testUpdate-result.xml @@ -19,9 +19,9 @@ assignee="karadoc" issue_attributes="JIRA=FOO-1234" tags="[null]" - issue_creation_date="2013-05-18" - issue_update_date="2013-05-19" - issue_close_date="2013-05-20" + issue_creation_date="1368828000000" + issue_update_date="1368914400000" + issue_close_date="1369000800000" created_at="1400000000000" updated_at="1500000000000" action_plan_key="current_sprint" diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStatsDaoTest/should_select_assignees.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStatsDaoTest/should_select_assignees.xml index 1fd8a9ec17b..ce76408c323 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStatsDaoTest/should_select_assignees.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStatsDaoTest/should_select_assignees.xml @@ -21,9 +21,9 @@ assignee="perceval" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="2013-04-16" updated_at="2013-04-16" /> @@ -46,9 +46,9 @@ assignee="[null]" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="2013-04-16" updated_at="2013-04-16" /> @@ -71,9 +71,9 @@ assignee="perceval" author_login="[null]" issue_attributes="JIRA=FOO-1234" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="2013-04-16" updated_at="2013-04-16" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml index e9701caee67..a69e0a72135 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_insert_new_issues-result.xml @@ -16,9 +16,9 @@ issue_attributes="foo=bar" tags="[null]" action_plan_key="[null]" - issue_creation_date="2013-05-18" - issue_update_date="2013-05-18" - issue_close_date="2013-05-18" + issue_creation_date="1368828000000" + issue_update_date="1368828000000" + issue_close_date="1368828000000" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates.xml index 0346a0a03ea..b026a0da238 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_resolve_conflicts_on_updates.xml @@ -28,8 +28,8 @@ action_plan_key="[null]" created_at="2005-05-12" updated_at="2013-05-18" - issue_creation_date="2005-05-12 00:00:00.0" - issue_update_date="2013-05-18 00:00:00.0" + issue_creation_date="1115848800000" + issue_update_date="1368828000000" issue_close_date="[null]" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues-result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues-result.xml index 3cc73d72c7c..ea291ef00bc 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStorageTest/should_update_issues-result.xml @@ -21,9 +21,9 @@ issue_attributes="foo=bar" tags="[null]" action_plan_key="[null]" - issue_creation_date="2013-05-18 00:00:00.0" - issue_update_date="2013-05-18 00:00:00.0" - issue_close_date="2013-05-18 00:00:00.0" + issue_creation_date="1368828000000" + issue_update_date="1368828000000" + issue_close_date="1368828000000" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/persistence/PreviewDatabaseFactoryTest/multi-modules-with-issues.xml b/sonar-core/src/test/resources/org/sonar/core/persistence/PreviewDatabaseFactoryTest/multi-modules-with-issues.xml index f6847e78a1c..82a42cdbba9 100644 --- a/sonar-core/src/test/resources/org/sonar/core/persistence/PreviewDatabaseFactoryTest/multi-modules-with-issues.xml +++ b/sonar-core/src/test/resources/org/sonar/core/persistence/PreviewDatabaseFactoryTest/multi-modules-with-issues.xml @@ -88,9 +88,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="[null]" updated_at="[null]" /> @@ -114,9 +114,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="[null]" updated_at="[null]" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/persistence/PreviewDatabaseFactoryTest/should_create_database_with_issues.xml b/sonar-core/src/test/resources/org/sonar/core/persistence/PreviewDatabaseFactoryTest/should_create_database_with_issues.xml index ea885325961..d66c716e4a3 100644 --- a/sonar-core/src/test/resources/org/sonar/core/persistence/PreviewDatabaseFactoryTest/should_create_database_with_issues.xml +++ b/sonar-core/src/test/resources/org/sonar/core/persistence/PreviewDatabaseFactoryTest/should_create_database_with_issues.xml @@ -37,9 +37,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="[null]" updated_at="[null]" /> @@ -62,9 +62,9 @@ assignee="user" author_login="[null]" issue_attributes="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="[null]" updated_at="[null]" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml index 8912afe10d4..e7d8f49a2b4 100644 --- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml +++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeCommandsTest/shouldDeleteResource.xml @@ -23,9 +23,9 @@ reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" created_at="[null]" updated_at="[null]" /> diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/disable_resources_without_last_snapshot-result.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/disable_resources_without_last_snapshot-result.xml index b996557dd58..530dafeedaa 100644 --- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/disable_resources_without_last_snapshot-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/disable_resources_without_last_snapshot-result.xml @@ -68,12 +68,12 @@ What has been changed : component_uuid="GHIJ" project_uuid="ABCD" status="CLOSED" - issue_close_date="2014-04-09" + issue_close_date="1396994400000" resolution="REMOVED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="1450000000000" issue_creation_date="2013-04-16" issue_update_date="2014-04-09" + updated_at="1450000000000" issue_creation_date="1366063200000" issue_update_date="1396994400000" created_at="1450000000000" tags="[null]"/> @@ -81,12 +81,12 @@ What has been changed : component_uuid="EFGH" project_uuid="ABCD" status="CLOSED" - issue_close_date="2014-04-09" + issue_close_date="1396994400000" resolution="REMOVED" line="[null]" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="1450000000000" issue_creation_date="2013-04-16" issue_update_date="2014-04-09" + updated_at="1450000000000" issue_creation_date="1366063200000" issue_update_date="1396994400000" created_at="1450000000000" tags="[null]"/> @@ -94,12 +94,12 @@ What has been changed : component_uuid="ABCD" project_uuid="ABCD" status="CLOSED" - issue_close_date="2014-04-09" + issue_close_date="1396994400000" resolution="REMOVED" line="[null]" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="1450000000000" issue_creation_date="2013-04-16" issue_update_date="2014-04-09" + updated_at="1450000000000" issue_creation_date="1366063200000" issue_update_date="1396994400000" created_at="1450000000000" tags="[null]"/> @@ -107,12 +107,12 @@ What has been changed : component_uuid="GHIJ" project_uuid="ABCD" status="CLOSED" - issue_close_date="2015-12-08" + issue_close_date="1449529200000" resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="1450000000000" issue_creation_date="2013-04-16" issue_update_date="2014-04-08" + updated_at="1450000000000" issue_creation_date="1366063200000" issue_update_date="1396908000000" created_at="1450000000000" tags="[null]"/> diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/disable_resources_without_last_snapshot.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/disable_resources_without_last_snapshot.xml index ef2dd1725ec..46f8dbc543c 100644 --- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/disable_resources_without_last_snapshot.xml +++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/disable_resources_without_last_snapshot.xml @@ -54,7 +54,7 @@ issue_close_date="[null]" resolution="[null]" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="1450000000000"/> + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1450000000000"/> + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1450000000000"/> + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1450000000000"/> + updated_at="1450000000000" issue_creation_date="1366063200000" issue_update_date="1396908000000" created_at="1450000000000"/> diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml index 3d6f4e67009..1f164a2692e 100644 --- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml +++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml @@ -29,9 +29,9 @@ action_plan_key="[null]" created_at="[null]" updated_at="[null]" - issue_creation_date="2013-04-16" - issue_update_date="2013-04-16" - issue_close_date="2013-04-16" + issue_creation_date="1366063200000" + issue_update_date="1366063200000" + issue_close_date="1366063200000" /> + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="2013-04-16"/> + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="2013-04-16"/> --> @@ -55,7 +55,7 @@ manual_severity="[false]" tags="[null]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> @@ -70,7 +70,7 @@ manual_severity="[false]" tags="[null]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> @@ -80,10 +80,10 @@ component_uuid="100" project_uuid="1" status="CLOSED" - issue_close_date="2025-01-01" + issue_close_date="1735686000000" resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/> + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="2013-04-16"/> --> diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues.xml index 38c9c3ec360..9fe05024ebd 100644 --- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues.xml +++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_all_closed_issues.xml @@ -22,12 +22,12 @@ component_uuid="100" project_uuid="1" status="CLOSED" - issue_close_date="2010-01-01" + issue_close_date="1262300400000" resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> @@ -36,12 +36,12 @@ component_uuid="1" project_uuid="1" status="CLOSED" - issue_close_date="2010-01-01" + issue_close_date="1262300400000" resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> @@ -57,7 +57,7 @@ manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> @@ -72,7 +72,7 @@ manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> @@ -81,12 +81,12 @@ component_uuid="100" project_uuid="1" status="CLOSED" - issue_close_date="2025-01-01" + issue_close_date="1735686000000" resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues-result.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues-result.xml index 1095715f65f..1b0bcc3857b 100644 --- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues-result.xml @@ -20,20 +20,20 @@ component_uuid="100" project_uuid="1" status="CLOSED" - issue_close_date="2010-01-01" + issue_close_date="1262300400000" resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="2013-04-16"/> + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="2013-04-16"/> + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="2013-04-16"/> --> @@ -45,7 +45,7 @@ issue_close_date="[null]" resolution="[null]" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" tags="[null]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="1400000000000"/> + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> @@ -56,17 +56,17 @@ issue_close_date="[null]" resolution="[null]" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" tags="[null]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" created_at="1400000000000"/> + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues.xml index eb9fc8960c5..95d56051843 100644 --- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues.xml +++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/should_delete_old_closed_issues.xml @@ -21,12 +21,12 @@ component_uuid="100" project_uuid="1" status="CLOSED" - issue_close_date="2010-01-01" + issue_close_date="1262300400000" resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> @@ -35,12 +35,12 @@ component_uuid="1" project_uuid="1" status="CLOSED" - issue_close_date="2010-01-01" + issue_close_date="1262300400000" resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> @@ -56,7 +56,7 @@ manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> @@ -71,7 +71,7 @@ manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> @@ -80,12 +80,12 @@ component_uuid="100" project_uuid="1" status="CLOSED" - issue_close_date="2025-01-01" + issue_close_date="1735686000000" resolution="FIXED" line="200" severity="BLOCKER" reporter="perceval" assignee="arthur" rule_id="500" manual_severity="[false]" message="[null]" action_plan_key="[null]" effort_to_fix="[null]" technical_debt="[null]" issue_attributes="[null]" checksum="[null]" author_login="[null]" - updated_at="[null]" issue_creation_date="2013-04-16" issue_update_date="2013-04-16" + updated_at="[null]" issue_creation_date="1366063200000" issue_update_date="1366063200000" created_at="1400000000000"/> diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java index 6be43c827ea..a43e6926800 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/DateUtils.java @@ -21,6 +21,7 @@ package org.sonar.api.utils; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java index 7aa32f3cb20..b32de7f475d 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/utils/DateUtilsTest.java @@ -107,12 +107,19 @@ public class DateUtilsTest { } @Test - public void time_to_date() throws Exception { + public void long_to_date() throws Exception { Date date = new Date(); Assertions.assertThat(DateUtils.longToDate(date.getTime())).isEqualTo(date); Assertions.assertThat(DateUtils.longToDate(null)).isNull(); } + @Test + public void date_to_long() throws Exception { + Date date = new Date(); + Assertions.assertThat(DateUtils.dateToLong(date)).isEqualTo(date.getTime()); + Assertions.assertThat(DateUtils.dateToLong(null)).isEqualTo(null); + } + /** * Cordially copied from XStream unit test * See http://koders.com/java/fid8A231D75F2C6E6909FB26BCA11C12D08AD05FB50.aspx?s=ThreadSafeDateFormatTest -- cgit v1.2.3 From e1104eb967ad973c40ac2ae7f50732aaef0cc734 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Mon, 9 Feb 2015 18:05:17 +0100 Subject: fix quality flaws --- .../org/sonar/server/measure/MeasureFilterSql.java | 108 +++++++++++---------- .../org/sonar/core/purge/PurgeableSnapshotDto.java | 3 +- 2 files changed, 56 insertions(+), 55 deletions(-) (limited to 'sonar-core') diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterSql.java b/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterSql.java index 28bdc861d91..090e63d1647 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterSql.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/MeasureFilterSql.java @@ -33,19 +33,21 @@ import org.sonar.core.persistence.dialect.Oracle; import javax.annotation.Nullable; -import java.sql.*; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.Comparator; +import java.util.Date; import java.util.List; -import static org.sonar.api.utils.DateUtils.dateToLong; - class MeasureFilterSql { private final Database database; private final MeasureFilter filter; private final MeasureFilterContext context; private final String sql; - private final List dateParameters = Lists.newArrayList(); + private final List dateParameters = Lists.newArrayList(); MeasureFilterSql(Database database, MeasureFilter filter, MeasureFilterContext context) { this.database = database; @@ -54,12 +56,56 @@ class MeasureFilterSql { this.sql = generateSql(); } + private static void appendInStatement(List values, StringBuilder to) { + to.append(" ("); + for (int i = 0; i < values.size(); i++) { + if (i > 0) { + to.append(","); + } + to.append("'"); + to.append(StringEscapeUtils.escapeSql(values.get(i))); + to.append("'"); + } + to.append(") "); + } + + private static Ordering newObjectOrdering(boolean ascending) { + if (ascending) { + return Ordering.from(new Comparator() { + @Override + public int compare(@Nullable Comparable left, @Nullable Comparable right) { + if (left == null) { + return 1; + } + if (right == null) { + return -1; + } + + return left.compareTo(right); + } + }); + } + return Ordering.from(new Comparator() { + @Override + public int compare(@Nullable Comparable left, @Nullable Comparable right) { + if (left == null) { + return 1; + } + if (right == null) { + return -1; + } + + return -left.compareTo(right); + } + }); + } + List execute(Connection connection) throws SQLException { PreparedStatement statement = connection.prepareStatement(sql); ResultSet rs = null; try { for (int index = 0; index < dateParameters.size(); index++) { - statement.setLong(index + 1, dateToLong(dateParameters.get(index))); + statement.setLong(index + 1, dateParameters.get(index)); } rs = statement.executeQuery(); return process(rs); @@ -130,15 +176,15 @@ class MeasureFilterSql { } private void appendDateConditions(StringBuilder sb) { - java.util.Date fromDate = filter.getFromDate(); + Date fromDate = filter.getFromDate(); if (fromDate != null) { sb.append(" AND s.created_at >= ? "); - dateParameters.add(new Date(fromDate.getTime())); + dateParameters.add(fromDate.getTime()); } - java.util.Date toDate = filter.getToDate(); + Date toDate = filter.getToDate(); if (toDate != null) { sb.append(" AND s.created_at <= ? "); - dateParameters.add(new Date(toDate.getTime())); + dateParameters.add(toDate.getTime()); } } @@ -208,19 +254,6 @@ class MeasureFilterSql { return rowProcessor.sort(rows, filter.sort().isAsc()); } - private static void appendInStatement(List values, StringBuilder to) { - to.append(" ("); - for (int i = 0; i < values.size(); i++) { - if (i > 0) { - to.append(","); - } - to.append("'"); - to.append(StringEscapeUtils.escapeSql(values.get(i))); - to.append("'"); - } - to.append(") "); - } - /** * Replace escape percent and underscore by adding a slash just before */ @@ -370,35 +403,4 @@ class MeasureFilterSql { return newObjectOrdering(ascending); } } - - private static Ordering newObjectOrdering(boolean ascending) { - if (ascending) { - return Ordering.from(new Comparator() { - @Override - public int compare(@Nullable Comparable left, @Nullable Comparable right) { - if (left == null) { - return 1; - } - if (right == null) { - return -1; - } - - return left.compareTo(right); - } - }); - } - return Ordering.from(new Comparator() { - @Override - public int compare(@Nullable Comparable left, @Nullable Comparable right) { - if (left == null) { - return 1; - } - if (right == null) { - return -1; - } - - return -left.compareTo(right); - } - }); - } } diff --git a/sonar-core/src/main/java/org/sonar/core/purge/PurgeableSnapshotDto.java b/sonar-core/src/main/java/org/sonar/core/purge/PurgeableSnapshotDto.java index 23acb6fa52a..e3a8793bc90 100644 --- a/sonar-core/src/main/java/org/sonar/core/purge/PurgeableSnapshotDto.java +++ b/sonar-core/src/main/java/org/sonar/core/purge/PurgeableSnapshotDto.java @@ -47,8 +47,7 @@ public class PurgeableSnapshotDto implements Comparable { } public PurgeableSnapshotDto setDate(Long aLong) { - Date date = aLong == null ? null : new Date(aLong); - this.date = date; + this.date = new Date(aLong); return this; } -- cgit v1.2.3 From 605a62f9e0342e63b55b50e76db3260dee1ef748 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Tue, 3 Feb 2015 23:06:45 +0100 Subject: SONAR-2570 compress db table FILE_SOURCES --- pom.xml | 6 + .../server/benchmark/SourceDbBenchmarkTest.java | 48 +- .../server/computation/issue/IssueComputation.java | 1 - .../server/computation/issue/SourceLinesCache.java | 50 +- .../server/db/migrations/DatabaseMigrations.java | 3 +- .../migrations/v51/FeedFileSourcesBinaryData.java | 164 ++ .../sonar/server/source/index/SourceLineDoc.java | 5 +- .../server/source/index/SourceLineIndexer.java | 4 + .../source/index/SourceLineResultSetIterator.java | 128 +- .../batch/ProjectRepositoryLoaderMediumTest.java | 2 +- .../computation/issue/SourceLinesCacheTest.java | 15 +- .../v51/FeedFileSourcesBinaryDataTest.java | 95 + .../sonar/server/source/db/FileSourceTesting.java | 108 + .../server/source/index/SourceLineIndexerTest.java | 9 +- .../index/SourceLineResultSetIteratorTest.java | 77 +- .../ComponentDaoTest/select_module_files_tree.xml | 4 +- .../issue/SourceLinesCacheTest/load_data.xml | 2 +- .../v51/FeedFileSourcesBinaryDataTest/bad_data.xml | 7 + .../v51/FeedFileSourcesBinaryDataTest/data.xml | 22 + .../v51/FeedFileSourcesBinaryDataTest/schema.sql | 12 + .../source/index/SourceLineIndexerTest/db.xml | 4 +- .../SourceLineResultSetIteratorTest/empty-file.xml | 6 - .../SourceLineResultSetIteratorTest/null-file.xml | 6 - .../SourceLineResultSetIteratorTest/schema.sql | 2 +- .../SourceLineResultSetIteratorTest/shared.xml | 4 +- .../server/source/ws/HashActionTest/shared.xml | 2 +- .../db/migrate/781_add_file_sources_binary_data.rb | 29 + .../migrate/782_feed_file_sources_binary_data.rb | 29 + .../db/migrate/783_drop_file_sources_data.rb | 30 + sonar-batch-protocol/pom.xml | 4 + .../org/sonar/server/source/db/FileSourceDb.java | 3008 ++++++++++++++++++++ .../src/main/protobuf/file_source_db.proto | 72 + .../org/sonar/batch/index/SourceDataFactory.java | 376 +++ .../org/sonar/batch/index/SourcePersister.java | 402 +-- .../org/sonar/batch/scan/ProjectScanContainer.java | 2 + .../sonar/batch/index/SourceDataFactoryTest.java | 305 ++ .../org/sonar/batch/index/SourcePersisterTest.java | 428 --- .../shouldUpdateMeasure-result.xml | 24 - .../index/SourcePersisterTest/file_sources.xml | 9 - .../file_sources_missing_src_hash.xml | 2 +- .../batch/index/SourcePersisterTest/shared.xml | 9 + .../testPersistDontTouchUnchanged-result.xml | 4 +- .../testPersistEmptyFile-result.xml | 6 +- sonar-core/pom.xml | 24 + .../sonar/core/persistence/DatabaseVersion.java | 2 +- .../org/sonar/core/source/db/FileSourceDao.java | 58 +- .../org/sonar/core/source/db/FileSourceDto.java | 120 +- .../org/sonar/core/source/db/FileSourceMapper.java | 2 +- .../org/sonar/core/persistence/rows-h2.sql | 3 + .../org/sonar/core/persistence/schema-h2.ddl | 2 +- .../org/sonar/core/source/db/FileSourceMapper.xml | 21 +- .../sonar/core/source/db/FileSourceDaoTest.java | 87 +- ...e_file_sources_of_disabled_resources-result.xml | 2 +- .../delete_file_sources_of_disabled_resources.xml | 4 +- .../PurgeDaoTest/select_purgeable_file_uuids.xml | 4 +- .../purge/PurgeDaoTest/shouldDeleteProject.xml | 2 +- .../source/db/FileSourceDaoTest/insert-result.xml | 21 +- .../core/source/db/FileSourceDaoTest/shared.xml | 10 +- .../source/db/FileSourceDaoTest/update-result.xml | 11 +- 59 files changed, 4724 insertions(+), 1174 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/source/db/FileSourceTesting.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest/bad_data.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest/data.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest/schema.sql delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/empty-file.xml delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/null-file.xml create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/781_add_file_sources_binary_data.rb create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/782_feed_file_sources_binary_data.rb create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/783_drop_file_sources_data.rb create mode 100644 sonar-batch-protocol/src/main/gen-java/org/sonar/server/source/db/FileSourceDb.java create mode 100644 sonar-batch-protocol/src/main/protobuf/file_source_db.proto create mode 100644 sonar-batch/src/main/java/org/sonar/batch/index/SourceDataFactory.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/index/SourceDataFactoryTest.java delete mode 100644 sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java delete mode 100644 sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldUpdateMeasure-result.xml delete mode 100644 sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources.xml create mode 100644 sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/shared.xml (limited to 'sonar-core') diff --git a/pom.xml b/pom.xml index 712d316e61a..57b1e8ba278 100644 --- a/pom.xml +++ b/pom.xml @@ -1088,6 +1088,12 @@ protobuf-java 2.6.1 + + net.jpountz.lz4 + lz4 + 1.3.0 + + diff --git a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceDbBenchmarkTest.java b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceDbBenchmarkTest.java index 843308ca2ea..3d6dbf32645 100644 --- a/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceDbBenchmarkTest.java +++ b/server/sonar-server-benchmarks/src/test/java/org/sonar/server/benchmark/SourceDbBenchmarkTest.java @@ -30,11 +30,13 @@ import org.sonar.core.persistence.DbTester; import org.sonar.core.source.db.FileSourceDao; import org.sonar.core.source.db.FileSourceDto; import org.sonar.server.db.DbClient; +import org.sonar.server.source.db.FileSourceDb; import org.sonar.server.source.index.SourceLineResultSetIterator; import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; +import java.util.Arrays; import java.util.Timer; import java.util.concurrent.atomic.AtomicLong; @@ -43,8 +45,9 @@ import static org.assertj.core.api.Assertions.assertThat; public class SourceDbBenchmarkTest { public static final Logger LOGGER = LoggerFactory.getLogger("benchmarkSourceDbScrolling"); - // files are 3'220 lines long + public static final int NUMBER_OF_FILES = 1000; + public static final int NUMBER_OF_LINES = 3220; public static final String PROJECT_UUID = Uuids.create(); @Rule @@ -52,7 +55,7 @@ public class SourceDbBenchmarkTest { @Test public void benchmark() throws Exception { - prepareFileSources(); + prepareTable(); scrollRows(); } @@ -70,12 +73,12 @@ public class SourceDbBenchmarkTest { SourceLineResultSetIterator it = SourceLineResultSetIterator.create(dbClient, connection, 0L); while (it.hasNext()) { SourceLineResultSetIterator.SourceFile row = it.next(); - assertThat(row.getLines().size()).isEqualTo(3220); + assertThat(row.getLines().size()).isEqualTo(NUMBER_OF_LINES); assertThat(row.getFileUuid()).isNotEmpty(); counter.incrementAndGet(); } long end = System.currentTimeMillis(); - long period = end-start; + long period = end - start; long throughputPerSecond = 1000L * counter.get() / period; LOGGER.info(String.format("%d FILE_SOURCES rows scrolled in %d ms (%d rows/second)", counter.get(), period, throughputPerSecond)); @@ -85,25 +88,50 @@ public class SourceDbBenchmarkTest { } } - private void prepareFileSources() throws IOException { + private void prepareTable() throws IOException { LOGGER.info("Populate table FILE_SOURCES"); FileSourceDao dao = new FileSourceDao(dbTester.myBatis()); for (int i = 0; i < NUMBER_OF_FILES; i++) { - dao.insert(newFileSourceDto()); + dao.insert(generateDto()); } } - private FileSourceDto newFileSourceDto() throws IOException { + private FileSourceDto generateDto() throws IOException { long now = System.currentTimeMillis(); + byte[] data = generateData(); FileSourceDto dto = new FileSourceDto(); dto.setCreatedAt(now); dto.setUpdatedAt(now); + dto.setBinaryData(data); + dto.setDataHash("49d7230271f2bd24c759e54bcd66547d"); dto.setProjectUuid(PROJECT_UUID); dto.setFileUuid(Uuids.create()); - // this fake data is 3220 lines long - dto.setData(IOUtils.toString(getClass().getResourceAsStream("SourceDbBenchmarkTest/data.txt"))); - dto.setDataHash("49d7230271f2bd24c759e54bcd66547d"); dto.setLineHashes(IOUtils.toString(getClass().getResourceAsStream("SourceDbBenchmarkTest/line_hashes.txt"))); return dto; } + + private byte[] generateData() throws IOException { + FileSourceDb.Data.Builder dataBuilder = FileSourceDb.Data.newBuilder(); + for (int i = 1; i <= NUMBER_OF_LINES; i++) { + dataBuilder.addLinesBuilder() + .setLine(i) + .setScmRevision("REVISION_" + i) + .setScmAuthor("a_guy") + .setSource("this is not java code " + i) + .setUtLineHits(i) + .setUtConditions(i+1) + .setUtCoveredConditions(i) + .setItLineHits(i) + .setItConditions(i+1) + .setItCoveredConditions(i) + .setOverallLineHits(i) + .setOverallConditions(i+1) + .setOverallCoveredConditions(i) + .setScmDate(150000000L) + .setHighlighting("2,9,k;9,18,k") + .addAllDuplications(Arrays.asList(19,33,141)) + .build(); + } + return FileSourceDto.encodeData(dataBuilder.build()); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java index c6288054115..b503e2c74ac 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java @@ -55,7 +55,6 @@ public class IssueComputation { guessAuthor(issue); autoAssign(issue); copyRuleTags(issue); - // TODO execute extension points } diskIssuesAppender.append(issue); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/SourceLinesCache.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/SourceLinesCache.java index 5621a7fe25f..25d38307c38 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/SourceLinesCache.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/SourceLinesCache.java @@ -20,21 +20,16 @@ package org.sonar.server.computation.issue; import com.google.common.base.Function; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVRecord; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; -import org.sonar.api.utils.DateUtils; import org.sonar.core.source.db.FileSourceDto; import org.sonar.server.db.DbClient; +import org.sonar.server.source.db.FileSourceDb; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.io.Reader; +import java.io.InputStream; import java.util.ArrayList; -import java.util.Date; import java.util.List; /** @@ -53,7 +48,11 @@ public class SourceLinesCache { private final List authors = new ArrayList<>(); private boolean loaded = false; private String currentFileUuid = null; + + // date of the latest commit on the file private long lastCommitDate = 0L; + + // author of the latest commit on the file private String lastCommitAuthor = null; public SourceLinesCache(DbClient dbClient) { @@ -114,30 +113,25 @@ public class SourceLinesCache { return authors.size(); } - class FileDataParser implements Function { + /** + * Parse lines from db and collect SCM information + */ + class FileDataParser implements Function { @Override - public Void apply(Reader input) { - CSVParser csvParser = null; - try { - csvParser = new CSVParser(input, CSVFormat.DEFAULT); - for (CSVRecord csvRecord : csvParser) { - Date revisionDate = DateUtils.parseDateTimeQuietly(csvRecord.get(FileSourceDto.CSV_INDEX_SCM_DATE)); - - // do not keep all fields in memory - String author = csvRecord.get(FileSourceDto.CSV_INDEX_SCM_AUTHOR); - authors.add(author); - - if (revisionDate != null && revisionDate.getTime() > lastCommitDate) { - lastCommitDate = revisionDate.getTime(); - lastCommitAuthor = author; - } + public Void apply(InputStream input) { + FileSourceDb.Data data = FileSourceDto.decodeData(input); + for (FileSourceDb.Line line : data.getLinesList()) { + String author = null; + if (line.hasScmAuthor()) { + author = line.getScmAuthor(); + } + authors.add(author); + if (line.hasScmDate() && line.getScmDate() > lastCommitDate && author != null) { + lastCommitDate = line.getScmDate(); + lastCommitAuthor = author; } - return null; - } catch (Exception e) { - throw new IllegalStateException("Fail to parse CSV data", e); - } finally { - IOUtils.closeQuietly(csvParser); } + return null; } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java index d61f389ca5e..43d9dfbdb9c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java @@ -86,6 +86,7 @@ public interface DatabaseMigrations { UpdateProjectsModuleUuidPath.class, FeedIssueComponentUuids.class, FeedSnapshotsLongDates.class, - FeedIssuesLongDates.class + FeedIssuesLongDates.class, + FeedFileSourcesBinaryData.class ); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java new file mode 100644 index 00000000000..b80abfd3789 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java @@ -0,0 +1,164 @@ +/* + * 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.db.migrations.v51; + +import com.google.common.base.Function; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.DateUtils; +import org.sonar.core.persistence.Database; +import org.sonar.core.source.db.FileSourceDto; +import org.sonar.server.db.migrations.BaseDataChange; +import org.sonar.server.db.migrations.MassUpdate; +import org.sonar.server.db.migrations.Select; +import org.sonar.server.db.migrations.SqlStatement; +import org.sonar.server.source.db.FileSourceDb; + +import javax.annotation.Nullable; + +import java.sql.SQLException; +import java.util.Iterator; + +public class FeedFileSourcesBinaryData extends BaseDataChange { + + public FeedFileSourcesBinaryData(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate update = context.prepareMassUpdate().rowPluralName("issues"); + update.select("SELECT id,data FROM file_sources WHERE binary_data is null"); + update.update("UPDATE file_sources SET binary_data=? WHERE id=?"); + update.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Long fileSourceId = row.getLong(1); + update.setBytes(1, toBinary(fileSourceId, row.getString(2))); + update.setLong(2, fileSourceId); + return true; + } + }); + } + + private byte[] toBinary(Long fileSourceId, @Nullable String data) { + FileSourceDb.Data.Builder dataBuilder = FileSourceDb.Data.newBuilder(); + CSVParser parser = null; + try { + if (data != null) { + parser = CSVParser.parse(data, CSVFormat.DEFAULT); + Iterator rows = parser.iterator(); + int line = 1; + while (rows.hasNext()) { + CSVRecord row = rows.next(); + if (row.size() == 16) { + + FileSourceDb.Line.Builder lineBuilder = dataBuilder.addLinesBuilder(); + lineBuilder.setLine(line); + String s = row.get(0); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setScmRevision(s); + } + s = row.get(1); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setScmAuthor(s); + } + s = row.get(2); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setScmDate(DateUtils.parseDateTimeQuietly(s).getTime()); + } + s = row.get(3); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setUtLineHits(Integer.parseInt(s)); + } + s = row.get(4); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setUtConditions(Integer.parseInt(s)); + } + s = row.get(5); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setUtCoveredConditions(Integer.parseInt(s)); + } + s = row.get(6); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setItLineHits(Integer.parseInt(s)); + } + s = row.get(7); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setItConditions(Integer.parseInt(s)); + } + s = row.get(8); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setItCoveredConditions(Integer.parseInt(s)); + } + s = row.get(9); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setOverallLineHits(Integer.parseInt(s)); + } + s = row.get(10); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setOverallConditions(Integer.parseInt(s)); + } + s = row.get(11); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setOverallCoveredConditions(Integer.parseInt(s)); + } + s = row.get(12); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setHighlighting(s); + } + s = row.get(13); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setSymbols(s); + } + s = row.get(14); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.addAllDuplications(splitIntegers(s)); + } + s = row.get(15); + if (s != null) { + lineBuilder.setSource(s); + } + } + line++; + } + } + return FileSourceDto.encodeData(dataBuilder.build()); + } catch (Exception e) { + throw new IllegalStateException("Invalid FILE_SOURCES.DATA on row with ID " + fileSourceId + ": " + data, e); + } finally { + IOUtils.closeQuietly(parser); + } + } + + private static Iterable splitIntegers(String s) { + return Iterables.transform(Splitter.on(',').split(s), new Function() { + @Override + public Integer apply(String input) { + return Integer.parseInt(input); + } + }); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineDoc.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineDoc.java index 822f1d3297b..45fb14630c4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineDoc.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineDoc.java @@ -20,7 +20,6 @@ package org.sonar.server.source.index; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; import org.sonar.server.search.BaseDoc; import org.sonar.server.search.BaseNormalizer; import org.sonar.server.search.IndexUtils; @@ -30,6 +29,7 @@ import javax.annotation.Nullable; import java.util.Collection; import java.util.Date; +import java.util.HashMap; import java.util.Map; public class SourceLineDoc extends BaseDoc { @@ -38,9 +38,8 @@ public class SourceLineDoc extends BaseDoc { super(fields); } - // For testing purpose public SourceLineDoc() { - this(Maps.newHashMap()); + this(new HashMap(20)); } public String projectUuid() { diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java index cf00b9dd7fd..3e7ce485a15 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineIndexer.java @@ -34,6 +34,10 @@ import java.util.Iterator; import static org.sonar.server.source.index.SourceLineIndexDefinition.FIELD_FILE_UUID; import static org.sonar.server.source.index.SourceLineIndexDefinition.FIELD_PROJECT_UUID; +/** + * Add to Elasticsearch index {@link SourceLineIndexDefinition} the rows of + * db table FILE_SOURCES that are not indexed yet + */ public class SourceLineIndexer extends BaseIndexer { private final DbClient dbClient; diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java index 5f44a4013fc..e51d3126819 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/index/SourceLineResultSetIterator.java @@ -20,31 +20,18 @@ package org.sonar.server.source.index; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVRecord; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.math.NumberUtils; -import org.sonar.api.utils.DateUtils; import org.sonar.core.source.db.FileSourceDto; import org.sonar.server.db.DbClient; import org.sonar.server.db.ResultSetIterator; -import org.sonar.server.db.migrations.SqlUtil; +import org.sonar.server.source.db.FileSourceDb; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - -import java.io.IOException; -import java.io.Reader; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; import java.util.List; -import java.util.StringTokenizer; /** * Scroll over table FILE_SOURCES and directly parse CSV field required to @@ -84,7 +71,7 @@ public class SourceLineResultSetIterator extends ResultSetIteratornewHashMap()); - - doc.setProjectUuid(projectUuid); - doc.setFileUuid(fileUuid); - doc.setLine(line); - doc.setUpdateDate(updatedDate); - doc.setScmRevision(csvRecord.get(FileSourceDto.CSV_INDEX_SCM_REVISION)); - doc.setScmAuthor(csvRecord.get(FileSourceDto.CSV_INDEX_SCM_AUTHOR)); - doc.setScmDate(DateUtils.parseDateTimeQuietly(csvRecord.get(FileSourceDto.CSV_INDEX_SCM_DATE))); - // UT - doc.setUtLineHits(parseIntegerFromRecord(csvRecord.get(FileSourceDto.CSV_INDEX_UT_LINE_HITS))); - doc.setUtConditions(parseIntegerFromRecord(csvRecord.get(FileSourceDto.CSV_INDEX_UT_CONDITIONS))); - doc.setUtCoveredConditions(parseIntegerFromRecord(csvRecord.get(FileSourceDto.CSV_INDEX_UT_COVERED_CONDITIONS))); - // IT - doc.setItLineHits(parseIntegerFromRecord(csvRecord.get(FileSourceDto.CSV_INDEX_IT_LINE_HITS))); - doc.setItConditions(parseIntegerFromRecord(csvRecord.get(FileSourceDto.CSV_INDEX_IT_CONDITIONS))); - doc.setItCoveredConditions(parseIntegerFromRecord(csvRecord.get(FileSourceDto.CSV_INDEX_IT_COVERED_CONDITIONS))); - // OVERALL - doc.setOverallLineHits(parseIntegerFromRecord(csvRecord.get(FileSourceDto.CSV_INDEX_OVERALL_LINE_HITS))); - doc.setOverallConditions(parseIntegerFromRecord(csvRecord.get(FileSourceDto.CSV_INDEX_OVERALL_CONDITIONS))); - doc.setOverallCoveredConditions(parseIntegerFromRecord(csvRecord.get(FileSourceDto.CSV_INDEX_OVERALL_COVERED_CONDITIONS))); - - doc.setHighlighting(csvRecord.get(FileSourceDto.CSV_INDEX_HIGHLIGHTING)); - doc.setSymbols(csvRecord.get(FileSourceDto.CSV_INDEX_SYMBOLS)); - doc.setDuplications(parseDuplications(csvRecord.get(FileSourceDto.CSV_INDEX_DUPLICATIONS))); - - // source is always the latest field. All future fields will be added between duplications (14) and source. - doc.setSource(csvRecord.get(csvRecord.size()-1)); - - result.addLine(doc); - - line++; - } - } catch (IOException ioError) { - throw new IllegalStateException("Impossible to open stream for file_sources.data with file_uuid " + fileUuid, ioError); - } catch (ArrayIndexOutOfBoundsException lineError) { - throw new IllegalStateException( - String.format("Impossible to parse source line data, stuck at line %d", line), lineError); - } finally { - IOUtils.closeQuietly(csv); - IOUtils.closeQuietly(csvParser); + SourceFile result = new SourceFile(fileUuid, updatedAt); + FileSourceDb.Data data = FileSourceDto.decodeData(rs.getBinaryStream(4)); + for (FileSourceDb.Line line : data.getLinesList()) { + SourceLineDoc doc = new SourceLineDoc(); + doc.setProjectUuid(projectUuid); + doc.setFileUuid(fileUuid); + doc.setLine(line.getLine()); + doc.setUpdateDate(updatedDate); + doc.setScmRevision(line.getScmRevision()); + doc.setScmAuthor(line.getScmAuthor()); + doc.setScmDate(line.hasScmDate() ? new Date(line.getScmDate()) : null); + // UT + doc.setUtLineHits(line.hasUtLineHits() ? line.getUtLineHits() : null); + doc.setUtConditions(line.hasUtConditions() ? line.getUtConditions() : null); + doc.setUtCoveredConditions(line.hasUtCoveredConditions() ? line.getUtCoveredConditions() : null); + // IT + doc.setItLineHits(line.hasItLineHits() ? line.getItLineHits() : null); + doc.setItConditions(line.hasItConditions() ? line.getItConditions() : null); + doc.setItCoveredConditions(line.hasItCoveredConditions() ? line.getItCoveredConditions() : null); + // OVERALL + doc.setOverallLineHits(line.hasOverallLineHits() ? line.getOverallLineHits() : null); + doc.setOverallConditions(line.hasOverallConditions() ? line.getOverallConditions() : null); + doc.setOverallCoveredConditions(line.hasOverallCoveredConditions() ? line.getOverallCoveredConditions() : null); + + doc.setHighlighting(line.hasHighlighting() ? line.getHighlighting() : null); + doc.setSymbols(line.hasSymbols() ? line.getSymbols() : null); + doc.setDuplications(line.getDuplicationsList()); + + // source is always the latest field. All future fields will be added between duplications (14) and source. + doc.setSource(line.hasSource() ? line.getSource() : null); + + result.addLine(doc); } - return result; } - - private List parseDuplications(@Nullable String duplications) { - List dups = Lists.newArrayList(); - if (StringUtils.isNotEmpty(duplications)) { - StringTokenizer tokenizer = new StringTokenizer(duplications, ",", false); - while (tokenizer.hasMoreTokens()) { - dups.add(NumberUtils.toInt(tokenizer.nextToken(), -1)); - } - } - return dups; - } - - @CheckForNull - private Integer parseIntegerFromRecord(@Nullable String cellValue) { - if (cellValue == null || cellValue.isEmpty()) { - return null; - } else { - return Integer.parseInt(cellValue); - } - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java index 6bee60a5d28..1a1f7a62003 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java @@ -785,7 +785,7 @@ public class ProjectRepositoryLoaderMediumTest { return new FileSourceDto() .setFileUuid(file.uuid()) .setProjectUuid(file.projectUuid()) - .setData(",,,,,,,,,,,,,,,unchanged ,,,,,,,,,,,,,,,content ") + //.setData(",,,,,,,,,,,,,,,unchanged ,,,,,,,,,,,,,,,content ") .setDataHash("0263047cd758c68c27683625f072f010") .setLineHashes("8d7b3d6b83c0a517eac07e1aac94b773") .setCreatedAt(System.currentTimeMillis()) diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceLinesCacheTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceLinesCacheTest.java index b7c559e5a17..aed64514efb 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceLinesCacheTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceLinesCacheTest.java @@ -25,8 +25,12 @@ import org.junit.experimental.categories.Category; import org.sonar.core.persistence.DbTester; import org.sonar.core.source.db.FileSourceDao; import org.sonar.server.db.DbClient; +import org.sonar.server.source.db.FileSourceDb; +import org.sonar.server.source.db.FileSourceTesting; import org.sonar.test.DbTests; +import java.sql.Connection; + import static org.assertj.core.api.Assertions.assertThat; @Category(DbTests.class) @@ -38,6 +42,15 @@ public class SourceLinesCacheTest { @Test public void line_author() throws Exception { dbTester.prepareDbUnit(getClass(), "load_data.xml"); + FileSourceDb.Data.Builder data = FileSourceDb.Data.newBuilder(); + data.addLinesBuilder().setLine(1).setScmAuthor("charb").setScmDate(1_400_000_000_000L); + data.addLinesBuilder().setLine(2).setScmAuthor("cabu").setScmDate(1_500_000_000_000L); + data.addLinesBuilder().setLine(3).setScmAuthor("wolinski").setScmDate(1_300_000_000_000L); + data.addLinesBuilder().setLine(4); + try (Connection connection = dbTester.openConnection()) { + FileSourceTesting.updateDataColumn(connection, "FILE_A", data.build()); + } + DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new FileSourceDao(dbTester.myBatis())); SourceLinesCache cache = new SourceLinesCache(dbClient); cache.init("FILE_A"); @@ -55,12 +68,10 @@ public class SourceLinesCacheTest { // only 4 lines in the file -> return last committer on file assertThat(cache.lineAuthor(100)).isEqualTo("cabu"); - assertThat(cache.countLines()).isEqualTo(4); cache.clear(); assertThat(cache.countLines()).isEqualTo(0); } - } diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest.java new file mode 100644 index 00000000000..80fc3c07db9 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest.java @@ -0,0 +1,95 @@ +/* + * 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.db.migrations.v51; + +import org.apache.commons.dbutils.DbUtils; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.core.persistence.DbTester; +import org.sonar.core.source.db.FileSourceDto; +import org.sonar.server.db.migrations.DatabaseMigration; +import org.sonar.server.source.db.FileSourceDb; + +import java.io.InputStream; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; + +public class FeedFileSourcesBinaryDataTest { + + @ClassRule + public static DbTester db = new DbTester().schema(FeedFileSourcesBinaryDataTest.class, "schema.sql"); + + @Test + public void convert_csv_to_protobuf() throws Exception { + db.prepareDbUnit(getClass(), "data.xml"); + + DatabaseMigration migration = new FeedFileSourcesBinaryData(db.database()); + migration.execute(); + + int count = db.countSql("select count(*) from file_sources where binary_data is not null"); + assertThat(count).isEqualTo(3); + + try(Connection connection = db.openConnection()) { + FileSourceDb.Data data = selectData(connection, 1L); + assertThat(data.getLinesCount()).isEqualTo(4); + assertThat(data.getLines(0).getScmRevision()).isEqualTo("aef12a"); + + data = selectData(connection, 2L); + assertThat(data.getLinesCount()).isEqualTo(4); + assertThat(data.getLines(0).hasScmRevision()).isFalse(); + + data = selectData(connection, 3L); + assertThat(data.getLinesCount()).isEqualTo(0); + } + } + + @Test + public void fail_to_parse_csv() throws Exception { + db.prepareDbUnit(getClass(), "bad_data.xml"); + + DatabaseMigration migration = new FeedFileSourcesBinaryData(db.database()); + try { + migration.execute(); + fail(); + } catch (IllegalStateException e) { + assertThat(e).hasMessageContaining("Invalid FILE_SOURCES.DATA on row with ID 1:"); + } + } + + private FileSourceDb.Data selectData(Connection connection, long fileSourceId) throws SQLException { + PreparedStatement pstmt = connection.prepareStatement("select binary_data from file_sources where id=?"); + ResultSet rs = null; + try { + pstmt.setLong(1, fileSourceId); + rs = pstmt.executeQuery(); + rs.next(); + InputStream data = rs.getBinaryStream(1); + return FileSourceDto.decodeData(data); + } finally { + DbUtils.closeQuietly(rs); + DbUtils.closeQuietly(pstmt); + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/db/FileSourceTesting.java b/server/sonar-server/src/test/java/org/sonar/server/source/db/FileSourceTesting.java new file mode 100644 index 00000000000..10c44e5d259 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/source/db/FileSourceTesting.java @@ -0,0 +1,108 @@ +/* + * 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.source.db; + +import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang.math.RandomUtils; +import org.sonar.core.source.db.FileSourceDto; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Arrays; + +public class FileSourceTesting { + + private FileSourceTesting() { + // only static stuff + } + + public static void updateDataColumn(Connection connection, String fileUuid, FileSourceDb.Data data) throws SQLException { + updateDataColumn(connection, fileUuid, FileSourceDto.encodeData(data)); + } + + public static void updateDataColumn(Connection connection, String fileUuid, byte[] data) throws SQLException { + PreparedStatement stmt = connection.prepareStatement("UPDATE file_sources SET binary_data = ? WHERE file_uuid=?"); + stmt.setBytes(1, data); + stmt.setString(2, fileUuid); + stmt.executeUpdate(); + stmt.close(); + connection.commit(); + } + + /** + * Generate predefined fake data. Result is mutable. + */ + public static FileSourceDb.Data.Builder newFakeData(int numberOfLines) throws IOException { + FileSourceDb.Data.Builder dataBuilder = FileSourceDb.Data.newBuilder(); + for (int i = 1; i <= numberOfLines; i++) { + dataBuilder.addLinesBuilder() + .setLine(i) + .setScmRevision("REVISION_" + i) + .setScmAuthor("AUTHOR_" + i) + .setScmDate(1_500_000_000_00L + i) + .setSource("SOURCE_" + i) + .setUtLineHits(i) + .setUtConditions(i + 1) + .setUtCoveredConditions(i + 2) + .setItLineHits(i + 3) + .setItConditions(i + 4) + .setItCoveredConditions(i + 5) + .setOverallLineHits(i + 6) + .setOverallConditions(i + 7) + .setOverallCoveredConditions(i + 8) + .setHighlighting("HIGHLIGHTING_" + i) + .setSymbols("SYMBOLS_" + i) + .addAllDuplications(Arrays.asList(i)) + .build(); + } + return dataBuilder; + } + + /** + * Generate random data. Result is mutable. + */ + public static FileSourceDb.Data.Builder newRandomData(int numberOfLines) throws IOException { + FileSourceDb.Data.Builder dataBuilder = FileSourceDb.Data.newBuilder(); + for (int i = 1; i <= numberOfLines; i++) { + dataBuilder.addLinesBuilder() + .setLine(i) + .setScmRevision(RandomStringUtils.randomAlphanumeric(15)) + .setScmAuthor(RandomStringUtils.randomAlphanumeric(10)) + .setScmDate(RandomUtils.nextLong()) + .setSource(RandomStringUtils.randomAlphanumeric(20)) + .setUtLineHits(RandomUtils.nextInt(4)) + .setUtConditions(RandomUtils.nextInt(4)) + .setUtCoveredConditions(RandomUtils.nextInt(4)) + .setItLineHits(RandomUtils.nextInt(4)) + .setItConditions(RandomUtils.nextInt(4)) + .setItCoveredConditions(RandomUtils.nextInt(4)) + .setOverallLineHits(RandomUtils.nextInt(4)) + .setOverallConditions(RandomUtils.nextInt(4)) + .setOverallCoveredConditions(RandomUtils.nextInt(4)) + .setHighlighting(RandomStringUtils.randomAlphanumeric(40)) + .setSymbols(RandomStringUtils.randomAlphanumeric(30)) + .addAllDuplications(Arrays.asList(RandomUtils.nextInt(200), RandomUtils.nextInt(200))) + .build(); + } + return dataBuilder; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java index ed3a5dc49c2..35ae780b0ba 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineIndexerTest.java @@ -38,10 +38,12 @@ import org.sonar.core.persistence.DbTester; import org.sonar.server.db.DbClient; import org.sonar.server.es.EsTester; import org.sonar.server.search.BaseNormalizer; +import org.sonar.server.source.db.FileSourceTesting; import org.sonar.test.DbTests; import org.sonar.test.TestUtils; import java.io.FileInputStream; +import java.sql.Connection; import java.util.Date; import java.util.List; import java.util.Map; @@ -69,8 +71,13 @@ public class SourceLineIndexerTest { @Test public void index_source_lines_from_db() throws Exception { db.prepareDbUnit(getClass(), "db.xml"); + + Connection connection = db.openConnection(); + FileSourceTesting.updateDataColumn(connection, "FILE_UUID", FileSourceTesting.newRandomData(3).build()); + connection.close(); + indexer.index(); - assertThat(countDocuments()).isEqualTo(2); + assertThat(countDocuments()).isEqualTo(3); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java index 5d19f58a2b1..aaa10a7ae73 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/source/index/SourceLineResultSetIteratorTest.java @@ -26,10 +26,10 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.sonar.core.persistence.DbTester; import org.sonar.server.db.DbClient; +import org.sonar.server.source.db.FileSourceTesting; import org.sonar.test.DbTests; import java.sql.Connection; -import java.sql.PreparedStatement; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; @@ -56,39 +56,32 @@ public class SourceLineResultSetIteratorTest { } @Test - public void should_generate_source_line_documents() throws Exception { + public void parse_db_and_generate_source_line_documents() throws Exception { db.prepareDbUnit(getClass(), "shared.xml"); - PreparedStatement stmt = connection.prepareStatement("UPDATE file_sources SET data = ? WHERE id=1"); - stmt.setString(1, "aef12a,alice,2014-04-25T12:34:56+0100,1,0,0,2,0,0,3,0,0,polop,palap,,class Foo {\r\n" + - "abe465,bob,2014-07-25T12:34:56+0100,,,,,,,,,,,,, // Empty\r\n" + - "afb789,carol,2014-03-23T12:34:56+0100,,,,,,,,,,,,,}\r\n" + - "afb789,carol,2014-03-23T12:34:56+0100,,,,,,,,,,,,,\r\n"); - stmt.executeUpdate(); + FileSourceTesting.updateDataColumn(connection, "FILE_UUID", FileSourceTesting.newFakeData(3).build()); SourceLineResultSetIterator iterator = SourceLineResultSetIterator.create(dbClient, connection, 0L); assertThat(iterator.hasNext()).isTrue(); SourceLineResultSetIterator.SourceFile file = iterator.next(); - assertThat(file.getLines()).hasSize(4); + assertThat(file.getLines()).hasSize(3); SourceLineDoc firstLine = file.getLines().get(0); - assertThat(firstLine.projectUuid()).isEqualTo("uuid-MyProject"); - assertThat(firstLine.fileUuid()).isEqualTo("uuid-MyFile.xoo"); + assertThat(firstLine.projectUuid()).isEqualTo("PROJECT_UUID"); + assertThat(firstLine.fileUuid()).isEqualTo("FILE_UUID"); assertThat(firstLine.line()).isEqualTo(1); - assertThat(firstLine.scmRevision()).isEqualTo("aef12a"); - assertThat(firstLine.scmAuthor()).isEqualTo("alice"); - // TODO Sanitize usage of fscking dates - // assertThat(firstLine.scmDate()).isEqualTo(DateUtils.parseDateTime("2014-04-25T12:34:56+0100")); - assertThat(firstLine.highlighting()).isEqualTo("polop"); - assertThat(firstLine.symbols()).isEqualTo("palap"); - assertThat(firstLine.source()).isEqualTo("class Foo {"); + assertThat(firstLine.scmRevision()).isEqualTo("REVISION_1"); + assertThat(firstLine.scmAuthor()).isEqualTo("AUTHOR_1"); + assertThat(firstLine.highlighting()).isEqualTo("HIGHLIGHTING_1"); + assertThat(firstLine.symbols()).isEqualTo("SYMBOLS_1"); + assertThat(firstLine.source()).isEqualTo("SOURCE_1"); assertThat(firstLine.utLineHits()).isEqualTo(1); - assertThat(firstLine.utConditions()).isEqualTo(0); - assertThat(firstLine.utCoveredConditions()).isEqualTo(0); - assertThat(firstLine.itLineHits()).isEqualTo(2); - assertThat(firstLine.itConditions()).isEqualTo(0); - assertThat(firstLine.itCoveredConditions()).isEqualTo(0); - assertThat(firstLine.overallLineHits()).isEqualTo(3); - assertThat(firstLine.overallConditions()).isEqualTo(0); - assertThat(firstLine.overallCoveredConditions()).isEqualTo(0); + assertThat(firstLine.utConditions()).isEqualTo(2); + assertThat(firstLine.utCoveredConditions()).isEqualTo(3); + assertThat(firstLine.itLineHits()).isEqualTo(4); + assertThat(firstLine.itConditions()).isEqualTo(5); + assertThat(firstLine.itCoveredConditions()).isEqualTo(6); + assertThat(firstLine.overallLineHits()).isEqualTo(7); + assertThat(firstLine.overallConditions()).isEqualTo(8); + assertThat(firstLine.overallCoveredConditions()).isEqualTo(9); iterator.close(); } @@ -102,36 +95,10 @@ public class SourceLineResultSetIteratorTest { } @Test - public void parse_empty_file() throws Exception { - db.prepareDbUnit(getClass(), "empty-file.xml"); - - SourceLineResultSetIterator iterator = SourceLineResultSetIterator.create(dbClient, connection, 0L); - assertThat(iterator.hasNext()).isTrue(); - SourceLineResultSetIterator.SourceFile file = iterator.next(); - assertThat(file.getFileUuid()).isEqualTo("uuid-MyFile.xoo"); - assertThat(file.getLines()).isEmpty(); - iterator.close(); - } - - @Test - public void parse_null_file() throws Exception { - db.prepareDbUnit(getClass(), "null-file.xml"); - - SourceLineResultSetIterator iterator = SourceLineResultSetIterator.create(dbClient, connection, 0L); - assertThat(iterator.hasNext()).isTrue(); - SourceLineResultSetIterator.SourceFile file = iterator.next(); - assertThat(file.getFileUuid()).isEqualTo("uuid-MyFile.xoo"); - assertThat(file.getLines()).isEmpty(); - iterator.close(); - } - - @Test - public void should_fail_on_bad_csv() throws Exception { + public void should_fail_on_bad_data_format() throws Exception { db.prepareDbUnit(getClass(), "shared.xml"); - PreparedStatement stmt = connection.prepareStatement("UPDATE file_sources SET data = ? WHERE id=1"); - stmt.setString(1, "plouf"); - stmt.executeUpdate(); - stmt.close(); + + FileSourceTesting.updateDataColumn(connection, "FILE_UUID", "THIS_IS_NOT_PROTOBUF".getBytes()); SourceLineResultSetIterator iterator = SourceLineResultSetIterator.create(dbClient, connection, 0L); try { diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/select_module_files_tree.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/select_module_files_tree.xml index e1ea8f31504..443b1d0a5ae 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/select_module_files_tree.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/select_module_files_tree.xml @@ -20,7 +20,7 @@ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="src/org/struts/pom.xml" authorization_updated_at="[null]"/> + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest/data.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest/data.xml new file mode 100644 index 00000000000..fe6020ae9bb --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest/data.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest/schema.sql new file mode 100644 index 00000000000..5649f795864 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryDataTest/schema.sql @@ -0,0 +1,12 @@ +CREATE TABLE "FILE_SOURCES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "PROJECT_UUID" VARCHAR(50) NOT NULL, + "FILE_UUID" VARCHAR(50) NOT NULL, + "LINE_HASHES" CLOB(2147483647), + "DATA" CLOB(2147483647), + "DATA_HASH" VARCHAR(50) NOT NULL, + "SRC_HASH" VARCHAR(50) NULL, + "CREATED_AT" BIGINT NOT NULL, + "UPDATED_AT" BIGINT NOT NULL, + "BINARY_DATA" BINARY(167772150), +); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/db.xml b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/db.xml index d8d7eed7662..48e58478ab6 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/db.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineIndexerTest/db.xml @@ -1,6 +1,6 @@ - + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/empty-file.xml b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/empty-file.xml deleted file mode 100644 index cf6e01583dd..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/empty-file.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/null-file.xml b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/null-file.xml deleted file mode 100644 index 91dafded3dc..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/null-file.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/schema.sql index 3032e93f256..859eefe3625 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/schema.sql +++ b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/schema.sql @@ -3,7 +3,7 @@ CREATE TABLE "FILE_SOURCES" ( "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "PROJECT_UUID" VARCHAR(50) NOT NULL, "FILE_UUID" VARCHAR(50) NOT NULL, - "DATA" CLOB(2147483647), + "BINARY_DATA" BINARY(167772150), "DATA_HASH" VARCHAR(50) NOT NULL, "CREATED_AT" BIGINT NOT NULL, "UPDATED_AT" BIGINT NOT NULL diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/shared.xml index f1498ba9c56..521e0db9ca5 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/shared.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/source/index/SourceLineResultSetIteratorTest/shared.xml @@ -1,6 +1,6 @@ - + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/ws/HashActionTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/source/ws/HashActionTest/shared.xml index 756dbacf84e..4dcc5c21ee0 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/source/ws/HashActionTest/shared.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/source/ws/HashActionTest/shared.xml @@ -5,7 +5,7 @@ path="src/main/java/Action.java"/> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/781_add_file_sources_binary_data.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/781_add_file_sources_binary_data.rb new file mode 100644 index 00000000000..3b969e3967e --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/781_add_file_sources_binary_data.rb @@ -0,0 +1,29 @@ +# +# 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. +# + +# +# SonarQube 5.1 +# +class AddFileSourcesBinaryData < ActiveRecord::Migration + + def self.up + add_column 'file_sources', :binary_data, :binary, :null => true + end +end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/782_feed_file_sources_binary_data.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/782_feed_file_sources_binary_data.rb new file mode 100644 index 00000000000..dea83cfc92b --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/782_feed_file_sources_binary_data.rb @@ -0,0 +1,29 @@ +# +# 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. +# + +# +# SonarQube 5.1 +# +class FeedFileSourcesBinaryData < ActiveRecord::Migration + + def self.up + execute_java_migration('org.sonar.server.db.migrations.v51.FeedFileSourcesBinaryData') + end +end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/783_drop_file_sources_data.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/783_drop_file_sources_data.rb new file mode 100644 index 00000000000..06d5fbb598e --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/783_drop_file_sources_data.rb @@ -0,0 +1,30 @@ +# +# 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. +# + +# +# SonarQube 5.1 +# +class DropFileSourcesData < ActiveRecord::Migration + + def self.up + remove_column 'file_sources', 'data' + end + +end diff --git a/sonar-batch-protocol/pom.xml b/sonar-batch-protocol/pom.xml index 399c4a242e0..343d707f094 100644 --- a/sonar-batch-protocol/pom.xml +++ b/sonar-batch-protocol/pom.xml @@ -13,6 +13,10 @@ Classes used for communication between batch and server + + net.jpountz.lz4 + lz4 + com.google.protobuf protobuf-java diff --git a/sonar-batch-protocol/src/main/gen-java/org/sonar/server/source/db/FileSourceDb.java b/sonar-batch-protocol/src/main/gen-java/org/sonar/server/source/db/FileSourceDb.java new file mode 100644 index 00000000000..f3f9db893b2 --- /dev/null +++ b/sonar-batch-protocol/src/main/gen-java/org/sonar/server/source/db/FileSourceDb.java @@ -0,0 +1,3008 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: file_source_db.proto + +package org.sonar.server.source.db; + +public final class FileSourceDb { + private FileSourceDb() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface LineOrBuilder extends + // @@protoc_insertion_point(interface_extends:org.sonar.server.source.db.Line) + com.google.protobuf.MessageOrBuilder { + + /** + * optional int32 line = 1; + */ + boolean hasLine(); + /** + * optional int32 line = 1; + */ + int getLine(); + + /** + * optional string source = 2; + */ + boolean hasSource(); + /** + * optional string source = 2; + */ + java.lang.String getSource(); + /** + * optional string source = 2; + */ + com.google.protobuf.ByteString + getSourceBytes(); + + /** + * optional string scm_revision = 3; + * + *

+     * SCM
+     * 
+ */ + boolean hasScmRevision(); + /** + * optional string scm_revision = 3; + * + *
+     * SCM
+     * 
+ */ + java.lang.String getScmRevision(); + /** + * optional string scm_revision = 3; + * + *
+     * SCM
+     * 
+ */ + com.google.protobuf.ByteString + getScmRevisionBytes(); + + /** + * optional string scm_author = 4; + */ + boolean hasScmAuthor(); + /** + * optional string scm_author = 4; + */ + java.lang.String getScmAuthor(); + /** + * optional string scm_author = 4; + */ + com.google.protobuf.ByteString + getScmAuthorBytes(); + + /** + * optional int64 scm_date = 5; + */ + boolean hasScmDate(); + /** + * optional int64 scm_date = 5; + */ + long getScmDate(); + + /** + * optional int32 ut_line_hits = 6; + * + *
+     * unit testing
+     * 
+ */ + boolean hasUtLineHits(); + /** + * optional int32 ut_line_hits = 6; + * + *
+     * unit testing
+     * 
+ */ + int getUtLineHits(); + + /** + * optional int32 ut_conditions = 7; + */ + boolean hasUtConditions(); + /** + * optional int32 ut_conditions = 7; + */ + int getUtConditions(); + + /** + * optional int32 ut_covered_conditions = 8; + */ + boolean hasUtCoveredConditions(); + /** + * optional int32 ut_covered_conditions = 8; + */ + int getUtCoveredConditions(); + + /** + * optional int32 it_line_hits = 9; + * + *
+     * integration testing
+     * 
+ */ + boolean hasItLineHits(); + /** + * optional int32 it_line_hits = 9; + * + *
+     * integration testing
+     * 
+ */ + int getItLineHits(); + + /** + * optional int32 it_conditions = 10; + */ + boolean hasItConditions(); + /** + * optional int32 it_conditions = 10; + */ + int getItConditions(); + + /** + * optional int32 it_covered_conditions = 11; + */ + boolean hasItCoveredConditions(); + /** + * optional int32 it_covered_conditions = 11; + */ + int getItCoveredConditions(); + + /** + * optional int32 overall_line_hits = 12; + * + *
+     * overall testing
+     * 
+ */ + boolean hasOverallLineHits(); + /** + * optional int32 overall_line_hits = 12; + * + *
+     * overall testing
+     * 
+ */ + int getOverallLineHits(); + + /** + * optional int32 overall_conditions = 13; + */ + boolean hasOverallConditions(); + /** + * optional int32 overall_conditions = 13; + */ + int getOverallConditions(); + + /** + * optional int32 overall_covered_conditions = 14; + */ + boolean hasOverallCoveredConditions(); + /** + * optional int32 overall_covered_conditions = 14; + */ + int getOverallCoveredConditions(); + + /** + * optional string highlighting = 15; + */ + boolean hasHighlighting(); + /** + * optional string highlighting = 15; + */ + java.lang.String getHighlighting(); + /** + * optional string highlighting = 15; + */ + com.google.protobuf.ByteString + getHighlightingBytes(); + + /** + * optional string symbols = 16; + */ + boolean hasSymbols(); + /** + * optional string symbols = 16; + */ + java.lang.String getSymbols(); + /** + * optional string symbols = 16; + */ + com.google.protobuf.ByteString + getSymbolsBytes(); + + /** + * repeated int32 duplications = 17; + */ + java.util.List getDuplicationsList(); + /** + * repeated int32 duplications = 17; + */ + int getDuplicationsCount(); + /** + * repeated int32 duplications = 17; + */ + int getDuplications(int index); + } + /** + * Protobuf type {@code org.sonar.server.source.db.Line} + */ + public static final class Line extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:org.sonar.server.source.db.Line) + LineOrBuilder { + // Use Line.newBuilder() to construct. + private Line(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Line(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Line defaultInstance; + public static Line getDefaultInstance() { + return defaultInstance; + } + + public Line getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Line( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + line_ = input.readInt32(); + break; + } + case 18: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000002; + source_ = bs; + break; + } + case 26: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000004; + scmRevision_ = bs; + break; + } + case 34: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000008; + scmAuthor_ = bs; + break; + } + case 40: { + bitField0_ |= 0x00000010; + scmDate_ = input.readInt64(); + break; + } + case 48: { + bitField0_ |= 0x00000020; + utLineHits_ = input.readInt32(); + break; + } + case 56: { + bitField0_ |= 0x00000040; + utConditions_ = input.readInt32(); + break; + } + case 64: { + bitField0_ |= 0x00000080; + utCoveredConditions_ = input.readInt32(); + break; + } + case 72: { + bitField0_ |= 0x00000100; + itLineHits_ = input.readInt32(); + break; + } + case 80: { + bitField0_ |= 0x00000200; + itConditions_ = input.readInt32(); + break; + } + case 88: { + bitField0_ |= 0x00000400; + itCoveredConditions_ = input.readInt32(); + break; + } + case 96: { + bitField0_ |= 0x00000800; + overallLineHits_ = input.readInt32(); + break; + } + case 104: { + bitField0_ |= 0x00001000; + overallConditions_ = input.readInt32(); + break; + } + case 112: { + bitField0_ |= 0x00002000; + overallCoveredConditions_ = input.readInt32(); + break; + } + case 122: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00004000; + highlighting_ = bs; + break; + } + case 130: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00008000; + symbols_ = bs; + break; + } + case 136: { + if (!((mutable_bitField0_ & 0x00010000) == 0x00010000)) { + duplications_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00010000; + } + duplications_.add(input.readInt32()); + break; + } + case 138: { + int length = input.readRawVarint32(); + int limit = input.pushLimit(length); + if (!((mutable_bitField0_ & 0x00010000) == 0x00010000) && input.getBytesUntilLimit() > 0) { + duplications_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00010000; + } + while (input.getBytesUntilLimit() > 0) { + duplications_.add(input.readInt32()); + } + input.popLimit(limit); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00010000) == 0x00010000)) { + duplications_ = java.util.Collections.unmodifiableList(duplications_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.sonar.server.source.db.FileSourceDb.internal_static_org_sonar_server_source_db_Line_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.sonar.server.source.db.FileSourceDb.internal_static_org_sonar_server_source_db_Line_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.sonar.server.source.db.FileSourceDb.Line.class, org.sonar.server.source.db.FileSourceDb.Line.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Line parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Line(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + public static final int LINE_FIELD_NUMBER = 1; + private int line_; + /** + * optional int32 line = 1; + */ + public boolean hasLine() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional int32 line = 1; + */ + public int getLine() { + return line_; + } + + public static final int SOURCE_FIELD_NUMBER = 2; + private java.lang.Object source_; + /** + * optional string source = 2; + */ + public boolean hasSource() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string source = 2; + */ + public java.lang.String getSource() { + java.lang.Object ref = source_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + source_ = s; + } + return s; + } + } + /** + * optional string source = 2; + */ + public com.google.protobuf.ByteString + getSourceBytes() { + java.lang.Object ref = source_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + source_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SCM_REVISION_FIELD_NUMBER = 3; + private java.lang.Object scmRevision_; + /** + * optional string scm_revision = 3; + * + *
+     * SCM
+     * 
+ */ + public boolean hasScmRevision() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string scm_revision = 3; + * + *
+     * SCM
+     * 
+ */ + public java.lang.String getScmRevision() { + java.lang.Object ref = scmRevision_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + scmRevision_ = s; + } + return s; + } + } + /** + * optional string scm_revision = 3; + * + *
+     * SCM
+     * 
+ */ + public com.google.protobuf.ByteString + getScmRevisionBytes() { + java.lang.Object ref = scmRevision_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + scmRevision_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SCM_AUTHOR_FIELD_NUMBER = 4; + private java.lang.Object scmAuthor_; + /** + * optional string scm_author = 4; + */ + public boolean hasScmAuthor() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string scm_author = 4; + */ + public java.lang.String getScmAuthor() { + java.lang.Object ref = scmAuthor_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + scmAuthor_ = s; + } + return s; + } + } + /** + * optional string scm_author = 4; + */ + public com.google.protobuf.ByteString + getScmAuthorBytes() { + java.lang.Object ref = scmAuthor_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + scmAuthor_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SCM_DATE_FIELD_NUMBER = 5; + private long scmDate_; + /** + * optional int64 scm_date = 5; + */ + public boolean hasScmDate() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional int64 scm_date = 5; + */ + public long getScmDate() { + return scmDate_; + } + + public static final int UT_LINE_HITS_FIELD_NUMBER = 6; + private int utLineHits_; + /** + * optional int32 ut_line_hits = 6; + * + *
+     * unit testing
+     * 
+ */ + public boolean hasUtLineHits() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional int32 ut_line_hits = 6; + * + *
+     * unit testing
+     * 
+ */ + public int getUtLineHits() { + return utLineHits_; + } + + public static final int UT_CONDITIONS_FIELD_NUMBER = 7; + private int utConditions_; + /** + * optional int32 ut_conditions = 7; + */ + public boolean hasUtConditions() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional int32 ut_conditions = 7; + */ + public int getUtConditions() { + return utConditions_; + } + + public static final int UT_COVERED_CONDITIONS_FIELD_NUMBER = 8; + private int utCoveredConditions_; + /** + * optional int32 ut_covered_conditions = 8; + */ + public boolean hasUtCoveredConditions() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional int32 ut_covered_conditions = 8; + */ + public int getUtCoveredConditions() { + return utCoveredConditions_; + } + + public static final int IT_LINE_HITS_FIELD_NUMBER = 9; + private int itLineHits_; + /** + * optional int32 it_line_hits = 9; + * + *
+     * integration testing
+     * 
+ */ + public boolean hasItLineHits() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional int32 it_line_hits = 9; + * + *
+     * integration testing
+     * 
+ */ + public int getItLineHits() { + return itLineHits_; + } + + public static final int IT_CONDITIONS_FIELD_NUMBER = 10; + private int itConditions_; + /** + * optional int32 it_conditions = 10; + */ + public boolean hasItConditions() { + return ((bitField0_ & 0x00000200) == 0x00000200); + } + /** + * optional int32 it_conditions = 10; + */ + public int getItConditions() { + return itConditions_; + } + + public static final int IT_COVERED_CONDITIONS_FIELD_NUMBER = 11; + private int itCoveredConditions_; + /** + * optional int32 it_covered_conditions = 11; + */ + public boolean hasItCoveredConditions() { + return ((bitField0_ & 0x00000400) == 0x00000400); + } + /** + * optional int32 it_covered_conditions = 11; + */ + public int getItCoveredConditions() { + return itCoveredConditions_; + } + + public static final int OVERALL_LINE_HITS_FIELD_NUMBER = 12; + private int overallLineHits_; + /** + * optional int32 overall_line_hits = 12; + * + *
+     * overall testing
+     * 
+ */ + public boolean hasOverallLineHits() { + return ((bitField0_ & 0x00000800) == 0x00000800); + } + /** + * optional int32 overall_line_hits = 12; + * + *
+     * overall testing
+     * 
+ */ + public int getOverallLineHits() { + return overallLineHits_; + } + + public static final int OVERALL_CONDITIONS_FIELD_NUMBER = 13; + private int overallConditions_; + /** + * optional int32 overall_conditions = 13; + */ + public boolean hasOverallConditions() { + return ((bitField0_ & 0x00001000) == 0x00001000); + } + /** + * optional int32 overall_conditions = 13; + */ + public int getOverallConditions() { + return overallConditions_; + } + + public static final int OVERALL_COVERED_CONDITIONS_FIELD_NUMBER = 14; + private int overallCoveredConditions_; + /** + * optional int32 overall_covered_conditions = 14; + */ + public boolean hasOverallCoveredConditions() { + return ((bitField0_ & 0x00002000) == 0x00002000); + } + /** + * optional int32 overall_covered_conditions = 14; + */ + public int getOverallCoveredConditions() { + return overallCoveredConditions_; + } + + public static final int HIGHLIGHTING_FIELD_NUMBER = 15; + private java.lang.Object highlighting_; + /** + * optional string highlighting = 15; + */ + public boolean hasHighlighting() { + return ((bitField0_ & 0x00004000) == 0x00004000); + } + /** + * optional string highlighting = 15; + */ + public java.lang.String getHighlighting() { + java.lang.Object ref = highlighting_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + highlighting_ = s; + } + return s; + } + } + /** + * optional string highlighting = 15; + */ + public com.google.protobuf.ByteString + getHighlightingBytes() { + java.lang.Object ref = highlighting_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + highlighting_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int SYMBOLS_FIELD_NUMBER = 16; + private java.lang.Object symbols_; + /** + * optional string symbols = 16; + */ + public boolean hasSymbols() { + return ((bitField0_ & 0x00008000) == 0x00008000); + } + /** + * optional string symbols = 16; + */ + public java.lang.String getSymbols() { + java.lang.Object ref = symbols_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + symbols_ = s; + } + return s; + } + } + /** + * optional string symbols = 16; + */ + public com.google.protobuf.ByteString + getSymbolsBytes() { + java.lang.Object ref = symbols_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + symbols_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int DUPLICATIONS_FIELD_NUMBER = 17; + private java.util.List duplications_; + /** + * repeated int32 duplications = 17; + */ + public java.util.List + getDuplicationsList() { + return duplications_; + } + /** + * repeated int32 duplications = 17; + */ + public int getDuplicationsCount() { + return duplications_.size(); + } + /** + * repeated int32 duplications = 17; + */ + public int getDuplications(int index) { + return duplications_.get(index); + } + + private void initFields() { + line_ = 0; + source_ = ""; + scmRevision_ = ""; + scmAuthor_ = ""; + scmDate_ = 0L; + utLineHits_ = 0; + utConditions_ = 0; + utCoveredConditions_ = 0; + itLineHits_ = 0; + itConditions_ = 0; + itCoveredConditions_ = 0; + overallLineHits_ = 0; + overallConditions_ = 0; + overallCoveredConditions_ = 0; + highlighting_ = ""; + symbols_ = ""; + duplications_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeInt32(1, line_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, getSourceBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, getScmRevisionBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, getScmAuthorBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeInt64(5, scmDate_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeInt32(6, utLineHits_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + output.writeInt32(7, utConditions_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + output.writeInt32(8, utCoveredConditions_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + output.writeInt32(9, itLineHits_); + } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + output.writeInt32(10, itConditions_); + } + if (((bitField0_ & 0x00000400) == 0x00000400)) { + output.writeInt32(11, itCoveredConditions_); + } + if (((bitField0_ & 0x00000800) == 0x00000800)) { + output.writeInt32(12, overallLineHits_); + } + if (((bitField0_ & 0x00001000) == 0x00001000)) { + output.writeInt32(13, overallConditions_); + } + if (((bitField0_ & 0x00002000) == 0x00002000)) { + output.writeInt32(14, overallCoveredConditions_); + } + if (((bitField0_ & 0x00004000) == 0x00004000)) { + output.writeBytes(15, getHighlightingBytes()); + } + if (((bitField0_ & 0x00008000) == 0x00008000)) { + output.writeBytes(16, getSymbolsBytes()); + } + for (int i = 0; i < duplications_.size(); i++) { + output.writeInt32(17, duplications_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(1, line_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, getSourceBytes()); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, getScmRevisionBytes()); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, getScmAuthorBytes()); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeInt64Size(5, scmDate_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(6, utLineHits_); + } + if (((bitField0_ & 0x00000040) == 0x00000040)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(7, utConditions_); + } + if (((bitField0_ & 0x00000080) == 0x00000080)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(8, utCoveredConditions_); + } + if (((bitField0_ & 0x00000100) == 0x00000100)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(9, itLineHits_); + } + if (((bitField0_ & 0x00000200) == 0x00000200)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(10, itConditions_); + } + if (((bitField0_ & 0x00000400) == 0x00000400)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(11, itCoveredConditions_); + } + if (((bitField0_ & 0x00000800) == 0x00000800)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(12, overallLineHits_); + } + if (((bitField0_ & 0x00001000) == 0x00001000)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(13, overallConditions_); + } + if (((bitField0_ & 0x00002000) == 0x00002000)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(14, overallCoveredConditions_); + } + if (((bitField0_ & 0x00004000) == 0x00004000)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(15, getHighlightingBytes()); + } + if (((bitField0_ & 0x00008000) == 0x00008000)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(16, getSymbolsBytes()); + } + { + int dataSize = 0; + for (int i = 0; i < duplications_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeInt32SizeNoTag(duplications_.get(i)); + } + size += dataSize; + size += 2 * getDuplicationsList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.sonar.server.source.db.FileSourceDb.Line parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.sonar.server.source.db.FileSourceDb.Line parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.sonar.server.source.db.FileSourceDb.Line parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.sonar.server.source.db.FileSourceDb.Line parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.sonar.server.source.db.FileSourceDb.Line parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.sonar.server.source.db.FileSourceDb.Line parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.sonar.server.source.db.FileSourceDb.Line parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.sonar.server.source.db.FileSourceDb.Line parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.sonar.server.source.db.FileSourceDb.Line parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.sonar.server.source.db.FileSourceDb.Line parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.sonar.server.source.db.FileSourceDb.Line prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code org.sonar.server.source.db.Line} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:org.sonar.server.source.db.Line) + org.sonar.server.source.db.FileSourceDb.LineOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.sonar.server.source.db.FileSourceDb.internal_static_org_sonar_server_source_db_Line_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.sonar.server.source.db.FileSourceDb.internal_static_org_sonar_server_source_db_Line_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.sonar.server.source.db.FileSourceDb.Line.class, org.sonar.server.source.db.FileSourceDb.Line.Builder.class); + } + + // Construct using org.sonar.server.source.db.FileSourceDb.Line.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + line_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + source_ = ""; + bitField0_ = (bitField0_ & ~0x00000002); + scmRevision_ = ""; + bitField0_ = (bitField0_ & ~0x00000004); + scmAuthor_ = ""; + bitField0_ = (bitField0_ & ~0x00000008); + scmDate_ = 0L; + bitField0_ = (bitField0_ & ~0x00000010); + utLineHits_ = 0; + bitField0_ = (bitField0_ & ~0x00000020); + utConditions_ = 0; + bitField0_ = (bitField0_ & ~0x00000040); + utCoveredConditions_ = 0; + bitField0_ = (bitField0_ & ~0x00000080); + itLineHits_ = 0; + bitField0_ = (bitField0_ & ~0x00000100); + itConditions_ = 0; + bitField0_ = (bitField0_ & ~0x00000200); + itCoveredConditions_ = 0; + bitField0_ = (bitField0_ & ~0x00000400); + overallLineHits_ = 0; + bitField0_ = (bitField0_ & ~0x00000800); + overallConditions_ = 0; + bitField0_ = (bitField0_ & ~0x00001000); + overallCoveredConditions_ = 0; + bitField0_ = (bitField0_ & ~0x00002000); + highlighting_ = ""; + bitField0_ = (bitField0_ & ~0x00004000); + symbols_ = ""; + bitField0_ = (bitField0_ & ~0x00008000); + duplications_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00010000); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.sonar.server.source.db.FileSourceDb.internal_static_org_sonar_server_source_db_Line_descriptor; + } + + public org.sonar.server.source.db.FileSourceDb.Line getDefaultInstanceForType() { + return org.sonar.server.source.db.FileSourceDb.Line.getDefaultInstance(); + } + + public org.sonar.server.source.db.FileSourceDb.Line build() { + org.sonar.server.source.db.FileSourceDb.Line result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.sonar.server.source.db.FileSourceDb.Line buildPartial() { + org.sonar.server.source.db.FileSourceDb.Line result = new org.sonar.server.source.db.FileSourceDb.Line(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.line_ = line_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.source_ = source_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.scmRevision_ = scmRevision_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.scmAuthor_ = scmAuthor_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.scmDate_ = scmDate_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.utLineHits_ = utLineHits_; + if (((from_bitField0_ & 0x00000040) == 0x00000040)) { + to_bitField0_ |= 0x00000040; + } + result.utConditions_ = utConditions_; + if (((from_bitField0_ & 0x00000080) == 0x00000080)) { + to_bitField0_ |= 0x00000080; + } + result.utCoveredConditions_ = utCoveredConditions_; + if (((from_bitField0_ & 0x00000100) == 0x00000100)) { + to_bitField0_ |= 0x00000100; + } + result.itLineHits_ = itLineHits_; + if (((from_bitField0_ & 0x00000200) == 0x00000200)) { + to_bitField0_ |= 0x00000200; + } + result.itConditions_ = itConditions_; + if (((from_bitField0_ & 0x00000400) == 0x00000400)) { + to_bitField0_ |= 0x00000400; + } + result.itCoveredConditions_ = itCoveredConditions_; + if (((from_bitField0_ & 0x00000800) == 0x00000800)) { + to_bitField0_ |= 0x00000800; + } + result.overallLineHits_ = overallLineHits_; + if (((from_bitField0_ & 0x00001000) == 0x00001000)) { + to_bitField0_ |= 0x00001000; + } + result.overallConditions_ = overallConditions_; + if (((from_bitField0_ & 0x00002000) == 0x00002000)) { + to_bitField0_ |= 0x00002000; + } + result.overallCoveredConditions_ = overallCoveredConditions_; + if (((from_bitField0_ & 0x00004000) == 0x00004000)) { + to_bitField0_ |= 0x00004000; + } + result.highlighting_ = highlighting_; + if (((from_bitField0_ & 0x00008000) == 0x00008000)) { + to_bitField0_ |= 0x00008000; + } + result.symbols_ = symbols_; + if (((bitField0_ & 0x00010000) == 0x00010000)) { + duplications_ = java.util.Collections.unmodifiableList(duplications_); + bitField0_ = (bitField0_ & ~0x00010000); + } + result.duplications_ = duplications_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.sonar.server.source.db.FileSourceDb.Line) { + return mergeFrom((org.sonar.server.source.db.FileSourceDb.Line)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.sonar.server.source.db.FileSourceDb.Line other) { + if (other == org.sonar.server.source.db.FileSourceDb.Line.getDefaultInstance()) return this; + if (other.hasLine()) { + setLine(other.getLine()); + } + if (other.hasSource()) { + bitField0_ |= 0x00000002; + source_ = other.source_; + onChanged(); + } + if (other.hasScmRevision()) { + bitField0_ |= 0x00000004; + scmRevision_ = other.scmRevision_; + onChanged(); + } + if (other.hasScmAuthor()) { + bitField0_ |= 0x00000008; + scmAuthor_ = other.scmAuthor_; + onChanged(); + } + if (other.hasScmDate()) { + setScmDate(other.getScmDate()); + } + if (other.hasUtLineHits()) { + setUtLineHits(other.getUtLineHits()); + } + if (other.hasUtConditions()) { + setUtConditions(other.getUtConditions()); + } + if (other.hasUtCoveredConditions()) { + setUtCoveredConditions(other.getUtCoveredConditions()); + } + if (other.hasItLineHits()) { + setItLineHits(other.getItLineHits()); + } + if (other.hasItConditions()) { + setItConditions(other.getItConditions()); + } + if (other.hasItCoveredConditions()) { + setItCoveredConditions(other.getItCoveredConditions()); + } + if (other.hasOverallLineHits()) { + setOverallLineHits(other.getOverallLineHits()); + } + if (other.hasOverallConditions()) { + setOverallConditions(other.getOverallConditions()); + } + if (other.hasOverallCoveredConditions()) { + setOverallCoveredConditions(other.getOverallCoveredConditions()); + } + if (other.hasHighlighting()) { + bitField0_ |= 0x00004000; + highlighting_ = other.highlighting_; + onChanged(); + } + if (other.hasSymbols()) { + bitField0_ |= 0x00008000; + symbols_ = other.symbols_; + onChanged(); + } + if (!other.duplications_.isEmpty()) { + if (duplications_.isEmpty()) { + duplications_ = other.duplications_; + bitField0_ = (bitField0_ & ~0x00010000); + } else { + ensureDuplicationsIsMutable(); + duplications_.addAll(other.duplications_); + } + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.sonar.server.source.db.FileSourceDb.Line parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.sonar.server.source.db.FileSourceDb.Line) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private int line_ ; + /** + * optional int32 line = 1; + */ + public boolean hasLine() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional int32 line = 1; + */ + public int getLine() { + return line_; + } + /** + * optional int32 line = 1; + */ + public Builder setLine(int value) { + bitField0_ |= 0x00000001; + line_ = value; + onChanged(); + return this; + } + /** + * optional int32 line = 1; + */ + public Builder clearLine() { + bitField0_ = (bitField0_ & ~0x00000001); + line_ = 0; + onChanged(); + return this; + } + + private java.lang.Object source_ = ""; + /** + * optional string source = 2; + */ + public boolean hasSource() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional string source = 2; + */ + public java.lang.String getSource() { + java.lang.Object ref = source_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + source_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string source = 2; + */ + public com.google.protobuf.ByteString + getSourceBytes() { + java.lang.Object ref = source_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + source_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string source = 2; + */ + public Builder setSource( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + source_ = value; + onChanged(); + return this; + } + /** + * optional string source = 2; + */ + public Builder clearSource() { + bitField0_ = (bitField0_ & ~0x00000002); + source_ = getDefaultInstance().getSource(); + onChanged(); + return this; + } + /** + * optional string source = 2; + */ + public Builder setSourceBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + source_ = value; + onChanged(); + return this; + } + + private java.lang.Object scmRevision_ = ""; + /** + * optional string scm_revision = 3; + * + *
+       * SCM
+       * 
+ */ + public boolean hasScmRevision() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional string scm_revision = 3; + * + *
+       * SCM
+       * 
+ */ + public java.lang.String getScmRevision() { + java.lang.Object ref = scmRevision_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + scmRevision_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string scm_revision = 3; + * + *
+       * SCM
+       * 
+ */ + public com.google.protobuf.ByteString + getScmRevisionBytes() { + java.lang.Object ref = scmRevision_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + scmRevision_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string scm_revision = 3; + * + *
+       * SCM
+       * 
+ */ + public Builder setScmRevision( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + scmRevision_ = value; + onChanged(); + return this; + } + /** + * optional string scm_revision = 3; + * + *
+       * SCM
+       * 
+ */ + public Builder clearScmRevision() { + bitField0_ = (bitField0_ & ~0x00000004); + scmRevision_ = getDefaultInstance().getScmRevision(); + onChanged(); + return this; + } + /** + * optional string scm_revision = 3; + * + *
+       * SCM
+       * 
+ */ + public Builder setScmRevisionBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + scmRevision_ = value; + onChanged(); + return this; + } + + private java.lang.Object scmAuthor_ = ""; + /** + * optional string scm_author = 4; + */ + public boolean hasScmAuthor() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional string scm_author = 4; + */ + public java.lang.String getScmAuthor() { + java.lang.Object ref = scmAuthor_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + scmAuthor_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string scm_author = 4; + */ + public com.google.protobuf.ByteString + getScmAuthorBytes() { + java.lang.Object ref = scmAuthor_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + scmAuthor_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string scm_author = 4; + */ + public Builder setScmAuthor( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + scmAuthor_ = value; + onChanged(); + return this; + } + /** + * optional string scm_author = 4; + */ + public Builder clearScmAuthor() { + bitField0_ = (bitField0_ & ~0x00000008); + scmAuthor_ = getDefaultInstance().getScmAuthor(); + onChanged(); + return this; + } + /** + * optional string scm_author = 4; + */ + public Builder setScmAuthorBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + scmAuthor_ = value; + onChanged(); + return this; + } + + private long scmDate_ ; + /** + * optional int64 scm_date = 5; + */ + public boolean hasScmDate() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional int64 scm_date = 5; + */ + public long getScmDate() { + return scmDate_; + } + /** + * optional int64 scm_date = 5; + */ + public Builder setScmDate(long value) { + bitField0_ |= 0x00000010; + scmDate_ = value; + onChanged(); + return this; + } + /** + * optional int64 scm_date = 5; + */ + public Builder clearScmDate() { + bitField0_ = (bitField0_ & ~0x00000010); + scmDate_ = 0L; + onChanged(); + return this; + } + + private int utLineHits_ ; + /** + * optional int32 ut_line_hits = 6; + * + *
+       * unit testing
+       * 
+ */ + public boolean hasUtLineHits() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional int32 ut_line_hits = 6; + * + *
+       * unit testing
+       * 
+ */ + public int getUtLineHits() { + return utLineHits_; + } + /** + * optional int32 ut_line_hits = 6; + * + *
+       * unit testing
+       * 
+ */ + public Builder setUtLineHits(int value) { + bitField0_ |= 0x00000020; + utLineHits_ = value; + onChanged(); + return this; + } + /** + * optional int32 ut_line_hits = 6; + * + *
+       * unit testing
+       * 
+ */ + public Builder clearUtLineHits() { + bitField0_ = (bitField0_ & ~0x00000020); + utLineHits_ = 0; + onChanged(); + return this; + } + + private int utConditions_ ; + /** + * optional int32 ut_conditions = 7; + */ + public boolean hasUtConditions() { + return ((bitField0_ & 0x00000040) == 0x00000040); + } + /** + * optional int32 ut_conditions = 7; + */ + public int getUtConditions() { + return utConditions_; + } + /** + * optional int32 ut_conditions = 7; + */ + public Builder setUtConditions(int value) { + bitField0_ |= 0x00000040; + utConditions_ = value; + onChanged(); + return this; + } + /** + * optional int32 ut_conditions = 7; + */ + public Builder clearUtConditions() { + bitField0_ = (bitField0_ & ~0x00000040); + utConditions_ = 0; + onChanged(); + return this; + } + + private int utCoveredConditions_ ; + /** + * optional int32 ut_covered_conditions = 8; + */ + public boolean hasUtCoveredConditions() { + return ((bitField0_ & 0x00000080) == 0x00000080); + } + /** + * optional int32 ut_covered_conditions = 8; + */ + public int getUtCoveredConditions() { + return utCoveredConditions_; + } + /** + * optional int32 ut_covered_conditions = 8; + */ + public Builder setUtCoveredConditions(int value) { + bitField0_ |= 0x00000080; + utCoveredConditions_ = value; + onChanged(); + return this; + } + /** + * optional int32 ut_covered_conditions = 8; + */ + public Builder clearUtCoveredConditions() { + bitField0_ = (bitField0_ & ~0x00000080); + utCoveredConditions_ = 0; + onChanged(); + return this; + } + + private int itLineHits_ ; + /** + * optional int32 it_line_hits = 9; + * + *
+       * integration testing
+       * 
+ */ + public boolean hasItLineHits() { + return ((bitField0_ & 0x00000100) == 0x00000100); + } + /** + * optional int32 it_line_hits = 9; + * + *
+       * integration testing
+       * 
+ */ + public int getItLineHits() { + return itLineHits_; + } + /** + * optional int32 it_line_hits = 9; + * + *
+       * integration testing
+       * 
+ */ + public Builder setItLineHits(int value) { + bitField0_ |= 0x00000100; + itLineHits_ = value; + onChanged(); + return this; + } + /** + * optional int32 it_line_hits = 9; + * + *
+       * integration testing
+       * 
+ */ + public Builder clearItLineHits() { + bitField0_ = (bitField0_ & ~0x00000100); + itLineHits_ = 0; + onChanged(); + return this; + } + + private int itConditions_ ; + /** + * optional int32 it_conditions = 10; + */ + public boolean hasItConditions() { + return ((bitField0_ & 0x00000200) == 0x00000200); + } + /** + * optional int32 it_conditions = 10; + */ + public int getItConditions() { + return itConditions_; + } + /** + * optional int32 it_conditions = 10; + */ + public Builder setItConditions(int value) { + bitField0_ |= 0x00000200; + itConditions_ = value; + onChanged(); + return this; + } + /** + * optional int32 it_conditions = 10; + */ + public Builder clearItConditions() { + bitField0_ = (bitField0_ & ~0x00000200); + itConditions_ = 0; + onChanged(); + return this; + } + + private int itCoveredConditions_ ; + /** + * optional int32 it_covered_conditions = 11; + */ + public boolean hasItCoveredConditions() { + return ((bitField0_ & 0x00000400) == 0x00000400); + } + /** + * optional int32 it_covered_conditions = 11; + */ + public int getItCoveredConditions() { + return itCoveredConditions_; + } + /** + * optional int32 it_covered_conditions = 11; + */ + public Builder setItCoveredConditions(int value) { + bitField0_ |= 0x00000400; + itCoveredConditions_ = value; + onChanged(); + return this; + } + /** + * optional int32 it_covered_conditions = 11; + */ + public Builder clearItCoveredConditions() { + bitField0_ = (bitField0_ & ~0x00000400); + itCoveredConditions_ = 0; + onChanged(); + return this; + } + + private int overallLineHits_ ; + /** + * optional int32 overall_line_hits = 12; + * + *
+       * overall testing
+       * 
+ */ + public boolean hasOverallLineHits() { + return ((bitField0_ & 0x00000800) == 0x00000800); + } + /** + * optional int32 overall_line_hits = 12; + * + *
+       * overall testing
+       * 
+ */ + public int getOverallLineHits() { + return overallLineHits_; + } + /** + * optional int32 overall_line_hits = 12; + * + *
+       * overall testing
+       * 
+ */ + public Builder setOverallLineHits(int value) { + bitField0_ |= 0x00000800; + overallLineHits_ = value; + onChanged(); + return this; + } + /** + * optional int32 overall_line_hits = 12; + * + *
+       * overall testing
+       * 
+ */ + public Builder clearOverallLineHits() { + bitField0_ = (bitField0_ & ~0x00000800); + overallLineHits_ = 0; + onChanged(); + return this; + } + + private int overallConditions_ ; + /** + * optional int32 overall_conditions = 13; + */ + public boolean hasOverallConditions() { + return ((bitField0_ & 0x00001000) == 0x00001000); + } + /** + * optional int32 overall_conditions = 13; + */ + public int getOverallConditions() { + return overallConditions_; + } + /** + * optional int32 overall_conditions = 13; + */ + public Builder setOverallConditions(int value) { + bitField0_ |= 0x00001000; + overallConditions_ = value; + onChanged(); + return this; + } + /** + * optional int32 overall_conditions = 13; + */ + public Builder clearOverallConditions() { + bitField0_ = (bitField0_ & ~0x00001000); + overallConditions_ = 0; + onChanged(); + return this; + } + + private int overallCoveredConditions_ ; + /** + * optional int32 overall_covered_conditions = 14; + */ + public boolean hasOverallCoveredConditions() { + return ((bitField0_ & 0x00002000) == 0x00002000); + } + /** + * optional int32 overall_covered_conditions = 14; + */ + public int getOverallCoveredConditions() { + return overallCoveredConditions_; + } + /** + * optional int32 overall_covered_conditions = 14; + */ + public Builder setOverallCoveredConditions(int value) { + bitField0_ |= 0x00002000; + overallCoveredConditions_ = value; + onChanged(); + return this; + } + /** + * optional int32 overall_covered_conditions = 14; + */ + public Builder clearOverallCoveredConditions() { + bitField0_ = (bitField0_ & ~0x00002000); + overallCoveredConditions_ = 0; + onChanged(); + return this; + } + + private java.lang.Object highlighting_ = ""; + /** + * optional string highlighting = 15; + */ + public boolean hasHighlighting() { + return ((bitField0_ & 0x00004000) == 0x00004000); + } + /** + * optional string highlighting = 15; + */ + public java.lang.String getHighlighting() { + java.lang.Object ref = highlighting_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + highlighting_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string highlighting = 15; + */ + public com.google.protobuf.ByteString + getHighlightingBytes() { + java.lang.Object ref = highlighting_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + highlighting_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string highlighting = 15; + */ + public Builder setHighlighting( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00004000; + highlighting_ = value; + onChanged(); + return this; + } + /** + * optional string highlighting = 15; + */ + public Builder clearHighlighting() { + bitField0_ = (bitField0_ & ~0x00004000); + highlighting_ = getDefaultInstance().getHighlighting(); + onChanged(); + return this; + } + /** + * optional string highlighting = 15; + */ + public Builder setHighlightingBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00004000; + highlighting_ = value; + onChanged(); + return this; + } + + private java.lang.Object symbols_ = ""; + /** + * optional string symbols = 16; + */ + public boolean hasSymbols() { + return ((bitField0_ & 0x00008000) == 0x00008000); + } + /** + * optional string symbols = 16; + */ + public java.lang.String getSymbols() { + java.lang.Object ref = symbols_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + symbols_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string symbols = 16; + */ + public com.google.protobuf.ByteString + getSymbolsBytes() { + java.lang.Object ref = symbols_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + symbols_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string symbols = 16; + */ + public Builder setSymbols( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00008000; + symbols_ = value; + onChanged(); + return this; + } + /** + * optional string symbols = 16; + */ + public Builder clearSymbols() { + bitField0_ = (bitField0_ & ~0x00008000); + symbols_ = getDefaultInstance().getSymbols(); + onChanged(); + return this; + } + /** + * optional string symbols = 16; + */ + public Builder setSymbolsBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00008000; + symbols_ = value; + onChanged(); + return this; + } + + private java.util.List duplications_ = java.util.Collections.emptyList(); + private void ensureDuplicationsIsMutable() { + if (!((bitField0_ & 0x00010000) == 0x00010000)) { + duplications_ = new java.util.ArrayList(duplications_); + bitField0_ |= 0x00010000; + } + } + /** + * repeated int32 duplications = 17; + */ + public java.util.List + getDuplicationsList() { + return java.util.Collections.unmodifiableList(duplications_); + } + /** + * repeated int32 duplications = 17; + */ + public int getDuplicationsCount() { + return duplications_.size(); + } + /** + * repeated int32 duplications = 17; + */ + public int getDuplications(int index) { + return duplications_.get(index); + } + /** + * repeated int32 duplications = 17; + */ + public Builder setDuplications( + int index, int value) { + ensureDuplicationsIsMutable(); + duplications_.set(index, value); + onChanged(); + return this; + } + /** + * repeated int32 duplications = 17; + */ + public Builder addDuplications(int value) { + ensureDuplicationsIsMutable(); + duplications_.add(value); + onChanged(); + return this; + } + /** + * repeated int32 duplications = 17; + */ + public Builder addAllDuplications( + java.lang.Iterable values) { + ensureDuplicationsIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, duplications_); + onChanged(); + return this; + } + /** + * repeated int32 duplications = 17; + */ + public Builder clearDuplications() { + duplications_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00010000); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:org.sonar.server.source.db.Line) + } + + static { + defaultInstance = new Line(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:org.sonar.server.source.db.Line) + } + + public interface DataOrBuilder extends + // @@protoc_insertion_point(interface_extends:org.sonar.server.source.db.Data) + com.google.protobuf.MessageOrBuilder { + + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + java.util.List + getLinesList(); + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + org.sonar.server.source.db.FileSourceDb.Line getLines(int index); + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + int getLinesCount(); + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + java.util.List + getLinesOrBuilderList(); + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + org.sonar.server.source.db.FileSourceDb.LineOrBuilder getLinesOrBuilder( + int index); + } + /** + * Protobuf type {@code org.sonar.server.source.db.Data} + */ + public static final class Data extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:org.sonar.server.source.db.Data) + DataOrBuilder { + // Use Data.newBuilder() to construct. + private Data(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Data(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Data defaultInstance; + public static Data getDefaultInstance() { + return defaultInstance; + } + + public Data getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Data( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + lines_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000001; + } + lines_.add(input.readMessage(org.sonar.server.source.db.FileSourceDb.Line.PARSER, extensionRegistry)); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + lines_ = java.util.Collections.unmodifiableList(lines_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.sonar.server.source.db.FileSourceDb.internal_static_org_sonar_server_source_db_Data_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.sonar.server.source.db.FileSourceDb.internal_static_org_sonar_server_source_db_Data_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.sonar.server.source.db.FileSourceDb.Data.class, org.sonar.server.source.db.FileSourceDb.Data.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Data parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Data(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public static final int LINES_FIELD_NUMBER = 1; + private java.util.List lines_; + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public java.util.List getLinesList() { + return lines_; + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public java.util.List + getLinesOrBuilderList() { + return lines_; + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public int getLinesCount() { + return lines_.size(); + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public org.sonar.server.source.db.FileSourceDb.Line getLines(int index) { + return lines_.get(index); + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public org.sonar.server.source.db.FileSourceDb.LineOrBuilder getLinesOrBuilder( + int index) { + return lines_.get(index); + } + + private void initFields() { + lines_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + for (int i = 0; i < lines_.size(); i++) { + output.writeMessage(1, lines_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + for (int i = 0; i < lines_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(1, lines_.get(i)); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.sonar.server.source.db.FileSourceDb.Data parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.sonar.server.source.db.FileSourceDb.Data parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.sonar.server.source.db.FileSourceDb.Data parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.sonar.server.source.db.FileSourceDb.Data parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.sonar.server.source.db.FileSourceDb.Data parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.sonar.server.source.db.FileSourceDb.Data parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.sonar.server.source.db.FileSourceDb.Data parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.sonar.server.source.db.FileSourceDb.Data parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.sonar.server.source.db.FileSourceDb.Data parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.sonar.server.source.db.FileSourceDb.Data parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.sonar.server.source.db.FileSourceDb.Data prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code org.sonar.server.source.db.Data} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder implements + // @@protoc_insertion_point(builder_implements:org.sonar.server.source.db.Data) + org.sonar.server.source.db.FileSourceDb.DataOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.sonar.server.source.db.FileSourceDb.internal_static_org_sonar_server_source_db_Data_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.sonar.server.source.db.FileSourceDb.internal_static_org_sonar_server_source_db_Data_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.sonar.server.source.db.FileSourceDb.Data.class, org.sonar.server.source.db.FileSourceDb.Data.Builder.class); + } + + // Construct using org.sonar.server.source.db.FileSourceDb.Data.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getLinesFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + if (linesBuilder_ == null) { + lines_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + linesBuilder_.clear(); + } + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.sonar.server.source.db.FileSourceDb.internal_static_org_sonar_server_source_db_Data_descriptor; + } + + public org.sonar.server.source.db.FileSourceDb.Data getDefaultInstanceForType() { + return org.sonar.server.source.db.FileSourceDb.Data.getDefaultInstance(); + } + + public org.sonar.server.source.db.FileSourceDb.Data build() { + org.sonar.server.source.db.FileSourceDb.Data result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.sonar.server.source.db.FileSourceDb.Data buildPartial() { + org.sonar.server.source.db.FileSourceDb.Data result = new org.sonar.server.source.db.FileSourceDb.Data(this); + int from_bitField0_ = bitField0_; + if (linesBuilder_ == null) { + if (((bitField0_ & 0x00000001) == 0x00000001)) { + lines_ = java.util.Collections.unmodifiableList(lines_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.lines_ = lines_; + } else { + result.lines_ = linesBuilder_.build(); + } + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.sonar.server.source.db.FileSourceDb.Data) { + return mergeFrom((org.sonar.server.source.db.FileSourceDb.Data)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.sonar.server.source.db.FileSourceDb.Data other) { + if (other == org.sonar.server.source.db.FileSourceDb.Data.getDefaultInstance()) return this; + if (linesBuilder_ == null) { + if (!other.lines_.isEmpty()) { + if (lines_.isEmpty()) { + lines_ = other.lines_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureLinesIsMutable(); + lines_.addAll(other.lines_); + } + onChanged(); + } + } else { + if (!other.lines_.isEmpty()) { + if (linesBuilder_.isEmpty()) { + linesBuilder_.dispose(); + linesBuilder_ = null; + lines_ = other.lines_; + bitField0_ = (bitField0_ & ~0x00000001); + linesBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getLinesFieldBuilder() : null; + } else { + linesBuilder_.addAllMessages(other.lines_); + } + } + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.sonar.server.source.db.FileSourceDb.Data parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.sonar.server.source.db.FileSourceDb.Data) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.util.List lines_ = + java.util.Collections.emptyList(); + private void ensureLinesIsMutable() { + if (!((bitField0_ & 0x00000001) == 0x00000001)) { + lines_ = new java.util.ArrayList(lines_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.sonar.server.source.db.FileSourceDb.Line, org.sonar.server.source.db.FileSourceDb.Line.Builder, org.sonar.server.source.db.FileSourceDb.LineOrBuilder> linesBuilder_; + + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public java.util.List getLinesList() { + if (linesBuilder_ == null) { + return java.util.Collections.unmodifiableList(lines_); + } else { + return linesBuilder_.getMessageList(); + } + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public int getLinesCount() { + if (linesBuilder_ == null) { + return lines_.size(); + } else { + return linesBuilder_.getCount(); + } + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public org.sonar.server.source.db.FileSourceDb.Line getLines(int index) { + if (linesBuilder_ == null) { + return lines_.get(index); + } else { + return linesBuilder_.getMessage(index); + } + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public Builder setLines( + int index, org.sonar.server.source.db.FileSourceDb.Line value) { + if (linesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureLinesIsMutable(); + lines_.set(index, value); + onChanged(); + } else { + linesBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public Builder setLines( + int index, org.sonar.server.source.db.FileSourceDb.Line.Builder builderForValue) { + if (linesBuilder_ == null) { + ensureLinesIsMutable(); + lines_.set(index, builderForValue.build()); + onChanged(); + } else { + linesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public Builder addLines(org.sonar.server.source.db.FileSourceDb.Line value) { + if (linesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureLinesIsMutable(); + lines_.add(value); + onChanged(); + } else { + linesBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public Builder addLines( + int index, org.sonar.server.source.db.FileSourceDb.Line value) { + if (linesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureLinesIsMutable(); + lines_.add(index, value); + onChanged(); + } else { + linesBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public Builder addLines( + org.sonar.server.source.db.FileSourceDb.Line.Builder builderForValue) { + if (linesBuilder_ == null) { + ensureLinesIsMutable(); + lines_.add(builderForValue.build()); + onChanged(); + } else { + linesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public Builder addLines( + int index, org.sonar.server.source.db.FileSourceDb.Line.Builder builderForValue) { + if (linesBuilder_ == null) { + ensureLinesIsMutable(); + lines_.add(index, builderForValue.build()); + onChanged(); + } else { + linesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public Builder addAllLines( + java.lang.Iterable values) { + if (linesBuilder_ == null) { + ensureLinesIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, lines_); + onChanged(); + } else { + linesBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public Builder clearLines() { + if (linesBuilder_ == null) { + lines_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + linesBuilder_.clear(); + } + return this; + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public Builder removeLines(int index) { + if (linesBuilder_ == null) { + ensureLinesIsMutable(); + lines_.remove(index); + onChanged(); + } else { + linesBuilder_.remove(index); + } + return this; + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public org.sonar.server.source.db.FileSourceDb.Line.Builder getLinesBuilder( + int index) { + return getLinesFieldBuilder().getBuilder(index); + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public org.sonar.server.source.db.FileSourceDb.LineOrBuilder getLinesOrBuilder( + int index) { + if (linesBuilder_ == null) { + return lines_.get(index); } else { + return linesBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public java.util.List + getLinesOrBuilderList() { + if (linesBuilder_ != null) { + return linesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(lines_); + } + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public org.sonar.server.source.db.FileSourceDb.Line.Builder addLinesBuilder() { + return getLinesFieldBuilder().addBuilder( + org.sonar.server.source.db.FileSourceDb.Line.getDefaultInstance()); + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public org.sonar.server.source.db.FileSourceDb.Line.Builder addLinesBuilder( + int index) { + return getLinesFieldBuilder().addBuilder( + index, org.sonar.server.source.db.FileSourceDb.Line.getDefaultInstance()); + } + /** + * repeated .org.sonar.server.source.db.Line lines = 1; + */ + public java.util.List + getLinesBuilderList() { + return getLinesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.sonar.server.source.db.FileSourceDb.Line, org.sonar.server.source.db.FileSourceDb.Line.Builder, org.sonar.server.source.db.FileSourceDb.LineOrBuilder> + getLinesFieldBuilder() { + if (linesBuilder_ == null) { + linesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.sonar.server.source.db.FileSourceDb.Line, org.sonar.server.source.db.FileSourceDb.Line.Builder, org.sonar.server.source.db.FileSourceDb.LineOrBuilder>( + lines_, + ((bitField0_ & 0x00000001) == 0x00000001), + getParentForChildren(), + isClean()); + lines_ = null; + } + return linesBuilder_; + } + + // @@protoc_insertion_point(builder_scope:org.sonar.server.source.db.Data) + } + + static { + defaultInstance = new Data(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:org.sonar.server.source.db.Data) + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_org_sonar_server_source_db_Line_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_org_sonar_server_source_db_Line_fieldAccessorTable; + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_org_sonar_server_source_db_Data_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_org_sonar_server_source_db_Data_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\024file_source_db.proto\022\032org.sonar.server" + + ".source.db\"\220\003\n\004Line\022\014\n\004line\030\001 \001(\005\022\016\n\006sou" + + "rce\030\002 \001(\t\022\024\n\014scm_revision\030\003 \001(\t\022\022\n\nscm_a" + + "uthor\030\004 \001(\t\022\020\n\010scm_date\030\005 \001(\003\022\024\n\014ut_line" + + "_hits\030\006 \001(\005\022\025\n\rut_conditions\030\007 \001(\005\022\035\n\025ut" + + "_covered_conditions\030\010 \001(\005\022\024\n\014it_line_hit" + + "s\030\t \001(\005\022\025\n\rit_conditions\030\n \001(\005\022\035\n\025it_cov" + + "ered_conditions\030\013 \001(\005\022\031\n\021overall_line_hi" + + "ts\030\014 \001(\005\022\032\n\022overall_conditions\030\r \001(\005\022\"\n\032" + + "overall_covered_conditions\030\016 \001(\005\022\024\n\014high", + "lighting\030\017 \001(\t\022\017\n\007symbols\030\020 \001(\t\022\024\n\014dupli" + + "cations\030\021 \003(\005\"7\n\004Data\022/\n\005lines\030\001 \003(\0132 .o" + + "rg.sonar.server.source.db.LineB\002H\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) { + descriptor = root; + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + internal_static_org_sonar_server_source_db_Line_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_org_sonar_server_source_db_Line_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_org_sonar_server_source_db_Line_descriptor, + new java.lang.String[] { "Line", "Source", "ScmRevision", "ScmAuthor", "ScmDate", "UtLineHits", "UtConditions", "UtCoveredConditions", "ItLineHits", "ItConditions", "ItCoveredConditions", "OverallLineHits", "OverallConditions", "OverallCoveredConditions", "Highlighting", "Symbols", "Duplications", }); + internal_static_org_sonar_server_source_db_Data_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_org_sonar_server_source_db_Data_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_org_sonar_server_source_db_Data_descriptor, + new java.lang.String[] { "Lines", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/sonar-batch-protocol/src/main/protobuf/file_source_db.proto b/sonar-batch-protocol/src/main/protobuf/file_source_db.proto new file mode 100644 index 00000000000..bdb0b877949 --- /dev/null +++ b/sonar-batch-protocol/src/main/protobuf/file_source_db.proto @@ -0,0 +1,72 @@ +/* + SonarQube, open source software quality management tool. + Copyright (C) 2008-2015 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. +*/ + +/* +Notes + + - "required" fields are not used as recommended by Google to keep forward-compatibility: + https://developers.google.com/protocol-buffers/docs/proto#simple + + - the related Java files are not generated during build. Indeed the existing protoc maven + plugins require protobuf to be installed on boxes. That means that generated Java files + are updated and committed for each change (see src/main/gen-java). +*/ + +// structure of db column FILE_SOURCES.BINARY_DATA + +// Temporarily in sonar-batch-protocol as this db table +// is still fed on batch-side. However generated sources +// are already in correct package + +package org.sonar.server.source.db; +option optimize_for = SPEED; + +message Line { + optional int32 line = 1; + optional string source = 2; + + // SCM + optional string scm_revision = 3; + optional string scm_author = 4; + optional int64 scm_date = 5; + + // unit tests + optional int32 ut_line_hits = 6; + optional int32 ut_conditions = 7; + optional int32 ut_covered_conditions = 8; + + // integration tests + optional int32 it_line_hits = 9; + optional int32 it_conditions = 10; + optional int32 it_covered_conditions = 11; + + // overall tests + optional int32 overall_line_hits = 12; + optional int32 overall_conditions = 13; + optional int32 overall_covered_conditions = 14; + + optional string highlighting = 15; + optional string symbols = 16; + repeated int32 duplications = 17; +} + +message Data { + repeated Line lines = 1; +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/SourceDataFactory.java b/sonar-batch/src/main/java/org/sonar/batch/index/SourceDataFactory.java new file mode 100644 index 00000000000..e3e9f33bc79 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/index/SourceDataFactory.java @@ -0,0 +1,376 @@ +/* + * 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.batch.index; + +import com.google.common.base.CharMatcher; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.BatchComponent; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.duplication.DuplicationGroup; +import org.sonar.api.batch.sensor.symbol.Symbol; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.utils.DateUtils; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.batch.duplication.DuplicationCache; +import org.sonar.batch.highlighting.SyntaxHighlightingData; +import org.sonar.batch.highlighting.SyntaxHighlightingRule; +import org.sonar.batch.scan.measure.MeasureCache; +import org.sonar.batch.source.CodeColorizers; +import org.sonar.batch.symbol.SymbolData; +import org.sonar.core.source.SnapshotDataTypes; +import org.sonar.core.source.db.FileSourceDto; +import org.sonar.server.source.db.FileSourceDb; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Consolidate different caches for the export of report to server. + * @see org.sonar.server.source.db.FileSourceDb + */ +public class SourceDataFactory implements BatchComponent { + + private static final String BOM = "\uFEFF"; + + private final MeasureCache measureCache; + private final ComponentDataCache componentDataCache; + private final DuplicationCache duplicationCache; + private final CodeColorizers codeColorizers; + + public SourceDataFactory(MeasureCache measureCache, ComponentDataCache componentDataCache, + DuplicationCache duplicationCache, CodeColorizers codeColorizers) { + this.measureCache = measureCache; + this.componentDataCache = componentDataCache; + this.duplicationCache = duplicationCache; + this.codeColorizers = codeColorizers; + } + + public byte[] consolidateData(DefaultInputFile inputFile) throws IOException { + FileSourceDb.Data.Builder dataBuilder = createForSource(inputFile); + applyLineMeasures(inputFile, dataBuilder); + applyDuplications(inputFile.key(), dataBuilder); + applyHighlighting(inputFile, dataBuilder); + applySymbolReferences(inputFile, dataBuilder); + return FileSourceDto.encodeData(dataBuilder.build()); + } + + FileSourceDb.Data.Builder createForSource(DefaultInputFile inputFile) throws IOException { + FileSourceDb.Data.Builder result = FileSourceDb.Data.newBuilder(); + List lines = FileUtils.readLines(inputFile.file(), inputFile.encoding()); + // Missing empty last line + if (lines.size() == inputFile.lines() - 1) { + lines.add(""); + } + for (int lineIdx = 1; lineIdx <= lines.size(); lineIdx++) { + String s = CharMatcher.anyOf(BOM).removeFrom(lines.get(lineIdx - 1)); + FileSourceDb.Line.Builder linesBuilder = result.addLinesBuilder(); + linesBuilder.setLine(lineIdx).setSource(s); + } + return result; + } + + void applyLineMeasures(DefaultInputFile file, FileSourceDb.Data.Builder dataBuilder) { + applyLineMeasure(file.key(), CoreMetrics.SCM_AUTHORS_BY_LINE_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setScmAuthor(value); + } + }); + applyLineMeasure(file.key(), CoreMetrics.SCM_REVISIONS_BY_LINE_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setScmRevision(value); + } + }); + applyLineMeasure(file.key(), CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setScmDate(DateUtils.parseDateTimeQuietly(value).getTime()); + } + }); + applyLineMeasure(file.key(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setUtLineHits(Integer.parseInt(value)); + } + }); + applyLineMeasure(file.key(), CoreMetrics.CONDITIONS_BY_LINE_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setUtConditions(Integer.parseInt(value)); + } + }); + applyLineMeasure(file.key(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setUtCoveredConditions(Integer.parseInt(value)); + } + }); + applyLineMeasure(file.key(), CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setItLineHits(Integer.parseInt(value)); + } + }); + applyLineMeasure(file.key(), CoreMetrics.IT_CONDITIONS_BY_LINE_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setItConditions(Integer.parseInt(value)); + } + }); + applyLineMeasure(file.key(), CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setItCoveredConditions(Integer.parseInt(value)); + } + }); + applyLineMeasure(file.key(), CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setOverallLineHits(Integer.parseInt(value)); + } + }); + applyLineMeasure(file.key(), CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setOverallConditions(Integer.parseInt(value)); + } + }); + applyLineMeasure(file.key(), CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY, dataBuilder, new MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setOverallCoveredConditions(Integer.parseInt(value)); + } + }); + } + + void applyLineMeasure(String inputFileKey, String metricKey, FileSourceDb.Data.Builder to, MeasureOperation op) { + Iterable measures = measureCache.byMetric(inputFileKey, metricKey); + if (measures != null) { + for (Measure measure : measures) { + Map lineMeasures = KeyValueFormat.parseIntString((String) measure.value()); + for (Map.Entry lineMeasure : lineMeasures.entrySet()) { + String value = lineMeasure.getValue(); + if (StringUtils.isNotEmpty(value)) { + FileSourceDb.Line.Builder lineBuilder = to.getLinesBuilder(lineMeasure.getKey() - 1); + op.apply(value, lineBuilder); + } + } + } + } + } + + static interface MeasureOperation { + void apply(String value, FileSourceDb.Line.Builder lineBuilder); + } + + void applyHighlighting(DefaultInputFile inputFile, FileSourceDb.Data.Builder to) { + SyntaxHighlightingData highlighting = componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING); + String language = inputFile.language(); + if (highlighting == null && language != null) { + highlighting = codeColorizers.toSyntaxHighlighting(inputFile.file(), inputFile.encoding(), language); + } + if (highlighting == null) { + return; + } + StringBuilder[] highlightingPerLine = new StringBuilder[inputFile.lines()]; + RuleItemWriter ruleItemWriter = new RuleItemWriter(); + int currentLineIdx = 1; + for (SyntaxHighlightingRule rule : highlighting.syntaxHighlightingRuleSet()) { + while (currentLineIdx < inputFile.lines() && rule.getStartPosition() >= inputFile.originalLineOffsets()[currentLineIdx]) { + // This rule starts on another line so advance + currentLineIdx++; + } + // Now we know current rule starts on current line + writeDataPerLine(inputFile.originalLineOffsets(), rule, rule.getStartPosition(), rule.getEndPosition(), highlightingPerLine, currentLineIdx, ruleItemWriter); + } + for (int i = 0; i < highlightingPerLine.length; i++) { + StringBuilder sb = highlightingPerLine[i]; + if (sb != null) { + to.getLinesBuilder(i).setHighlighting(sb.toString()); + } + } + } + + void applySymbolReferences(DefaultInputFile file, FileSourceDb.Data.Builder to) { + SymbolData symbolRefs = componentDataCache.getData(file.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING); + if (symbolRefs != null) { + StringBuilder[] refsPerLine = new StringBuilder[file.lines()]; + int symbolId = 1; + List symbols = new ArrayList(symbolRefs.referencesBySymbol().keySet()); + // Sort symbols to avoid false variation that would lead to an unnecessary update + Collections.sort(symbols, new Comparator() { + @Override + public int compare(Symbol o1, Symbol o2) { + return o1.getDeclarationStartOffset() - o2.getDeclarationStartOffset(); + } + }); + for (Symbol symbol : symbols) { + int declarationStartOffset = symbol.getDeclarationStartOffset(); + int declarationEndOffset = symbol.getDeclarationEndOffset(); + int length = declarationEndOffset - declarationStartOffset; + addSymbol(symbolId, declarationStartOffset, declarationEndOffset, file.originalLineOffsets(), refsPerLine); + for (Integer referenceStartOffset : symbolRefs.referencesBySymbol().get(symbol)) { + if (referenceStartOffset == declarationStartOffset) { + // Ignore old API that used to store reference as first declaration + continue; + } + addSymbol(symbolId, referenceStartOffset, referenceStartOffset + length, file.originalLineOffsets(), refsPerLine); + } + symbolId++; + } + for (int i = 0; i < refsPerLine.length; i++) { + StringBuilder sb = refsPerLine[i]; + if (sb != null) { + to.getLinesBuilder(i).setSymbols(sb.toString()); + } + } + } + } + + private void addSymbol(int symbolId, int startOffset, int endOffset, long[] originalLineOffsets, StringBuilder[] result) { + int startLine = binarySearchLine(startOffset, originalLineOffsets); + writeDataPerLine(originalLineOffsets, symbolId, startOffset, endOffset, result, startLine, new SymbolItemWriter()); + } + + private int binarySearchLine(int declarationStartOffset, long[] originalLineOffsets) { + int begin = 0; + int end = originalLineOffsets.length - 1; + while (begin < end) { + int mid = (int) Math.round((begin + end) / 2D); + if (declarationStartOffset < originalLineOffsets[mid]) { + end = mid - 1; + } else { + begin = mid; + } + } + return begin + 1; + } + + private void writeDataPerLine(long[] originalLineOffsets, G item, int globalStartOffset, int globalEndOffset, StringBuilder[] dataPerLine, int startLine, + RangeItemWriter writer) { + int currentLineIdx = startLine; + // We know current item starts on current line + long ruleStartOffsetCurrentLine = globalStartOffset; + while (currentLineIdx < originalLineOffsets.length && globalEndOffset >= originalLineOffsets[currentLineIdx]) { + // item continue on next line so write current line and continue on next line with same item + writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine - originalLineOffsets[currentLineIdx - 1], originalLineOffsets[currentLineIdx] + - originalLineOffsets[currentLineIdx - 1], writer); + currentLineIdx++; + ruleStartOffsetCurrentLine = originalLineOffsets[currentLineIdx - 1]; + } + // item ends on current line + writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine - originalLineOffsets[currentLineIdx - 1], globalEndOffset + - originalLineOffsets[currentLineIdx - 1], writer); + } + + private void writeItem(G item, StringBuilder[] dataPerLine, int currentLineIdx, long startLineOffset, long endLineOffset, RangeItemWriter writer) { + if (startLineOffset == endLineOffset) { + // Do not store empty items + return; + } + if (dataPerLine[currentLineIdx - 1] == null) { + dataPerLine[currentLineIdx - 1] = new StringBuilder(); + } + StringBuilder currentLineSb = dataPerLine[currentLineIdx - 1]; + writer.writeItem(currentLineSb, startLineOffset, endLineOffset, item); + } + + private static interface RangeItemWriter { + /** + * Write item on a single line + */ + void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, G item); + } + + private static class RuleItemWriter implements RangeItemWriter { + @Override + public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, SyntaxHighlightingRule item) { + if (currentLineSb.length() > 0) { + currentLineSb.append(SyntaxHighlightingData.RULE_SEPARATOR); + } + currentLineSb.append(startLineOffset) + .append(SyntaxHighlightingData.FIELD_SEPARATOR) + .append(endLineOffset) + .append(SyntaxHighlightingData.FIELD_SEPARATOR) + .append(item.getTextType().cssClass()); + } + + } + + private static class SymbolItemWriter implements RangeItemWriter { + @Override + public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, Integer symbolId) { + if (currentLineSb.length() > 0) { + currentLineSb.append(SymbolData.SYMBOL_SEPARATOR); + } + currentLineSb.append(startLineOffset) + .append(SymbolData.FIELD_SEPARATOR) + .append(endLineOffset) + .append(SymbolData.FIELD_SEPARATOR) + .append(symbolId); + } + } + + void applyDuplications(String inputFileKey, FileSourceDb.Data.Builder to) { + List groups = duplicationCache.byComponent(inputFileKey); + if (groups != null) { + Multimap duplicationsPerLine = ArrayListMultimap.create(); + int blockId = 1; + for (Iterator it = groups.iterator(); it.hasNext();) { + DuplicationGroup group = it.next(); + addBlock(blockId, group.originBlock(), duplicationsPerLine); + blockId++; + for (Iterator dupsIt = group.duplicates().iterator(); dupsIt.hasNext();) { + DuplicationGroup.Block dups = dupsIt.next(); + if (inputFileKey.equals(dups.resourceKey())) { + addBlock(blockId, dups, duplicationsPerLine); + blockId++; + } + // Save memory + dupsIt.remove(); + } + // Save memory + it.remove(); + } + for (Map.Entry> entry : duplicationsPerLine.asMap().entrySet()) { + to.getLinesBuilder(entry.getKey() - 1).addAllDuplications(entry.getValue()); + } + } + } + + private void addBlock(int blockId, DuplicationGroup.Block block, Multimap dupPerLine) { + int currentLine = block.startLine(); + for (int i = 0; i < block.length(); i++) { + dupPerLine.put(currentLine, blockId); + currentLine++; + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java index 76fdd9015f5..a7a18548418 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/SourcePersister.java @@ -19,82 +19,43 @@ */ package org.sonar.batch.index; -import com.google.common.base.CharMatcher; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.StringUtils; import org.apache.ibatis.session.ResultContext; import org.apache.ibatis.session.ResultHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputPath; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.sensor.duplication.DuplicationGroup; -import org.sonar.api.batch.sensor.duplication.DuplicationGroup.Block; -import org.sonar.api.batch.sensor.symbol.Symbol; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.utils.KeyValueFormat; import org.sonar.api.utils.System2; -import org.sonar.api.utils.text.CsvWriter; import org.sonar.batch.ProjectTree; -import org.sonar.batch.duplication.DuplicationCache; -import org.sonar.batch.highlighting.SyntaxHighlightingData; -import org.sonar.batch.highlighting.SyntaxHighlightingRule; import org.sonar.batch.scan.filesystem.InputPathCache; -import org.sonar.batch.scan.measure.MeasureCache; -import org.sonar.batch.source.CodeColorizers; -import org.sonar.batch.symbol.SymbolData; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; -import org.sonar.core.source.SnapshotDataTypes; import org.sonar.core.source.db.FileSourceDto; import org.sonar.core.source.db.FileSourceMapper; import javax.annotation.CheckForNull; -import javax.annotation.Nullable; import java.io.IOException; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; import java.util.Map; public class SourcePersister implements ScanPersister { - private static final Logger LOG = LoggerFactory.getLogger(SourcePersister.class); - - private static final String BOM = "\uFEFF"; - private final InputPathCache inputPathCache; private final MyBatis mybatis; - private final MeasureCache measureCache; - private final ComponentDataCache componentDataCache; private final System2 system2; private final ProjectTree projectTree; private final ResourceCache resourceCache; - private CodeColorizers codeColorizers; - private DuplicationCache duplicationCache; + private final InputPathCache inputPathCache; + private final SourceDataFactory dataFactory; - public SourcePersister(InputPathCache inputPathCache, - MyBatis mybatis, MeasureCache measureCache, ComponentDataCache componentDataCache, ProjectTree projectTree, System2 system2, - ResourceCache resourceCache, CodeColorizers codeColorizers, DuplicationCache duplicationCache) { + public SourcePersister(InputPathCache inputPathCache, MyBatis mybatis, System2 system2, + ProjectTree projectTree, ResourceCache resourceCache, SourceDataFactory dataFactory) { this.inputPathCache = inputPathCache; this.mybatis = mybatis; - this.measureCache = measureCache; - this.componentDataCache = componentDataCache; - this.projectTree = projectTree; this.system2 = system2; + this.projectTree = projectTree; this.resourceCache = resourceCache; - this.codeColorizers = codeColorizers; - this.duplicationCache = duplicationCache; + this.dataFactory = dataFactory; } @Override @@ -102,22 +63,19 @@ public class SourcePersister implements ScanPersister { // Don't use batch insert for file_sources since keeping all data in memory can produce OOM for big files try (DbSession session = mybatis.openSession(false)) { - final Map fileSourceDtoByFileUuid = new HashMap(); - - session.select("org.sonar.core.source.db.FileSourceMapper.selectAllFileDataHashByProject", projectTree.getRootProject().getUuid(), new ResultHandler() { - + final Map previousDtosByUuid = new HashMap<>(); + session.select("org.sonar.core.source.db.FileSourceMapper.selectHashesForProject", projectTree.getRootProject().getUuid(), new ResultHandler() { @Override public void handleResult(ResultContext context) { FileSourceDto dto = (FileSourceDto) context.getResultObject(); - fileSourceDtoByFileUuid.put(dto.getFileUuid(), dto); + previousDtosByUuid.put(dto.getFileUuid(), dto); } }); FileSourceMapper mapper = session.getMapper(FileSourceMapper.class); - for (InputPath inputPath : inputPathCache.all()) { - if (inputPath instanceof InputFile) { - persist(session, mapper, inputPath, fileSourceDtoByFileUuid); + if (inputPath instanceof DefaultInputFile) { + persist(session, mapper, (DefaultInputFile) inputPath, previousDtosByUuid); } } } catch (Exception e) { @@ -126,43 +84,36 @@ public class SourcePersister implements ScanPersister { } - private void persist(DbSession session, FileSourceMapper mapper, InputPath inputPath, Map fileSourceDtoByFileUuid) { - DefaultInputFile inputFile = (DefaultInputFile) inputPath; - LOG.debug("Processing {}", inputFile.absolutePath()); - org.sonar.api.resources.File file = (org.sonar.api.resources.File) resourceCache.get(inputFile.key()).resource(); - String fileUuid = file.getUuid(); - FileSourceDto previous = fileSourceDtoByFileUuid.get(fileUuid); - String newData = getSourceData(inputFile); - String newDataHash = newData != null ? DigestUtils.md5Hex(newData) : "0"; - Date now = system2.newDate(); - try { - if (previous == null) { - FileSourceDto newFileSource = new FileSourceDto() - .setProjectUuid(projectTree.getRootProject().getUuid()) - .setFileUuid(fileUuid) - .setData(newData) - .setDataHash(newDataHash) + private void persist(DbSession session, FileSourceMapper mapper, DefaultInputFile inputFile, Map previousDtosByUuid) { + String fileUuid = resourceCache.get(inputFile.key()).resource().getUuid(); + + byte[] data = computeData(inputFile); + String dataHash = DigestUtils.md5Hex(data); + FileSourceDto previousDto = previousDtosByUuid.get(fileUuid); + if (previousDto == null) { + FileSourceDto dto = new FileSourceDto() + .setProjectUuid(projectTree.getRootProject().getUuid()) + .setFileUuid(fileUuid) + .setBinaryData(data) + .setDataHash(dataHash) + .setSrcHash(inputFile.hash()) + .setLineHashes(lineHashesAsMd5Hex(inputFile)) + .setCreatedAt(system2.now()) + .setUpdatedAt(system2.now()); + mapper.insert(dto); + session.commit(); + } else { + // Update only if data_hash has changed or if src_hash is missing (progressive migration) + if (!dataHash.equals(previousDto.getDataHash()) || !inputFile.hash().equals(previousDto.getSrcHash())) { + previousDto + .setBinaryData(data) + .setDataHash(dataHash) .setSrcHash(inputFile.hash()) .setLineHashes(lineHashesAsMd5Hex(inputFile)) - .setCreatedAt(now.getTime()) - .setUpdatedAt(now.getTime()); - mapper.insert(newFileSource); + .setUpdatedAt(system2.now()); + mapper.update(previousDto); session.commit(); - } else { - // Update only if data_hash has changed or if src_hash is missing (progressive migration) - if (!newDataHash.equals(previous.getDataHash()) || !inputFile.hash().equals(previous.getSrcHash())) { - previous - .setData(newData) - .setLineHashes(lineHashesAsMd5Hex(inputFile)) - .setDataHash(newDataHash) - .setSrcHash(inputFile.hash()) - .setUpdatedAt(now.getTime()); - mapper.update(previous); - session.commit(); - } } - } catch (Exception e) { - throw new IllegalStateException("Unable to save file sources for " + inputPath.absolutePath(), e); } } @@ -182,284 +133,11 @@ public class SourcePersister implements ScanPersister { return result.toString(); } - @CheckForNull - String getSourceData(DefaultInputFile file) { - if (file.lines() == 0) { - return null; - } - List lines; + private byte[] computeData(DefaultInputFile inputFile) { try { - lines = FileUtils.readLines(file.file(), file.encoding()); + return dataFactory.consolidateData(inputFile); } catch (IOException e) { - throw new IllegalStateException("Unable to read file", e); - } - // Missing empty last line - if (lines.size() == file.lines() - 1) { - lines.add(""); - } - Map authorsByLine = getLineMetric(file, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY); - Map revisionsByLine = getLineMetric(file, CoreMetrics.SCM_REVISIONS_BY_LINE_KEY); - Map datesByLine = getLineMetric(file, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY); - Map utHitsByLine = getLineMetric(file, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY); - Map utCondByLine = getLineMetric(file, CoreMetrics.CONDITIONS_BY_LINE_KEY); - Map utCoveredCondByLine = getLineMetric(file, CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY); - Map itHitsByLine = getLineMetric(file, CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY); - Map itCondByLine = getLineMetric(file, CoreMetrics.IT_CONDITIONS_BY_LINE_KEY); - Map itCoveredCondByLine = getLineMetric(file, CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY); - Map overallHitsByLine = getLineMetric(file, CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY); - Map overallCondByLine = getLineMetric(file, CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY); - Map overallCoveredCondByLine = getLineMetric(file, CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY); - SyntaxHighlightingData highlighting = loadHighlighting(file); - String[] highlightingPerLine = computeHighlightingPerLine(file, highlighting); - String[] symbolReferencesPerLine = computeSymbolReferencesPerLine(file, loadSymbolReferences(file)); - String[] duplicationsPerLine = computeDuplicationsPerLine(file, duplicationCache.byComponent(file.key())); - - StringWriter writer = new StringWriter(file.lines() * 16); - CsvWriter csv = CsvWriter.of(writer); - for (int lineIdx = 1; lineIdx <= file.lines(); lineIdx++) { - csv.values(revisionsByLine.get(lineIdx), authorsByLine.get(lineIdx), datesByLine.get(lineIdx), - utHitsByLine.get(lineIdx), utCondByLine.get(lineIdx), utCoveredCondByLine.get(lineIdx), - itHitsByLine.get(lineIdx), itCondByLine.get(lineIdx), itCoveredCondByLine.get(lineIdx), - overallHitsByLine.get(lineIdx), overallCondByLine.get(lineIdx), overallCoveredCondByLine.get(lineIdx), - highlightingPerLine[lineIdx - 1], symbolReferencesPerLine[lineIdx - 1], duplicationsPerLine[lineIdx - 1], - CharMatcher.anyOf(BOM).removeFrom(lines.get(lineIdx - 1))); - // Free memory - revisionsByLine.remove(lineIdx); - authorsByLine.remove(lineIdx); - datesByLine.remove(lineIdx); - utHitsByLine.remove(lineIdx); - utCondByLine.remove(lineIdx); - utCoveredCondByLine.remove(lineIdx); - itHitsByLine.remove(lineIdx); - itCondByLine.remove(lineIdx); - itCoveredCondByLine.remove(lineIdx); - overallHitsByLine.remove(lineIdx); - overallCondByLine.remove(lineIdx); - overallCoveredCondByLine.remove(lineIdx); - highlightingPerLine[lineIdx - 1] = null; - symbolReferencesPerLine[lineIdx - 1] = null; - duplicationsPerLine[lineIdx - 1] = null; - lines.set(lineIdx - 1, null); - } - csv.close(); - return StringUtils.defaultIfEmpty(writer.toString(), null); - } - - private String[] computeDuplicationsPerLine(DefaultInputFile file, List duplicationGroups) { - String[] result = new String[file.lines()]; - if (duplicationGroups == null) { - return result; - } - List groups = new LinkedList(duplicationGroups); - StringBuilder[] dupPerLine = new StringBuilder[file.lines()]; - int blockId = 1; - for (Iterator it = groups.iterator(); it.hasNext();) { - DuplicationGroup group = it.next(); - addBlock(blockId, group.originBlock(), dupPerLine); - blockId++; - for (Iterator dupsIt = group.duplicates().iterator(); dupsIt.hasNext();) { - Block dups = dupsIt.next(); - if (dups.resourceKey().equals(file.key())) { - addBlock(blockId, dups, dupPerLine); - blockId++; - } - // Save memory - dupsIt.remove(); - } - // Save memory - it.remove(); - } - for (int i = 0; i < file.lines(); i++) { - result[i] = dupPerLine[i] != null ? dupPerLine[i].toString() : null; - // Save memory - dupPerLine[i] = null; - } - return result; - } - - private void addBlock(int blockId, Block block, StringBuilder[] dupPerLine) { - int currentLine = block.startLine(); - for (int i = 0; i < block.length(); i++) { - if (dupPerLine[currentLine - 1] == null) { - dupPerLine[currentLine - 1] = new StringBuilder(); - } - if (dupPerLine[currentLine - 1].length() > 0) { - dupPerLine[currentLine - 1].append(','); - } - dupPerLine[currentLine - 1].append(blockId); - currentLine++; - } - - } - - @CheckForNull - private SyntaxHighlightingData loadHighlighting(DefaultInputFile file) { - SyntaxHighlightingData highlighting = componentDataCache.getData(file.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING); - String language = file.language(); - if (highlighting == null && language != null) { - highlighting = codeColorizers.toSyntaxHighlighting(file.file(), file.encoding(), language); - } - return highlighting; - } - - @CheckForNull - private SymbolData loadSymbolReferences(DefaultInputFile file) { - return componentDataCache.getData(file.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING); - } - - String[] computeHighlightingPerLine(DefaultInputFile file, @Nullable SyntaxHighlightingData highlighting) { - String[] result = new String[file.lines()]; - if (highlighting == null) { - return result; - } - Iterable rules = highlighting.syntaxHighlightingRuleSet(); - int currentLineIdx = 1; - StringBuilder[] highlightingPerLine = new StringBuilder[file.lines()]; - for (SyntaxHighlightingRule rule : rules) { - while (currentLineIdx < file.lines() && rule.getStartPosition() >= file.originalLineOffsets()[currentLineIdx]) { - // This rule starts on another line so advance - currentLineIdx++; - } - // Now we know current rule starts on current line - writeDataPerLine(file.originalLineOffsets(), rule, rule.getStartPosition(), rule.getEndPosition(), highlightingPerLine, currentLineIdx, new RuleItemWriter()); - } - for (int i = 0; i < file.lines(); i++) { - result[i] = highlightingPerLine[i] != null ? highlightingPerLine[i].toString() : null; - } - return result; - } - - String[] computeSymbolReferencesPerLine(DefaultInputFile file, @Nullable SymbolData symbolRefs) { - String[] result = new String[file.lines()]; - if (symbolRefs == null) { - return result; - } - StringBuilder[] symbolRefsPerLine = new StringBuilder[file.lines()]; - long[] originalLineOffsets = file.originalLineOffsets(); - int symbolId = 1; - List symbols = new ArrayList(symbolRefs.referencesBySymbol().keySet()); - // Sort symbols to avoid false variation that would lead to an unnecessary update - Collections.sort(symbols, new Comparator() { - @Override - public int compare(Symbol o1, Symbol o2) { - return o1.getDeclarationStartOffset() - o2.getDeclarationStartOffset(); - } - }); - for (Symbol symbol : symbols) { - int declarationStartOffset = symbol.getDeclarationStartOffset(); - int declarationEndOffset = symbol.getDeclarationEndOffset(); - int length = declarationEndOffset - declarationStartOffset; - addSymbol(symbolId, declarationStartOffset, declarationEndOffset, originalLineOffsets, symbolRefsPerLine); - for (Integer referenceStartOffset : symbolRefs.referencesBySymbol().get(symbol)) { - if (referenceStartOffset == declarationStartOffset) { - // Ignore old API that used to store reference as first declaration - continue; - } - addSymbol(symbolId, referenceStartOffset, referenceStartOffset + length, originalLineOffsets, symbolRefsPerLine); - } - symbolId++; - } - for (int i = 0; i < file.lines(); i++) { - result[i] = symbolRefsPerLine[i] != null ? symbolRefsPerLine[i].toString() : null; - } - return result; - } - - private void addSymbol(int symbolId, int startOffset, int endOffset, long[] originalLineOffsets, StringBuilder[] result) { - int startLine = binarySearchLine(startOffset, originalLineOffsets); - writeDataPerLine(originalLineOffsets, symbolId, startOffset, endOffset, result, startLine, new SymbolItemWriter()); - } - - private int binarySearchLine(int declarationStartOffset, long[] originalLineOffsets) { - int begin = 0; - int end = originalLineOffsets.length - 1; - while (begin < end) { - int mid = (int) Math.round((begin + end) / 2D); - if (declarationStartOffset < originalLineOffsets[mid]) { - end = mid - 1; - } else { - begin = mid; - } - } - return begin + 1; - } - - private void writeDataPerLine(long[] originalLineOffsets, G item, int globalStartOffset, int globalEndOffset, StringBuilder[] dataPerLine, int startLine, - RangeItemWriter writer) { - int currentLineIdx = startLine; - // We know current item starts on current line - long ruleStartOffsetCurrentLine = globalStartOffset; - while (currentLineIdx < originalLineOffsets.length && globalEndOffset >= originalLineOffsets[currentLineIdx]) { - // item continue on next line so write current line and continue on next line with same item - writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine - originalLineOffsets[currentLineIdx - 1], originalLineOffsets[currentLineIdx] - - originalLineOffsets[currentLineIdx - 1], writer); - currentLineIdx++; - ruleStartOffsetCurrentLine = originalLineOffsets[currentLineIdx - 1]; - } - // item ends on current line - writeItem(item, dataPerLine, currentLineIdx, ruleStartOffsetCurrentLine - originalLineOffsets[currentLineIdx - 1], globalEndOffset - - originalLineOffsets[currentLineIdx - 1], writer); - } - - private void writeItem(G item, StringBuilder[] dataPerLine, int currentLineIdx, long startLineOffset, long endLineOffset, RangeItemWriter writer) { - if (startLineOffset == endLineOffset) { - // Do not store empty items - return; - } - if (dataPerLine[currentLineIdx - 1] == null) { - dataPerLine[currentLineIdx - 1] = new StringBuilder(); - } - StringBuilder currentLineSb = dataPerLine[currentLineIdx - 1]; - writer.writeItem(currentLineSb, startLineOffset, endLineOffset, item); - } - - private static interface RangeItemWriter { - /** - * Write item on a single line - */ - void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, G item); - } - - private static class RuleItemWriter implements RangeItemWriter { - - @Override - public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, SyntaxHighlightingRule item) { - if (currentLineSb.length() > 0) { - currentLineSb.append(SyntaxHighlightingData.RULE_SEPARATOR); - } - currentLineSb.append(startLineOffset) - .append(SyntaxHighlightingData.FIELD_SEPARATOR) - .append(endLineOffset) - .append(SyntaxHighlightingData.FIELD_SEPARATOR) - .append(item.getTextType().cssClass()); - } - - } - - private static class SymbolItemWriter implements RangeItemWriter { - - @Override - public void writeItem(StringBuilder currentLineSb, long startLineOffset, long endLineOffset, Integer symbolId) { - if (currentLineSb.length() > 0) { - currentLineSb.append(SymbolData.SYMBOL_SEPARATOR); - } - currentLineSb.append(startLineOffset) - .append(SymbolData.FIELD_SEPARATOR) - .append(endLineOffset) - .append(SymbolData.FIELD_SEPARATOR) - .append(symbolId); - } - - } - - private Map getLineMetric(DefaultInputFile file, String metricKey) { - Map authorsByLine; - Iterator authorsIt = measureCache.byMetric(file.key(), metricKey).iterator(); - if (authorsIt.hasNext()) { - authorsByLine = KeyValueFormat.parseIntString((String) authorsIt.next().value()); - } else { - authorsByLine = Collections.emptyMap(); + throw new IllegalStateException("Fail to read file " + inputFile, e); } - return authorsByLine; } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index 8edc824b1fd..30ef5615488 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -56,6 +56,7 @@ import org.sonar.batch.index.MeasurePersister; import org.sonar.batch.index.ResourceCache; import org.sonar.batch.index.ResourceKeyMigration; import org.sonar.batch.index.ResourcePersister; +import org.sonar.batch.index.SourceDataFactory; import org.sonar.batch.index.SourcePersister; import org.sonar.batch.issue.DefaultProjectIssues; import org.sonar.batch.issue.IssueCache; @@ -147,6 +148,7 @@ public class ProjectScanContainer extends ComponentContainer { Caches.class, ResourceCache.class, ComponentDataCache.class, + SourceDataFactory.class, // file system InputPathCache.class, diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/SourceDataFactoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/SourceDataFactoryTest.java new file mode 100644 index 00000000000..821cb735756 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/index/SourceDataFactoryTest.java @@ -0,0 +1,305 @@ +/* + * 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.batch.index; + +import com.google.common.base.Charsets; +import com.google.common.collect.Lists; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.duplication.DuplicationGroup; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.Metric; +import org.sonar.batch.duplication.DuplicationCache; +import org.sonar.batch.highlighting.SyntaxHighlightingData; +import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder; +import org.sonar.batch.scan.measure.MeasureCache; +import org.sonar.batch.source.CodeColorizers; +import org.sonar.batch.symbol.DefaultSymbolTableBuilder; +import org.sonar.core.source.SnapshotDataTypes; +import org.sonar.server.source.db.FileSourceDb; + +import java.io.File; +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SourceDataFactoryTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + MeasureCache measureCache = mock(MeasureCache.class); + ComponentDataCache componentDataCache = mock(ComponentDataCache.class); + DuplicationCache duplicationCache = mock(DuplicationCache.class); + CodeColorizers colorizers = mock(CodeColorizers.class); + DefaultInputFile inputFile; + SourceDataFactory sut = new SourceDataFactory(measureCache, componentDataCache, duplicationCache, colorizers); + FileSourceDb.Data.Builder output; + + @Before + public void setUp() throws Exception { + // generate a file with 3 lines + File file = temp.newFile(); + inputFile = new DefaultInputFile("module_key", "src/Foo.java") + .setLines(3) + .setEncoding(Charsets.UTF_8.name()) + .setFile(file); + FileUtils.write(file, "one\ntwo\nthree\n"); + output = sut.createForSource(inputFile); + } + + @Test + public void createForSource() throws Exception { + FileSourceDb.Data data = output.build(); + assertThat(data.getLinesCount()).isEqualTo(3); + for (int index = 1; index <= 3; index++) { + assertThat(data.getLines(index - 1).getLine()).isEqualTo(index); + } + } + + @Test + public void consolidateData() throws Exception { + byte[] bytes = sut.consolidateData(inputFile); + assertThat(bytes).isNotEmpty(); + } + + @Test + public void applyLineMeasure() throws Exception { + Metric metric = CoreMetrics.COVERAGE_LINE_HITS_DATA; + when(measureCache.byMetric("component_key", metric.key())).thenReturn( + // line 1 has 10 hits, ... + Arrays.asList(new Measure().setData("1=10;3=4").setMetric(metric))); + + sut.applyLineMeasure("component_key", metric.key(), output, new SourceDataFactory.MeasureOperation() { + @Override + public void apply(String value, FileSourceDb.Line.Builder lineBuilder) { + lineBuilder.setUtLineHits(Integer.parseInt(value)); + } + }); + + FileSourceDb.Data data = output.build(); + assertThat(data.getLines(0).getUtLineHits()).isEqualTo(10); + assertThat(data.getLines(1).hasUtLineHits()).isFalse(); + assertThat(data.getLines(2).getUtLineHits()).isEqualTo(4); + } + + @Test + public void applyLineMeasures() throws Exception { + setupLineMeasure(CoreMetrics.SCM_AUTHORS_BY_LINE, "1=him;2=her"); + setupLineMeasure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "1=2014-10-11T16:44:02+0100;2=2014-10-12T16:44:02+0100;3=2014-10-13T16:44:02+0100"); + setupLineMeasure(CoreMetrics.SCM_REVISIONS_BY_LINE, "1=ABC;2=234;3=345"); + setupLineMeasure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "1=10;3=4"); + setupLineMeasure(CoreMetrics.CONDITIONS_BY_LINE, "1=10;3=4"); + setupLineMeasure(CoreMetrics.CONDITIONS_BY_LINE, "1=10;3=4"); + setupLineMeasure(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "1=10;3=4"); + setupLineMeasure(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA, "1=11;2=4"); + setupLineMeasure(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE, "1=10;3=4"); + setupLineMeasure(CoreMetrics.IT_CONDITIONS_BY_LINE, "1=10;3=4"); + setupLineMeasure(CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA, "1=10;3=4"); + setupLineMeasure(CoreMetrics.OVERALL_CONDITIONS_BY_LINE, "1=10;3=4"); + setupLineMeasure(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE, "1=10;3=4"); + + sut.applyLineMeasures(inputFile, output); + + FileSourceDb.Data data = output.build(); + assertThat(data.getLines(0).getUtLineHits()).isEqualTo(10); + assertThat(data.getLines(0).getItLineHits()).isEqualTo(11); + assertThat(data.getLines(0).getScmRevision()).isEqualTo("ABC"); + assertThat(data.getLines(0).getScmAuthor()).isEqualTo("him"); + + assertThat(data.getLines(1).hasUtLineHits()).isFalse(); + assertThat(data.getLines(1).getItLineHits()).isEqualTo(4); + assertThat(data.getLines(1).getScmAuthor()).isEqualTo("her"); + + assertThat(data.getLines(2).getUtLineHits()).isEqualTo(4); + assertThat(data.getLines(2).hasScmAuthor()).isFalse(); + } + + private void setupLineMeasure(Metric metric, String dataPerLine) { + when(measureCache.byMetric(inputFile.key(), metric.key())).thenReturn( + Arrays.asList(new Measure().setData(dataPerLine).setMetric(metric))); + } + + + @Test + public void applyDuplications() throws Exception { + DuplicationGroup group1 = new DuplicationGroup(new DuplicationGroup.Block(inputFile.key(), 1, 1)) + .addDuplicate(new DuplicationGroup.Block(inputFile.key(), 3, 1)) + .addDuplicate(new DuplicationGroup.Block("anotherFile1", 12, 1)) + .addDuplicate(new DuplicationGroup.Block("anotherFile2", 13, 1)); + DuplicationGroup group2 = new DuplicationGroup(new DuplicationGroup.Block(inputFile.key(), 1, 2)) + .addDuplicate(new DuplicationGroup.Block("anotherFile1", 12, 2)) + .addDuplicate(new DuplicationGroup.Block("anotherFile2", 13, 2)); + when(duplicationCache.byComponent(inputFile.key())).thenReturn(Lists.newArrayList(group1, group2)); + + sut.applyDuplications(inputFile.key(), output); + + FileSourceDb.Data data = output.build(); + assertThat(data.getLines(0).getDuplicationsList()).containsExactly(1, 3); + assertThat(data.getLines(1).getDuplicationsList()).containsExactly(3); + assertThat(data.getLines(2).getDuplicationsList()).containsExactly(2); + } + + @Test + public void applyHighlighting_missing() throws Exception { + when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(null); + + sut.applyHighlighting(inputFile, output); + + FileSourceDb.Data data = output.build(); + assertThat(data.getLines(0).hasHighlighting()).isFalse(); + assertThat(data.getLines(1).hasHighlighting()).isFalse(); + assertThat(data.getLines(2).hasHighlighting()).isFalse(); + } + + @Test + public void applyHighlighting() throws Exception { + SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() + .registerHighlightingRule(0, 4, TypeOfText.ANNOTATION) + .registerHighlightingRule(4, 5, TypeOfText.COMMENT) + .registerHighlightingRule(7, 16, TypeOfText.CONSTANT) + .build(); + when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting); + inputFile.setOriginalLineOffsets(new long[] {0, 4, 7}); + + sut.applyHighlighting(inputFile, output); + + FileSourceDb.Data data = output.build(); + assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,4,a"); + assertThat(data.getLines(1).getHighlighting()).isEqualTo("0,1,cd"); + assertThat(data.getLines(2).getHighlighting()).isEqualTo("0,9,c"); + } + + @Test + public void applyHighlighting_multiple_lines() throws Exception { + SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() + .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION) + .registerHighlightingRule(4, 9, TypeOfText.COMMENT) + .registerHighlightingRule(10, 16, TypeOfText.CONSTANT) + .build(); + when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting); + inputFile.setOriginalLineOffsets(new long[] {0, 4, 7}); + + sut.applyHighlighting(inputFile, output); + + FileSourceDb.Data data = output.build(); + assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,3,a"); + assertThat(data.getLines(1).getHighlighting()).isEqualTo("0,3,cd"); + assertThat(data.getLines(2).getHighlighting()).isEqualTo("0,2,cd;3,9,c"); + } + + @Test + public void applyHighlighting_nested_rules() throws Exception { + SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() + .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION) + .registerHighlightingRule(4, 6, TypeOfText.COMMENT) + .registerHighlightingRule(7, 16, TypeOfText.CONSTANT) + .registerHighlightingRule(8, 15, TypeOfText.KEYWORD) + .build(); + when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting); + inputFile.setOriginalLineOffsets(new long[] {0, 4, 7}); + + sut.applyHighlighting(inputFile, output); + + FileSourceDb.Data data = output.build(); + assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,3,a"); + assertThat(data.getLines(1).getHighlighting()).isEqualTo("0,2,cd"); + assertThat(data.getLines(2).getHighlighting()).isEqualTo("0,9,c;1,8,k"); + } + + @Test + public void applyHighlighting_nested_rules_and_multiple_lines() throws Exception { + SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() + .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION) + .registerHighlightingRule(4, 6, TypeOfText.COMMENT) + .registerHighlightingRule(4, 16, TypeOfText.CONSTANT) + .registerHighlightingRule(8, 15, TypeOfText.KEYWORD) + .build(); + when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYNTAX_HIGHLIGHTING)).thenReturn(highlighting); + inputFile.setOriginalLineOffsets(new long[] {0, 4, 7}); + + sut.applyHighlighting(inputFile, output); + + FileSourceDb.Data data = output.build(); + assertThat(data.getLines(0).getHighlighting()).isEqualTo("0,3,a"); + assertThat(data.getLines(1).getHighlighting()).isEqualTo("0,3,c;0,2,cd"); + assertThat(data.getLines(2).getHighlighting()).isEqualTo("0,9,c;1,8,k"); + } + + @Test + public void applySymbolReferences_missing() throws Exception { + when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING)).thenReturn(null); + + sut.applySymbolReferences(inputFile, output); + + FileSourceDb.Data data = output.build(); + assertThat(data.getLines(0).hasSymbols()).isFalse(); + assertThat(data.getLines(1).hasSymbols()).isFalse(); + assertThat(data.getLines(2).hasSymbols()).isFalse(); + } + + @Test + public void applySymbolReferences() throws Exception { + DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(inputFile.key(), null); + org.sonar.api.batch.sensor.symbol.Symbol s1 = symbolBuilder.newSymbol(1, 2); + symbolBuilder.newReference(s1, 4); + symbolBuilder.newReference(s1, 11); + org.sonar.api.batch.sensor.symbol.Symbol s2 = symbolBuilder.newSymbol(4, 6); + symbolBuilder.newReference(s2, 0); + symbolBuilder.newReference(s2, 7); + when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING)).thenReturn(symbolBuilder.build()); + inputFile.setOriginalLineOffsets(new long[] {0, 4, 7}); + + sut.applySymbolReferences(inputFile, output); + + FileSourceDb.Data data = output.build(); + assertThat(data.getLines(0).getSymbols()).isEqualTo("1,2,1;0,2,2"); + assertThat(data.getLines(1).getSymbols()).isEqualTo("0,1,1;0,2,2"); + assertThat(data.getLines(2).getSymbols()).isEqualTo("4,5,1;0,2,2"); + } + + @Test + public void applySymbolReferences_declaration_order_is_not_important() throws Exception { + DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(inputFile.key(), null); + org.sonar.api.batch.sensor.symbol.Symbol s2 = symbolBuilder.newSymbol(4, 6); + symbolBuilder.newReference(s2, 7); + symbolBuilder.newReference(s2, 0); + org.sonar.api.batch.sensor.symbol.Symbol s1 = symbolBuilder.newSymbol(1, 2); + symbolBuilder.newReference(s1, 11); + symbolBuilder.newReference(s1, 4); + when(componentDataCache.getData(inputFile.key(), SnapshotDataTypes.SYMBOL_HIGHLIGHTING)).thenReturn(symbolBuilder.build()); + inputFile.setOriginalLineOffsets(new long[] {0, 4, 7}); + + sut.applySymbolReferences(inputFile, output); + + FileSourceDb.Data data = output.build(); + assertThat(data.getLines(0).getSymbols()).isEqualTo("1,2,1;0,2,2"); + assertThat(data.getLines(1).getSymbols()).isEqualTo("0,1,1;0,2,2"); + assertThat(data.getLines(2).getSymbols()).isEqualTo("4,5,1;0,2,2"); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java deleted file mode 100644 index b23b40aa541..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/index/SourcePersisterTest.java +++ /dev/null @@ -1,428 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.batch.index; - -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.io.FileUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.sonar.api.batch.fs.InputPath; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.sensor.duplication.DuplicationGroup; -import org.sonar.api.batch.sensor.highlighting.TypeOfText; -import org.sonar.api.database.model.Snapshot; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Project; -import org.sonar.api.utils.DateUtils; -import org.sonar.api.utils.System2; -import org.sonar.batch.ProjectTree; -import org.sonar.batch.duplication.DuplicationCache; -import org.sonar.batch.highlighting.SyntaxHighlightingData; -import org.sonar.batch.highlighting.SyntaxHighlightingDataBuilder; -import org.sonar.batch.scan.filesystem.InputPathCache; -import org.sonar.batch.scan.measure.MeasureCache; -import org.sonar.batch.source.CodeColorizers; -import org.sonar.batch.symbol.DefaultSymbolTableBuilder; -import org.sonar.core.persistence.AbstractDaoTestCase; -import org.sonar.core.source.SnapshotDataTypes; -import org.sonar.core.source.db.FileSourceDao; -import org.sonar.core.source.db.FileSourceDto; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class SourcePersisterTest extends AbstractDaoTestCase { - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - private SourcePersister sourcePersister; - private InputPathCache inputPathCache; - private ResourceCache resourceCache; - private ProjectTree projectTree; - private System2 system2; - private MeasureCache measureCache; - private ComponentDataCache componentDataCache; - private DuplicationCache duplicationCache; - - private static final String PROJECT_KEY = "foo"; - - private java.io.File basedir; - - @Before - public void before() throws IOException { - Snapshot snapshot = new Snapshot(); - snapshot.setId(1000); - inputPathCache = mock(InputPathCache.class); - resourceCache = mock(ResourceCache.class); - projectTree = mock(ProjectTree.class); - system2 = mock(System2.class); - measureCache = mock(MeasureCache.class); - when(measureCache.byMetric(anyString(), anyString())).thenReturn(Collections.emptyList()); - componentDataCache = mock(ComponentDataCache.class); - duplicationCache = mock(DuplicationCache.class); - sourcePersister = new SourcePersister(inputPathCache, - getMyBatis(), measureCache, componentDataCache, projectTree, system2, - resourceCache, mock(CodeColorizers.class), duplicationCache); - Project project = new Project(PROJECT_KEY); - project.setUuid("projectUuid"); - when(projectTree.getRootProject()).thenReturn(project); - basedir = temp.newFolder(); - } - - @Test - public void testPersistUpdateWhenSrcHashIsMissing() throws Exception { - setupData("file_sources_missing_src_hash"); - Date now = DateUtils.parseDateTime("2014-10-29T16:44:02+0100"); - when(system2.newDate()).thenReturn(now); - - String relativePathSame = "src/same.java"; - java.io.File sameFile = new java.io.File(basedir, relativePathSame); - FileUtils.write(sameFile, "unchanged\ncontent"); - DefaultInputFile inputFileNew = new DefaultInputFile(PROJECT_KEY, relativePathSame) - .setLines(2) - .setAbsolutePath(sameFile.getAbsolutePath()) - .setHash("123456") - .setLineHashes(new byte[][] {md5("unchanged"), md5("content")}); - when(inputPathCache.all()).thenReturn(Arrays.asList(inputFileNew)); - - mockResourceCache(relativePathSame, PROJECT_KEY, "uuidsame"); - - sourcePersister.persist(); - FileSourceDto fileSourceDto = new FileSourceDao(getMyBatis()).select("uuidsame"); - assertThat(fileSourceDto.getCreatedAt()).isEqualTo(DateUtils.parseDateTime("2014-10-10T16:44:02+0200").getTime()); - assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now.getTime()); - assertThat(fileSourceDto.getSrcHash()).isEqualTo("123456"); - } - - @Test - public void testPersistDontTouchUnchanged() throws Exception { - setupData("file_sources"); - when(system2.newDate()).thenReturn(DateUtils.parseDateTime("2014-10-29T16:44:02+0100")); - - String relativePathSame = "src/same.java"; - java.io.File sameFile = new java.io.File(basedir, relativePathSame); - FileUtils.write(sameFile, "unchanged\ncontent"); - DefaultInputFile inputFileNew = new DefaultInputFile(PROJECT_KEY, relativePathSame).setLines(2) - .setAbsolutePath(sameFile.getAbsolutePath()) - .setHash("123456") - .setLineHashes(new byte[][] {md5("unchanged"), md5("ncontent")}); - when(inputPathCache.all()).thenReturn(Arrays.asList(inputFileNew)); - - mockResourceCache(relativePathSame, PROJECT_KEY, "uuidsame"); - - sourcePersister.persist(); - checkTables("testPersistDontTouchUnchanged", "file_sources"); - } - - @Test - public void testPersistUpdateChanged() throws Exception { - setupData("file_sources"); - Date now = DateUtils.parseDateTime("2014-10-29T16:44:02+0100"); - when(system2.newDate()).thenReturn(now); - - String relativePathSame = "src/changed.java"; - java.io.File sameFile = new java.io.File(basedir, relativePathSame); - FileUtils.write(sameFile, "changed\ncontent"); - DefaultInputFile inputFileNew = new DefaultInputFile(PROJECT_KEY, relativePathSame).setLines(2) - .setAbsolutePath(sameFile.getAbsolutePath()) - .setHash("123456") - .setLineHashes(new byte[][] {md5("changed"), md5("content")}); - when(inputPathCache.all()).thenReturn(Arrays.asList(inputFileNew)); - - mockResourceCache(relativePathSame, PROJECT_KEY, "uuidsame"); - - sourcePersister.persist(); - - FileSourceDto fileSourceDto = new FileSourceDao(getMyBatis()).select("uuidsame"); - assertThat(fileSourceDto.getCreatedAt()).isEqualTo(DateUtils.parseDateTime("2014-10-10T16:44:02+0200").getTime()); - assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now.getTime()); - assertThat(fileSourceDto.getData()).isEqualTo( - ",,,,,,,,,,,,,,,changed\r\n,,,,,,,,,,,,,,,content\r\n"); - assertThat(fileSourceDto.getLineHashes()).isEqualTo(md5Hex("changed") + "\n" + md5Hex("content")); - assertThat(fileSourceDto.getDataHash()).isEqualTo("d1a4dd62422639f665a8d80b37c59f8d"); - assertThat(fileSourceDto.getSrcHash()).isEqualTo("123456"); - } - - @Test - public void testPersistEmptyFile() throws Exception { - setupData("file_sources"); - when(system2.newDate()).thenReturn(DateUtils.parseDateTime("2014-10-29T16:44:02+0100")); - - String relativePathEmpty = "src/empty.java"; - DefaultInputFile inputFileEmpty = new DefaultInputFile(PROJECT_KEY, relativePathEmpty) - .setLines(0) - .setHash("abcd") - .setLineHashes(new byte[][] {}); - when(inputPathCache.all()).thenReturn(Arrays.asList(inputFileEmpty)); - - mockResourceCache(relativePathEmpty, PROJECT_KEY, "uuidempty"); - - sourcePersister.persist(); - checkTables("testPersistEmptyFile", "file_sources"); - } - - @Test - public void testPersistNewFileNoScmNoHighlighting() throws Exception { - setupData("file_sources"); - Date now = DateUtils.parseDateTime("2014-10-29T16:44:02+0100"); - when(system2.newDate()).thenReturn(now); - - String relativePathNew = "src/new.java"; - java.io.File newFile = new java.io.File(basedir, relativePathNew); - FileUtils.write(newFile, "foo\nbar\nbiz"); - DefaultInputFile inputFileNew = new DefaultInputFile(PROJECT_KEY, relativePathNew) - .setLines(3) - .setAbsolutePath(newFile.getAbsolutePath()) - .setLineHashes(new byte[][] {md5("foo"), md5("bar"), md5("biz")}); - when(inputPathCache.all()).thenReturn(Arrays.asList(inputFileNew)); - - mockResourceCache(relativePathNew, PROJECT_KEY, "uuidnew"); - - sourcePersister.persist(); - FileSourceDto fileSourceDto = new FileSourceDao(getMyBatis()).select("uuidnew"); - assertThat(fileSourceDto.getCreatedAt()).isEqualTo(now.getTime()); - assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now.getTime()); - assertThat(fileSourceDto.getData()).isEqualTo( - ",,,,,,,,,,,,,,,foo\r\n,,,,,,,,,,,,,,,bar\r\n,,,,,,,,,,,,,,,biz\r\n"); - assertThat(fileSourceDto.getLineHashes()).isEqualTo(md5Hex("foo") + "\n" + md5Hex("bar") + "\n" + md5Hex("biz")); - assertThat(fileSourceDto.getDataHash()).isEqualTo("a34ed99cc7d27150c82f5cba2b22b665"); - - } - - @Test - public void testPersistNewFileWithScmAndCoverageAndHighlighting() throws Exception { - setupData("file_sources"); - Date now = DateUtils.parseDateTime("2014-10-29T16:44:02+0100"); - when(system2.newDate()).thenReturn(now); - - String relativePathNew = "src/new.java"; - java.io.File newFile = new java.io.File(basedir, relativePathNew); - FileUtils.write(newFile, "foo\nbar\nbiz"); - DefaultInputFile inputFileNew = new DefaultInputFile(PROJECT_KEY, relativePathNew) - .setLines(3) - .setAbsolutePath(newFile.getAbsolutePath()) - .setOriginalLineOffsets(new long[] {0, 4, 7}) - .setLineHashes(new byte[][] {md5("foo"), md5("bar"), md5("biz")}); - when(inputPathCache.all()).thenReturn(Arrays.asList(inputFileNew)); - - mockResourceCache(relativePathNew, PROJECT_KEY, "uuidnew"); - - String fileKey = PROJECT_KEY + ":" + relativePathNew; - when(measureCache.byMetric(fileKey, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.SCM_AUTHORS_BY_LINE, "1=julien;2=simon;3=julien"))); - when(measureCache.byMetric(fileKey, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE, "1=2014-10-11T16:44:02+0100;2=2014-10-12T16:44:02+0100;3=2014-10-13T16:44:02+0100"))); - when(measureCache.byMetric(fileKey, CoreMetrics.SCM_REVISIONS_BY_LINE_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.SCM_REVISIONS_BY_LINE, "1=123;2=234;3=345"))); - when(measureCache.byMetric(fileKey, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.COVERAGE_LINE_HITS_DATA, "1=1;3=0"))); - when(measureCache.byMetric(fileKey, CoreMetrics.CONDITIONS_BY_LINE_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.CONDITIONS_BY_LINE, "1=4"))); - when(measureCache.byMetric(fileKey, CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.COVERED_CONDITIONS_BY_LINE, "1=2"))); - when(measureCache.byMetric(fileKey, CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.IT_COVERAGE_LINE_HITS_DATA, "1=2;3=0"))); - when(measureCache.byMetric(fileKey, CoreMetrics.IT_CONDITIONS_BY_LINE_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.IT_CONDITIONS_BY_LINE, "1=5"))); - when(measureCache.byMetric(fileKey, CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE, "1=3"))); - when(measureCache.byMetric(fileKey, CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA, "1=3;3=0"))); - when(measureCache.byMetric(fileKey, CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.OVERALL_CONDITIONS_BY_LINE, "1=6"))); - when(measureCache.byMetric(fileKey, CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY)) - .thenReturn(Arrays.asList(new Measure(CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE, "1=4"))); - - SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() - .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION) - .registerHighlightingRule(4, 5, TypeOfText.COMMENT) - .registerHighlightingRule(7, 16, TypeOfText.CONSTANT) - .build(); - when(componentDataCache.getData(fileKey, SnapshotDataTypes.SYNTAX_HIGHLIGHTING)) - .thenReturn(highlighting); - - DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(fileKey, null); - org.sonar.api.batch.sensor.symbol.Symbol s1 = symbolBuilder.newSymbol(1, 2); - symbolBuilder.newReference(s1, 4); - symbolBuilder.newReference(s1, 11); - org.sonar.api.batch.sensor.symbol.Symbol s2 = symbolBuilder.newSymbol(4, 6); - symbolBuilder.newReference(s2, 0); - symbolBuilder.newReference(s2, 7); - when(componentDataCache.getData(fileKey, SnapshotDataTypes.SYMBOL_HIGHLIGHTING)) - .thenReturn(symbolBuilder.build()); - - DuplicationGroup group1 = new DuplicationGroup(new DuplicationGroup.Block(fileKey, 1, 1)) - .addDuplicate(new DuplicationGroup.Block(fileKey, 3, 1)) - .addDuplicate(new DuplicationGroup.Block("anotherFile1", 12, 1)) - .addDuplicate(new DuplicationGroup.Block("anotherFile2", 13, 1)); - - DuplicationGroup group2 = new DuplicationGroup(new DuplicationGroup.Block(fileKey, 1, 2)) - .addDuplicate(new DuplicationGroup.Block("anotherFile1", 12, 2)) - .addDuplicate(new DuplicationGroup.Block("anotherFile2", 13, 2)); - when(duplicationCache.byComponent(fileKey)).thenReturn(Arrays.asList(group1, group2)); - - sourcePersister.persist(); - - FileSourceDto fileSourceDto = new FileSourceDao(getMyBatis()).select("uuidnew"); - assertThat(fileSourceDto.getCreatedAt()).isEqualTo(now.getTime()); - assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now.getTime()); - assertThat(fileSourceDto.getLineHashes()).isEqualTo(md5Hex("foo") + "\n" + md5Hex("bar") + "\n" + md5Hex("biz")); - assertThat(fileSourceDto.getData()).isEqualTo( - "123,julien,2014-10-11T16:44:02+0100,1,4,2,2,5,3,3,6,4,\"0,3,a\",\"1,2,1;0,2,2\",\"1,3\",foo\r\n" - + "234,simon,2014-10-12T16:44:02+0100,,,,,,,,,,\"0,1,cd\",\"0,1,1;0,2,2\",3,bar\r\n" - + "345,julien,2014-10-13T16:44:02+0100,0,,,0,,,0,,,\"0,9,c\",\"4,5,1;0,2,2\",2,biz\r\n"); - assertThat(fileSourceDto.getDataHash()).isEqualTo("26930cf0250d525b04083185ff24a046"); - } - - @Test - public void testSimpleConversionOfHighlightingOffset() { - DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java") - .setLines(3) - .setOriginalLineOffsets(new long[] {0, 4, 7}); - - SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() - .registerHighlightingRule(0, 4, TypeOfText.ANNOTATION) - .registerHighlightingRule(4, 5, TypeOfText.COMMENT) - .registerHighlightingRule(7, 16, TypeOfText.CONSTANT) - .build(); - - String[] highlightingPerLine = sourcePersister.computeHighlightingPerLine(file, highlighting); - - assertThat(highlightingPerLine).containsOnly("0,4,a", "0,1,cd", "0,9,c"); - } - - @Test - public void testConversionOfHighlightingOffsetMultiLine() { - DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java") - .setLines(3) - .setOriginalLineOffsets(new long[] {0, 4, 7}); - - SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() - .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION) - .registerHighlightingRule(4, 9, TypeOfText.COMMENT) - .registerHighlightingRule(10, 16, TypeOfText.CONSTANT) - .build(); - - String[] highlightingPerLine = sourcePersister.computeHighlightingPerLine(file, highlighting); - - assertThat(highlightingPerLine).containsOnly("0,3,a", "0,3,cd", "0,2,cd;3,9,c"); - } - - @Test - public void testConversionOfHighlightingNestedRules() { - DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java") - .setLines(3) - .setOriginalLineOffsets(new long[] {0, 4, 7}); - - SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() - .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION) - .registerHighlightingRule(4, 6, TypeOfText.COMMENT) - .registerHighlightingRule(7, 16, TypeOfText.CONSTANT) - .registerHighlightingRule(8, 15, TypeOfText.KEYWORD) - .build(); - - String[] highlightingPerLine = sourcePersister.computeHighlightingPerLine(file, highlighting); - - assertThat(highlightingPerLine).containsOnly("0,3,a", "0,2,cd", "0,9,c;1,8,k"); - } - - @Test - public void testConversionOfHighlightingNestedRulesMultiLine() { - DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java") - .setLines(3) - .setOriginalLineOffsets(new long[] {0, 4, 7}); - - SyntaxHighlightingData highlighting = new SyntaxHighlightingDataBuilder() - .registerHighlightingRule(0, 3, TypeOfText.ANNOTATION) - .registerHighlightingRule(4, 6, TypeOfText.COMMENT) - .registerHighlightingRule(4, 16, TypeOfText.CONSTANT) - .registerHighlightingRule(8, 15, TypeOfText.KEYWORD) - .build(); - - String[] highlightingPerLine = sourcePersister.computeHighlightingPerLine(file, highlighting); - - assertThat(highlightingPerLine).containsOnly("0,3,a", "0,3,c;0,2,cd", "0,9,c;1,8,k"); - } - - @Test - public void testSimpleConversionOfSymbolOffset() { - DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java") - .setLines(3) - .setOriginalLineOffsets(new long[] {0, 4, 7}); - - DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(PROJECT_KEY + ":" + "src/foo.java", null); - org.sonar.api.batch.sensor.symbol.Symbol s1 = symbolBuilder.newSymbol(1, 2); - symbolBuilder.newReference(s1, 4); - symbolBuilder.newReference(s1, 11); - org.sonar.api.batch.sensor.symbol.Symbol s2 = symbolBuilder.newSymbol(4, 6); - symbolBuilder.newReference(s2, 0); - symbolBuilder.newReference(s2, 7); - - String[] symbolsPerLine = sourcePersister.computeSymbolReferencesPerLine(file, symbolBuilder.build()); - - assertThat(symbolsPerLine).containsOnly("1,2,1;0,2,2", "0,1,1;0,2,2", "4,5,1;0,2,2"); - } - - @Test - public void verifyDeclarationOrderOfSymbolHasNoImpact() { - DefaultInputFile file = new DefaultInputFile(PROJECT_KEY, "src/foo.java") - .setLines(3) - .setOriginalLineOffsets(new long[] {0, 4, 7}); - - DefaultSymbolTableBuilder symbolBuilder = new DefaultSymbolTableBuilder(PROJECT_KEY + ":" + "src/foo.java", null); - org.sonar.api.batch.sensor.symbol.Symbol s2 = symbolBuilder.newSymbol(4, 6); - symbolBuilder.newReference(s2, 7); - symbolBuilder.newReference(s2, 0); - org.sonar.api.batch.sensor.symbol.Symbol s1 = symbolBuilder.newSymbol(1, 2); - symbolBuilder.newReference(s1, 11); - symbolBuilder.newReference(s1, 4); - - String[] symbolsPerLine = sourcePersister.computeSymbolReferencesPerLine(file, symbolBuilder.build()); - - assertThat(symbolsPerLine).containsOnly("1,2,1;0,2,2", "0,1,1;0,2,2", "4,5,1;0,2,2"); - } - - private void mockResourceCache(String relativePathEmpty, String projectKey, String uuid) { - File sonarFile = File.create(relativePathEmpty); - sonarFile.setUuid(uuid); - when(resourceCache.get(projectKey + ":" + relativePathEmpty)).thenReturn(new BatchResource(1, sonarFile, null)); - } - - private byte[] md5(String string) { - return DigestUtils.md5(string); - } - - private String md5Hex(String string) { - return DigestUtils.md5Hex(string); - } - -} diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldUpdateMeasure-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldUpdateMeasure-result.xml deleted file mode 100644 index 07f08fb086c..00000000000 --- a/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldUpdateMeasure-result.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources.xml deleted file mode 100644 index 61fa8fc265a..00000000000 --- a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources_missing_src_hash.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources_missing_src_hash.xml index 4a26ff98e6a..2fc32f5845b 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources_missing_src_hash.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/file_sources_missing_src_hash.xml @@ -1,6 +1,6 @@ + + + diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistDontTouchUnchanged-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistDontTouchUnchanged-result.xml index 940080dc041..0942d062ede 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistDontTouchUnchanged-result.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/index/SourcePersisterTest/testPersistDontTouchUnchanged-result.xml @@ -1,7 +1,7 @@ - - - diff --git a/sonar-core/pom.xml b/sonar-core/pom.xml index b1d6b8696e9..3682bc1fb64 100644 --- a/sonar-core/pom.xml +++ b/sonar-core/pom.xml @@ -51,6 +51,12 @@ org.codehaus.sonar sonar-update-center-common + + org.codehaus.sonar + sonar-batch-protocol + ${project.version} + + org.codehaus.sonar sonar-home @@ -183,6 +189,24 @@ + + org.codehaus.mojo + build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + src/main/gen-java + + + + + org.apache.maven.plugins maven-jar-plugin diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java index b2d44f173e4..80de635be56 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java @@ -33,7 +33,7 @@ import java.util.List; */ public class DatabaseVersion implements BatchComponent, ServerComponent { - public static final int LAST_VERSION = 780; + public static final int LAST_VERSION = 783; /** * List of all the tables.n diff --git a/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceDao.java b/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceDao.java index c0a75f7a412..eced8499a4d 100644 --- a/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceDao.java +++ b/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceDao.java @@ -31,6 +31,7 @@ import org.sonar.core.persistence.MyBatis; import javax.annotation.CheckForNull; +import java.io.InputStream; import java.io.Reader; import java.sql.Connection; import java.sql.PreparedStatement; @@ -56,17 +57,48 @@ public class FileSourceDao implements BatchComponent, ServerComponent, DaoCompon } } - public void readDataStream(String fileUuid, Function function) { + public void readDataStream(String fileUuid, Function function) { DbSession dbSession = mybatis.openSession(false); + Connection connection = dbSession.getConnection(); + PreparedStatement pstmt = null; + ResultSet rs = null; + InputStream input = null; try { - readColumnStream(dbSession, fileUuid, function, "data"); + pstmt = connection.prepareStatement("SELECT binary_data FROM file_sources WHERE file_uuid=?"); + pstmt.setString(1, fileUuid); + rs = pstmt.executeQuery(); + if (rs.next()) { + input = rs.getBinaryStream(1); + function.apply(input); + } + } catch (SQLException e) { + throw new IllegalStateException("Fail to read FILE_SOURCES.BINARY_DATA of file " + fileUuid, e); } finally { + IOUtils.closeQuietly(input); + DbUtils.closeQuietly(connection, pstmt, rs); MyBatis.closeQuietly(dbSession); } } public void readLineHashesStream(DbSession dbSession, String fileUuid, Function function) { - readColumnStream(dbSession, fileUuid, function, "line_hashes"); + Connection connection = dbSession.getConnection(); + PreparedStatement pstmt = null; + ResultSet rs = null; + Reader reader = null; + try { + pstmt = connection.prepareStatement("SELECT line_hashes FROM file_sources WHERE file_uuid=?"); + pstmt.setString(1, fileUuid); + rs = pstmt.executeQuery(); + if (rs.next()) { + reader = rs.getCharacterStream(1); + function.apply(reader); + } + } catch (SQLException e) { + throw new IllegalStateException("Fail to read FILE_SOURCES.LINE_HASHES of file " + fileUuid, e); + } finally { + IOUtils.closeQuietly(reader); + DbUtils.closeQuietly(connection, pstmt, rs); + } } public void insert(FileSourceDto dto) { @@ -89,24 +121,4 @@ public class FileSourceDao implements BatchComponent, ServerComponent, DaoCompon } } - private void readColumnStream(DbSession dbSession, String fileUuid, Function function, String column) { - Connection connection = dbSession.getConnection(); - PreparedStatement pstmt = null; - ResultSet rs = null; - Reader reader = null; - try { - pstmt = connection.prepareStatement("SELECT " + column + " FROM file_sources WHERE file_uuid = ?"); - pstmt.setString(1, fileUuid); - rs = pstmt.executeQuery(); - if (rs.next()) { - reader = rs.getCharacterStream(1); - function.apply(reader); - } - } catch (SQLException e) { - throw new IllegalStateException("Fail to read FILE_SOURCES." + column.toUpperCase() + " of file " + fileUuid, e); - } finally { - IOUtils.closeQuietly(reader); - DbUtils.closeQuietly(connection, pstmt, rs); - } - } } diff --git a/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceDto.java b/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceDto.java index 999ee5f2dd5..7840d213df6 100644 --- a/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceDto.java +++ b/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceDto.java @@ -19,36 +19,30 @@ */ package org.sonar.core.source.db; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; +import org.apache.commons.io.IOUtils; +import org.sonar.server.source.db.FileSourceDb; + import javax.annotation.CheckForNull; import javax.annotation.Nullable; -public class FileSourceDto { +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; - public static final int CSV_INDEX_SCM_REVISION = 0; - public static final int CSV_INDEX_SCM_AUTHOR = 1; - public static final int CSV_INDEX_SCM_DATE = 2; - public static final int CSV_INDEX_UT_LINE_HITS = 3; - public static final int CSV_INDEX_UT_CONDITIONS = 4; - public static final int CSV_INDEX_UT_COVERED_CONDITIONS = 5; - public static final int CSV_INDEX_IT_LINE_HITS = 6; - public static final int CSV_INDEX_IT_CONDITIONS = 7; - public static final int CSV_INDEX_IT_COVERED_CONDITIONS = 8; - public static final int CSV_INDEX_OVERALL_LINE_HITS = 9; - public static final int CSV_INDEX_OVERALL_CONDITIONS = 10; - public static final int CSV_INDEX_OVERALL_COVERED_CONDITIONS = 11; - public static final int CSV_INDEX_HIGHLIGHTING = 12; - public static final int CSV_INDEX_SYMBOLS = 13; - public static final int CSV_INDEX_DUPLICATIONS = 14; +public class FileSourceDto { private Long id; private String projectUuid; private String fileUuid; private long createdAt; private long updatedAt; - private String data; private String lineHashes; - private String dataHash; private String srcHash; + private byte[] binaryData; + private String dataHash; public Long getId() { return id; @@ -77,32 +71,91 @@ public class FileSourceDto { return this; } - @CheckForNull - public String getData() { - return data; + public String getDataHash() { + return dataHash; } - public FileSourceDto setData(@Nullable String data) { - this.data = data; + /** + * MD5 of column BINARY_DATA. Used to know to detect data changes and need for update. + */ + public FileSourceDto setDataHash(String s) { + this.dataHash = s; return this; } - @CheckForNull - public String getLineHashes() { - return lineHashes; + /** + * Compressed value of serialized protobuf message {@link org.sonar.server.source.db.FileSourceDb.Data} + */ + public byte[] getBinaryData() { + return binaryData; } - public FileSourceDto setLineHashes(@Nullable String lineHashes) { - this.lineHashes = lineHashes; + /** + * Compressed value of serialized protobuf message {@link org.sonar.server.source.db.FileSourceDb.Data} + */ + public FileSourceDb.Data getData() { + return decodeData(binaryData); + } + + public static FileSourceDb.Data decodeData(byte[] binaryData) { + // stream is always closed + return decodeData(new ByteArrayInputStream(binaryData)); + } + + /** + * Decompress and deserialize content of column FILE_SOURCES.BINARY_DATA. + * The parameter "input" is always closed by this method. + */ + public static FileSourceDb.Data decodeData(InputStream binaryInput) { + LZ4BlockInputStream lz4Input = null; + try { + lz4Input = new LZ4BlockInputStream(binaryInput); + return FileSourceDb.Data.parseFrom(lz4Input); + } catch (IOException e) { + throw new IllegalStateException("Fail to decompress and deserialize source data", e); + } finally { + IOUtils.closeQuietly(lz4Input); + } + } + + /** + * Set compressed value of the protobuf message {@link org.sonar.server.source.db.FileSourceDb.Data} + */ + public FileSourceDto setBinaryData(byte[] data) { + this.binaryData = data; return this; } - public String getDataHash() { - return dataHash; + public FileSourceDto setData(FileSourceDb.Data data) { + this.binaryData = encodeData(data); + return this; + } + + /** + * Serialize and compress protobuf message {@link org.sonar.server.source.db.FileSourceDb.Data} + * in the column BINARY_DATA. + */ + public static byte[] encodeData(FileSourceDb.Data data) { + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + LZ4BlockOutputStream compressedOutput = new LZ4BlockOutputStream(byteOutput); + try { + data.writeTo(compressedOutput); + compressedOutput.close(); + return byteOutput.toByteArray(); + } catch (IOException e) { + throw new IllegalStateException("Fail to serialize and compress source data", e); + } finally { + IOUtils.closeQuietly(compressedOutput); + } } - public FileSourceDto setDataHash(String dataHash) { - this.dataHash = dataHash; + @CheckForNull + public String getLineHashes() { + return lineHashes; + } + + public FileSourceDto setLineHashes(@Nullable String lineHashes) { + this.lineHashes = lineHashes; return this; } @@ -110,6 +163,9 @@ public class FileSourceDto { return srcHash; } + /** + * Hash of file content. Value is computed by batch. + */ public FileSourceDto setSrcHash(String srcHash) { this.srcHash = srcHash; return this; diff --git a/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceMapper.java b/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceMapper.java index 2fd054897eb..e8876892357 100644 --- a/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/source/db/FileSourceMapper.java @@ -26,7 +26,7 @@ import java.util.List; public interface FileSourceMapper { - List selectAllFileDataHashByProject(String projectUuid); + List selectHashesForProject(String projectUuid); @CheckForNull FileSourceDto select(String fileUuid); diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql index 92e9f0aff0c..fd2663d75e5 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql @@ -308,6 +308,9 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('777'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('778'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('779'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('780'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('781'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('782'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('783'); INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482', null, null); ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl index 202f896611e..fadf4581aa9 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl @@ -554,8 +554,8 @@ CREATE TABLE "FILE_SOURCES" ( "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "PROJECT_UUID" VARCHAR(50) NOT NULL, "FILE_UUID" VARCHAR(50) NOT NULL, - "DATA" CLOB(2147483647), "LINE_HASHES" CLOB(2147483647), + "BINARY_DATA" BINARY(167772150), "DATA_HASH" VARCHAR(50) NOT NULL, "SRC_HASH" VARCHAR(50) NULL, "CREATED_AT" BIGINT NOT NULL, diff --git a/sonar-core/src/main/resources/org/sonar/core/source/db/FileSourceMapper.xml b/sonar-core/src/main/resources/org/sonar/core/source/db/FileSourceMapper.xml index 009f36d1451..2edae7f055c 100644 --- a/sonar-core/src/main/resources/org/sonar/core/source/db/FileSourceMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/source/db/FileSourceMapper.xml @@ -5,29 +5,32 @@ - SELECT id, file_uuid as fileUuid, data_hash as dataHash, src_hash as srcHash FROM file_sources WHERE project_uuid = #{projectUuid} - insert into file_sources (project_uuid, file_uuid, created_at, updated_at, data, line_hashes, data_hash, src_hash) - values (#{projectUuid,jdbcType=VARCHAR}, #{fileUuid,jdbcType=VARCHAR}, #{createdAt,jdbcType=BIGINT}, #{updatedAt,jdbcType=BIGINT}, #{data,jdbcType=CLOB}, #{lineHashes,jdbcType=CLOB}, #{dataHash,jdbcType=VARCHAR}, #{srcHash,jdbcType=VARCHAR}) + insert into file_sources (project_uuid, file_uuid, created_at, updated_at, binary_data, line_hashes, data_hash, src_hash) + values (#{projectUuid,jdbcType=VARCHAR}, #{fileUuid,jdbcType=VARCHAR}, #{createdAt,jdbcType=BIGINT}, + #{updatedAt,jdbcType=BIGINT}, #{binaryData,jdbcType=BLOB}, #{lineHashes,jdbcType=CLOB}, + #{dataHash,jdbcType=VARCHAR}, #{srcHash,jdbcType=VARCHAR}) update file_sources set - updated_at = #{updatedAt}, - data = #{data}, - line_hashes = #{lineHashes}, - data_hash = #{dataHash}, - src_hash = #{srcHash} + updated_at = #{updatedAt,jdbcType=BIGINT}, + binary_data = #{binaryData,jdbcType=BLOB}, + line_hashes = #{lineHashes,jdbcType=CLOB}, + data_hash = #{dataHash,jdbcType=VARCHAR}, + src_hash = #{srcHash,jdbcType=VARCHAR} where id = #{id} diff --git a/sonar-core/src/test/java/org/sonar/core/source/db/FileSourceDaoTest.java b/sonar-core/src/test/java/org/sonar/core/source/db/FileSourceDaoTest.java index dc8dc262db2..aed0f04fd71 100644 --- a/sonar-core/src/test/java/org/sonar/core/source/db/FileSourceDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/source/db/FileSourceDaoTest.java @@ -25,13 +25,12 @@ import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.sonar.api.utils.DateUtils; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.persistence.DbSession; import java.io.IOException; +import java.io.InputStream; import java.io.Reader; -import java.util.Date; import static org.assertj.core.api.Assertions.assertThat; @@ -56,77 +55,82 @@ public class FileSourceDaoTest extends AbstractDaoTestCase { public void select() throws Exception { setupData("shared"); - FileSourceDto fileSourceDto = dao.select("ab12"); + FileSourceDto fileSourceDto = dao.select("FILE1_UUID"); - assertThat(fileSourceDto.getData()).isEqualTo("aef12a,alice,2014-04-25T12:34:56+0100,,class Foo"); + assertThat(fileSourceDto.getBinaryData()).isNotEmpty(); assertThat(fileSourceDto.getDataHash()).isEqualTo("hash"); - assertThat(fileSourceDto.getProjectUuid()).isEqualTo("abcd"); - assertThat(fileSourceDto.getFileUuid()).isEqualTo("ab12"); - assertThat(new Date(fileSourceDto.getCreatedAt())).isEqualTo(DateUtils.parseDateTime("2014-10-29T16:44:02+0100")); - assertThat(new Date(fileSourceDto.getUpdatedAt())).isEqualTo(DateUtils.parseDateTime("2014-10-30T16:44:02+0100")); + assertThat(fileSourceDto.getProjectUuid()).isEqualTo("PRJ_UUID"); + assertThat(fileSourceDto.getFileUuid()).isEqualTo("FILE1_UUID"); + assertThat(fileSourceDto.getCreatedAt()).isEqualTo(1500000000000L); + assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(1500000000000L); } @Test public void select_data() throws Exception { setupData("shared"); - StringParser stringParser = new StringParser(); - dao.readDataStream("ab12", stringParser); + InputStreamToStringFunction fn = new InputStreamToStringFunction(); + dao.readDataStream("FILE1_UUID", fn); - assertThat(stringParser.getResult()).isEqualTo("aef12a,alice,2014-04-25T12:34:56+0100,,class Foo"); + assertThat(fn.result).isNotEmpty(); } @Test public void select_line_hashes() throws Exception { setupData("shared"); - StringParser stringParser = new StringParser(); - dao.readLineHashesStream(session, "ab12", stringParser); + ReaderToStringFunction fn = new ReaderToStringFunction(); + dao.readLineHashesStream(session, "FILE1_UUID", fn); - assertThat(stringParser.getResult()).isEqualTo("truc"); + assertThat(fn.result).isEqualTo("ABC\\nDEF\\nGHI"); } @Test public void no_line_hashes_on_unknown_file() throws Exception { setupData("shared"); - StringParser stringParser = new StringParser(); - dao.readLineHashesStream(session, "unknown", stringParser); + ReaderToStringFunction fn = new ReaderToStringFunction(); + dao.readLineHashesStream(session, "unknown", fn); - assertThat(stringParser.getResult()).isEmpty(); + assertThat(fn.result).isNull(); } @Test public void insert() throws Exception { setupData("shared"); - dao.insert(new FileSourceDto().setProjectUuid("prj").setFileUuid("file").setData("bla bla") - .setDataHash("hash2") - .setLineHashes("foo\nbar") - .setSrcHash("hache") - .setCreatedAt(DateUtils.parseDateTime("2014-10-31T16:44:02+0100").getTime()) - .setUpdatedAt(DateUtils.parseDateTime("2014-10-31T16:44:02+0100").getTime())); - - checkTable("insert", "file_sources"); + dao.insert(new FileSourceDto() + .setProjectUuid("PRJ_UUID") + .setFileUuid("FILE2_UUID") + .setBinaryData("FILE2_BINARY_DATA".getBytes()) + .setDataHash("FILE2_DATA_HASH") + .setLineHashes("LINE1_HASH\\nLINE2_HASH") + .setSrcHash("FILE2_HASH") + .setCreatedAt(1500000000000L) + .setUpdatedAt(1500000000001L)); + + checkTable("insert", "file_sources", "project_uuid", "file_uuid", "data_hash", "line_hashes", "src_hash", "created_at", "updated_at"); } @Test public void update() throws Exception { setupData("shared"); - dao.update(new FileSourceDto().setId(101L).setProjectUuid("prj").setFileUuid("file") - .setData("updated data") - .setDataHash("hash2") - .setSrcHash("123456") - .setLineHashes("foo2\nbar2") - .setUpdatedAt(DateUtils.parseDateTime("2014-10-31T16:44:02+0100").getTime())); + dao.update(new FileSourceDto().setId(101L) + .setProjectUuid("PRJ_UUID") + .setFileUuid("FILE1_UUID") + .setBinaryData("updated data".getBytes()) + .setDataHash("NEW_DATA_HASH") + .setSrcHash("NEW_FILE_HASH") + .setLineHashes("NEW_LINE_HASHES") + .setUpdatedAt(1500000000002L)); - checkTable("update", "file_sources"); + checkTable("update", "file_sources", "project_uuid", "file_uuid", "data_hash", "line_hashes", "src_hash", "created_at", "updated_at"); } - class StringParser implements Function { + private static class ReaderToStringFunction implements Function { - String result = ""; + String result = null; @Override public String apply(Reader input) { @@ -137,9 +141,20 @@ public class FileSourceDaoTest extends AbstractDaoTestCase { throw new RuntimeException(e); } } + } + + private static class InputStreamToStringFunction implements Function { - public String getResult() { - return result; + String result = null; + + @Override + public String apply(InputStream input) { + try { + result = IOUtils.toString(input); + return IOUtils.toString(input); + } catch (IOException e) { + throw new RuntimeException(e); + } } } } diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources-result.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources-result.xml index 13b1c270dc4..7caaa526513 100644 --- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources-result.xml @@ -1,5 +1,5 @@ - diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources.xml index fa5e4a7828d..aaae9915d98 100644 --- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources.xml +++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources.xml @@ -72,8 +72,8 @@ depth="[null]" scope="PRJ" qualifier="TRK" created_at="1228222680000" build_date="1228222680000" version="[null]" path="[null]"/> - - diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/select_purgeable_file_uuids.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/select_purgeable_file_uuids.xml index cfc39014c9d..5bbb5aa5891 100644 --- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/select_purgeable_file_uuids.xml +++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/select_purgeable_file_uuids.xml @@ -79,8 +79,8 @@ depth="[null]" scope="PRJ" qualifier="TRK" created_at="1228222680000" build_date="1228222680000" version="[null]" path="[null]"/> - - diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml index 1f164a2692e..d50cc3926f5 100644 --- a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml +++ b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml @@ -103,6 +103,6 @@ depth="[null]" scope="FIL" qualifier="FIL" created_at="1228222680000" build_date="1228222680000" version="[null]" path="[null]"/> - diff --git a/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/insert-result.xml b/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/insert-result.xml index f43f9f06564..74bca5ec788 100644 --- a/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/insert-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/insert-result.xml @@ -1,16 +1,17 @@ - + - + diff --git a/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/shared.xml b/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/shared.xml index fbfa94a6ddd..79a340f841d 100644 --- a/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/shared.xml +++ b/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/shared.xml @@ -1,9 +1,9 @@ - + diff --git a/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/update-result.xml b/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/update-result.xml index 64ff997152f..40cbfa91a43 100644 --- a/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/update-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/source/db/FileSourceDaoTest/update-result.xml @@ -1,10 +1,11 @@ - + -- cgit v1.2.3 From 53829c96bba989fb21f4888cd0162be946c7a01a Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Sat, 7 Feb 2015 11:23:20 +0100 Subject: SONAR-5960 remove CSV from db USERS.SCM_ACCOUNTS --- .../v51/CopyScmAccountsFromAuthorsToUsers.java | 49 ++++++++++------------ .../java/org/sonar/server/user/UserUpdater.java | 26 ++---------- .../java/org/sonar/server/user/db/UserDao.java | 3 +- .../server/user/index/UserResultSetIterator.java | 40 +----------------- .../v51/CopyScmAccountsFromAuthorsToUsersTest.java | 5 ++- .../sonar/server/user/UserServiceMediumTest.java | 2 +- .../org/sonar/server/user/UserUpdaterTest.java | 20 ++++----- .../java/org/sonar/server/user/db/UserDaoTest.java | 2 +- .../fail_to_create_user_if_already_exists.xml | 2 +- ...ciate_default_groups_when_reactivating_user.xml | 2 +- ...associate_default_groups_when_updating_user.xml | 2 +- ...reate_user_when_scm_account_is_already_used.xml | 2 +- ...en_scm_account_is_already_used_by_many_user.xml | 2 +- .../fail_to_reactivate_user_if_not_disabled.xml | 2 +- ...pdate_user_when_scm_account_is_already_used.xml | 4 +- ...roup_when_updating_user_if_already_existing.xml | 2 +- .../user/UserUpdaterTest/reactivate_user.xml | 2 +- .../server/user/UserUpdaterTest/update_user.xml | 2 +- .../server/user/db/UserDaoTest/select_by_login.xml | 2 +- .../UserDaoTest/select_nullable_by_scm_account.xml | 2 +- ...sults_when_same_email_is_used_by_many_users.xml | 2 +- .../server/user/index/UserIndexerTest/index.xml | 2 +- .../index/UserResultSetIteratorTest/shared.xml | 4 +- .../src/main/java/org/sonar/core/user/UserDto.java | 41 +++++++++++++++++- .../main/java/org/sonar/core/user/UserMapper.java | 2 +- .../resources/org/sonar/core/user/UserMapper.xml | 17 ++------ .../test/java/org/sonar/core/user/UserDtoTest.java | 45 ++++++++++++++++++++ 27 files changed, 149 insertions(+), 137 deletions(-) create mode 100644 sonar-core/src/test/java/org/sonar/core/user/UserDtoTest.java (limited to 'sonar-core') diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsers.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsers.java index b5ed489b350..e6ebec997f6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsers.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsers.java @@ -20,11 +20,10 @@ package org.sonar.server.db.migrations.v51; -import com.google.common.base.CharMatcher; +import com.google.common.base.Joiner; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import org.sonar.api.utils.System2; -import org.sonar.api.utils.text.CsvWriter; import org.sonar.core.persistence.Database; import org.sonar.server.db.migrations.BaseDataChange; import org.sonar.server.db.migrations.Select; @@ -32,7 +31,8 @@ import org.sonar.server.db.migrations.Upsert; import org.sonar.server.db.migrations.UpsertImpl; import org.sonar.server.util.ProgressLogger; -import java.io.StringWriter; +import javax.annotation.CheckForNull; + import java.sql.SQLException; import java.util.Collection; import java.util.List; @@ -42,6 +42,8 @@ import static com.google.common.collect.Lists.newArrayList; public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange { + private static final char SCM_ACCOUNTS_SEPARATOR = '\n'; + private final System2 system; private final AtomicLong counter = new AtomicLong(0L); @@ -60,13 +62,13 @@ public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange { final Multimap authorsByPersonId = ArrayListMultimap.create(); context.prepareSelect("SELECT a.person_id, a.login FROM authors a," + " (SELECT person_id, COUNT(*) AS nb FROM authors GROUP BY person_id HAVING COUNT(*) > 1) group_by_person" + - " WHERE a.person_id = group_by_person.person_id " - ).scroll(new Select.RowHandler() { - @Override - public void handle(Select.Row row) throws SQLException { - authorsByPersonId.put(row.getLong(1), row.getString(2)); - } - }); + " WHERE a.person_id = group_by_person.person_id " + ).scroll(new Select.RowHandler() { + @Override + public void handle(Select.Row row) throws SQLException { + authorsByPersonId.put(row.getLong(1), row.getString(2)); + } + }); Upsert update = context.prepareUpsert("UPDATE users SET scm_accounts = ?, updated_at = ? WHERE id = ?"); for (Long personId : authorsByPersonId.keySet()) { @@ -82,7 +84,7 @@ public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange { } if (!authors.isEmpty()) { update - .setString(1, convertScmAccountsToCsv(authors)) + .setString(1, encodeScmAccounts(authors)) .setLong(2, now) .setLong(3, user.id) .addBatch(); @@ -130,32 +132,25 @@ public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange { return users; } - private static String convertScmAccountsToCsv(List scmAccounts) { - List list = newArrayList(scmAccounts); - // Add one empty character at the begin and at the end of the list to be able to generate a coma at the begin and at the end of the - // text - list.add(0, ""); - list.add(""); - int size = list.size(); - StringWriter writer = new StringWriter(size); - CsvWriter csv = CsvWriter.of(writer); - csv.values(list.toArray(new String[size])); - csv.close(); - // Remove useless line break added by CsvWriter at this end of the text - return CharMatcher.JAVA_ISO_CONTROL.removeFrom(writer.toString()); + @CheckForNull + private static String encodeScmAccounts(List scmAccounts) { + if (scmAccounts.isEmpty()) { + return null; + } + return SCM_ACCOUNTS_SEPARATOR + Joiner.on(SCM_ACCOUNTS_SEPARATOR).join(scmAccounts) + SCM_ACCOUNTS_SEPARATOR; } private static class User { Long id; String login; String email; - String scmAcounts; + String scmAccounts; - User(Long id, String login, String email, String scmAcounts) { + User(Long id, String login, String email, String scmAccounts) { this.id = id; this.login = login; this.email = email; - this.scmAcounts = scmAcounts; + this.scmAccounts = scmAccounts; } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java index d5ab0f666a0..e90ec514fa3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java @@ -20,7 +20,6 @@ package org.sonar.server.user; -import com.google.common.base.CharMatcher; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.base.Strings; @@ -32,7 +31,6 @@ import org.sonar.api.ServerComponent; import org.sonar.api.config.Settings; import org.sonar.api.platform.NewUserHandler; import org.sonar.api.utils.System2; -import org.sonar.api.utils.text.CsvWriter; import org.sonar.core.persistence.DbSession; import org.sonar.core.user.GroupDto; import org.sonar.core.user.UserDto; @@ -46,14 +44,12 @@ import org.sonar.server.util.Validation; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.io.StringWriter; import java.security.SecureRandom; import java.util.Arrays; import java.util.List; import java.util.Random; import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Sets.newLinkedHashSet; public class UserUpdater implements ServerComponent { @@ -151,7 +147,7 @@ public class UserUpdater implements ServerComponent { List scmAccounts = sanitizeScmAccounts(newUser.scmAccounts()); if (scmAccounts != null && !scmAccounts.isEmpty()) { validateScmAccounts(dbSession, scmAccounts, login, email, null, messages); - userDto.setScmAccounts(convertScmAccountsToCsv(scmAccounts)); + userDto.setScmAccounts(scmAccounts); } if (!messages.isEmpty()) { @@ -186,9 +182,9 @@ public class UserUpdater implements ServerComponent { List scmAccounts = sanitizeScmAccounts(updateUser.scmAccounts()); if (scmAccounts != null && !scmAccounts.isEmpty()) { validateScmAccounts(dbSession, scmAccounts, userDto.getLogin(), email != null ? email : userDto.getEmail(), userDto, messages); - userDto.setScmAccounts(convertScmAccountsToCsv(scmAccounts)); + userDto.setScmAccounts(scmAccounts); } else { - userDto.setScmAccounts(null); + userDto.setScmAccounts((String) null); } } @@ -288,22 +284,6 @@ public class UserUpdater implements ServerComponent { userDto.setCryptedPassword(DigestUtils.sha1Hex("--" + saltHex + "--" + password + "--")); } - private static String convertScmAccountsToCsv(List scmAccounts) { - // Use a set to remove duplication, then use a list to add empty characters - List uniqueScmAccounts = newArrayList(newLinkedHashSet(scmAccounts)); - // Add one empty character at the begin and at the end of the list to be able to generate a coma at the begin and at the end of the - // text - uniqueScmAccounts.add(0, ""); - uniqueScmAccounts.add(""); - int size = uniqueScmAccounts.size(); - StringWriter writer = new StringWriter(size); - CsvWriter csv = CsvWriter.of(writer); - csv.values(uniqueScmAccounts.toArray(new String[size])); - csv.close(); - // Remove useless line break added by CsvWriter at this end of the text - return CharMatcher.JAVA_ISO_CONTROL.removeFrom(writer.toString()); - } - private void notifyNewUser(String login, String name, String email) { newUserNotifier.onNewUser(NewUserHandler.Context.builder() .setLogin(login) diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/db/UserDao.java b/server/sonar-server/src/main/java/org/sonar/server/user/db/UserDao.java index ba3db9a9c7c..3b813e0ddd0 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/db/UserDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/db/UserDao.java @@ -52,7 +52,8 @@ public class UserDao extends org.sonar.core.user.UserDao implements DaoComponent } public List selectNullableByScmAccountOrLoginOrEmail(DbSession session, String scmAccountOrLoginOrEmail) { - return mapper(session).selectNullableByScmAccountOrLoginOrEmail(scmAccountOrLoginOrEmail); + String like = new StringBuilder().append("%").append(UserDto.SCM_ACCOUNTS_SEPARATOR).append(scmAccountOrLoginOrEmail).append(UserDto.SCM_ACCOUNTS_SEPARATOR).append("%").toString(); + return mapper(session).selectNullableByScmAccountOrLoginOrEmail(scmAccountOrLoginOrEmail, like); } protected UserMapper mapper(DbSession session) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserResultSetIterator.java index 6934ae53ba5..15e038159a9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/index/UserResultSetIterator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/index/UserResultSetIterator.java @@ -19,27 +19,16 @@ */ package org.sonar.server.user.index; -import com.google.common.base.Strings; import com.google.common.collect.Maps; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVRecord; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; +import org.sonar.core.user.UserDto; import org.sonar.server.db.DbClient; import org.sonar.server.db.ResultSetIterator; -import javax.annotation.Nullable; - -import java.io.IOException; -import java.io.StringReader; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.List; - -import static com.google.common.collect.Lists.newArrayList; /** * Scrolls over table USERS and reads documents to populate the user index @@ -89,35 +78,10 @@ class UserResultSetIterator extends ResultSetIterator { doc.setName(rs.getString(2)); doc.setEmail(rs.getString(3)); doc.setActive(rs.getBoolean(4)); - doc.setScmAccounts(getScmAccounts(rs.getString(5), login)); + doc.setScmAccounts(UserDto.decodeScmAccounts(rs.getString(5))); doc.setCreatedAt(rs.getLong(6)); doc.setUpdatedAt(rs.getLong(7)); return doc; } - private List getScmAccounts(@Nullable String csv, String login) { - List result = newArrayList(); - if (csv == null) { - return result; - } - CSVParser csvParser = null; - StringReader reader = null; - try { - reader = new StringReader(csv); - csvParser = new CSVParser(reader, CSVFormat.DEFAULT); - for (CSVRecord csvRecord : csvParser) { - for (String aCsvRecord : csvRecord) { - if (!Strings.isNullOrEmpty(aCsvRecord)) { - result.add(aCsvRecord); - } - } - } - return result; - } catch (IOException e) { - throw new IllegalStateException(String.format("Fail to read scm accounts for user '%s'", login), e); - } finally { - IOUtils.closeQuietly(reader); - IOUtils.closeQuietly(csvParser); - } - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsersTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsersTest.java index 251a261795b..603f1cad8ef 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsersTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/CopyScmAccountsFromAuthorsToUsersTest.java @@ -25,6 +25,7 @@ import org.junit.ClassRule; import org.junit.Test; import org.sonar.api.utils.System2; import org.sonar.core.persistence.DbTester; +import org.sonar.core.user.UserDto; import org.sonar.server.db.migrations.DatabaseMigration; import java.util.Map; @@ -58,11 +59,11 @@ public class CopyScmAccountsFromAuthorsToUsersTest { migration.execute(); User simon = getUserByLogin("simon"); - assertThat(simon.scmAccounts).isEqualTo(",Simon B,simon@codehaus.org,"); + assertThat(simon.scmAccounts).isEqualTo(UserDto.SCM_ACCOUNTS_SEPARATOR + "Simon B" + UserDto.SCM_ACCOUNTS_SEPARATOR + "simon@codehaus.org" + UserDto.SCM_ACCOUNTS_SEPARATOR); assertThat(simon.updatedAt).isEqualTo(updatedDate); User fabrice = getUserByLogin("fabrice"); - assertThat(fabrice.scmAccounts).isEqualTo(",fab,"); + assertThat(fabrice.scmAccounts).isEqualTo(UserDto.SCM_ACCOUNTS_SEPARATOR + "fab" + UserDto.SCM_ACCOUNTS_SEPARATOR); assertThat(fabrice.updatedAt).isEqualTo(updatedDate); assertThat(getUserByLogin("julien").updatedAt).isEqualTo(oldDate); diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/UserServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/UserServiceMediumTest.java index f33f226b454..22f9e98fb2f 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/UserServiceMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/UserServiceMediumTest.java @@ -89,7 +89,7 @@ public class UserServiceMediumTest { assertThat(userDto.getEmail()).isEqualTo("user@mail.com"); assertThat(userDto.getCryptedPassword()).isNotNull(); assertThat(userDto.getSalt()).isNotNull(); - assertThat(userDto.getScmAccounts()).contains(",u1,u_1,"); + assertThat(userDto.getScmAccountsAsList()).containsOnly("u1", "u_1"); assertThat(userDto.getCreatedAt()).isNotNull(); assertThat(userDto.getUpdatedAt()).isNotNull(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java index 3121e0133c6..15590048f24 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java @@ -116,7 +116,7 @@ public class UserUpdaterTest { assertThat(dto.getLogin()).isEqualTo("user"); assertThat(dto.getName()).isEqualTo("User"); assertThat(dto.getEmail()).isEqualTo("user@mail.com"); - assertThat(dto.getScmAccounts()).isEqualTo(",u1,u_1,User 1,"); + assertThat(dto.getScmAccountsAsList()).containsOnly("u1", "u_1", "User 1"); assertThat(dto.isActive()).isTrue(); assertThat(dto.getSalt()).isNotNull(); @@ -158,7 +158,7 @@ public class UserUpdaterTest { .setPasswordConfirmation("password") .setScmAccounts(newArrayList("u1", "", null))); - assertThat(userDao.selectNullableByLogin(session, "user").getScmAccounts()).isEqualTo(",u1,"); + assertThat(userDao.selectNullableByLogin(session, "user").getScmAccountsAsList()).containsOnly("u1"); } @Test @@ -188,7 +188,7 @@ public class UserUpdaterTest { .setPasswordConfirmation("password") .setScmAccounts(newArrayList("u1", "u1"))); - assertThat(userDao.selectNullableByLogin(session, "user").getScmAccounts()).isEqualTo(",u1,"); + assertThat(userDao.selectNullableByLogin(session, "user").getScmAccountsAsList()).containsOnly("u1"); } @Test @@ -561,7 +561,7 @@ public class UserUpdaterTest { assertThat(dto.isActive()).isTrue(); assertThat(dto.getName()).isEqualTo("Marius2"); assertThat(dto.getEmail()).isEqualTo("marius2@mail.com"); - assertThat(dto.getScmAccounts()).isEqualTo(",ma2,"); + assertThat(dto.getScmAccountsAsList()).containsOnly("ma2"); assertThat(dto.getSalt()).isNotEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365"); assertThat(dto.getCryptedPassword()).isNotEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"); @@ -584,7 +584,7 @@ public class UserUpdaterTest { session.clearCache(); UserDto dto = userDao.selectNullableByLogin(session, "marius"); - assertThat(dto.getScmAccounts()).isEqualTo(",ma2,"); + assertThat(dto.getScmAccountsAsList()).containsOnly("ma2"); } @Test @@ -602,7 +602,7 @@ public class UserUpdaterTest { // Following fields has not changed assertThat(dto.getEmail()).isEqualTo("marius@lesbronzes.fr"); - assertThat(dto.getScmAccounts()).isEqualTo(",ma,marius33,"); + assertThat(dto.getScmAccountsAsList()).containsOnly("ma", "marius33"); assertThat(dto.getSalt()).isEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365"); assertThat(dto.getCryptedPassword()).isEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"); } @@ -622,7 +622,7 @@ public class UserUpdaterTest { // Following fields has not changed assertThat(dto.getName()).isEqualTo("Marius"); - assertThat(dto.getScmAccounts()).isEqualTo(",ma,marius33,"); + assertThat(dto.getScmAccountsAsList()).containsOnly("ma", "marius33"); assertThat(dto.getSalt()).isEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365"); assertThat(dto.getCryptedPassword()).isEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"); } @@ -638,7 +638,7 @@ public class UserUpdaterTest { session.clearCache(); UserDto dto = userDao.selectNullableByLogin(session, "marius"); - assertThat(dto.getScmAccounts()).isEqualTo(",ma2,"); + assertThat(dto.getScmAccountsAsList()).containsOnly("ma2"); // Following fields has not changed assertThat(dto.getName()).isEqualTo("Marius"); @@ -658,7 +658,7 @@ public class UserUpdaterTest { session.clearCache(); UserDto dto = userDao.selectNullableByLogin(session, "marius"); - assertThat(dto.getScmAccounts()).isEqualTo(",ma,marius33,"); + assertThat(dto.getScmAccountsAsList()).containsOnly("ma", "marius33"); } @Test @@ -692,7 +692,7 @@ public class UserUpdaterTest { // Following fields has not changed assertThat(dto.getName()).isEqualTo("Marius"); - assertThat(dto.getScmAccounts()).isEqualTo(",ma,marius33,"); + assertThat(dto.getScmAccountsAsList()).containsOnly("ma", "marius33"); assertThat(dto.getEmail()).isEqualTo("marius@lesbronzes.fr"); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/db/UserDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/db/UserDaoTest.java index 68384726366..8773aceaa01 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/db/UserDaoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/db/UserDaoTest.java @@ -66,7 +66,7 @@ public class UserDaoTest { assertThat(dto.getName()).isEqualTo("Marius"); assertThat(dto.getEmail()).isEqualTo("marius@lesbronzes.fr"); assertThat(dto.isActive()).isTrue(); - assertThat(dto.getScmAccounts()).isEqualTo(",ma,marius33,"); + assertThat(dto.getScmAccountsAsList()).containsOnly("ma","marius33"); assertThat(dto.getSalt()).isEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365"); assertThat(dto.getCryptedPassword()).isEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"); assertThat(dto.getCreatedAt()).isEqualTo(1418215735482L); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserCreatorTest/fail_to_create_user_if_already_exists.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserCreatorTest/fail_to_create_user_if_already_exists.xml index 3c22b1ebf61..926b75324ad 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserCreatorTest/fail_to_create_user_if_already_exists.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserCreatorTest/fail_to_create_user_if_already_exists.xml @@ -1,6 +1,6 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_reactivating_user.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_reactivating_user.xml index 15ee29c168a..d0acdb2a121 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_reactivating_user.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_reactivating_user.xml @@ -1,6 +1,6 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_updating_user.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_updating_user.xml index fee1ef143c5..617fe0f8389 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_updating_user.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_updating_user.xml @@ -1,6 +1,6 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used.xml index 5a257afb338..33dac1a0693 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used.xml @@ -1,6 +1,6 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used_by_many_user.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used_by_many_user.xml index 4bcf30b7e61..1f32fad9c4d 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used_by_many_user.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used_by_many_user.xml @@ -1,6 +1,6 @@ - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_update_user_when_scm_account_is_already_used.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_update_user_when_scm_account_is_already_used.xml index d535e5bf01d..6c8d4d3a854 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_update_user_when_scm_account_is_already_used.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_update_user_when_scm_account_is_already_used.xml @@ -1,8 +1,8 @@ - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/not_associate_default_group_when_updating_user_if_already_existing.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/not_associate_default_group_when_updating_user_if_already_existing.xml index 8bbd3daa996..8daf3302314 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/not_associate_default_group_when_updating_user_if_already_existing.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/not_associate_default_group_when_updating_user_if_already_existing.xml @@ -1,6 +1,6 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/reactivate_user.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/reactivate_user.xml index 24fd96bdfc7..eef35eda879 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/reactivate_user.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/reactivate_user.xml @@ -1,6 +1,6 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_user.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_user.xml index 87839ca2fe5..926b75324ad 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_user.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_user.xml @@ -1,6 +1,6 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/db/UserDaoTest/select_by_login.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/db/UserDaoTest/select_by_login.xml index a1879e0b470..5a54ec22958 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/db/UserDaoTest/select_by_login.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/db/UserDaoTest/select_by_login.xml @@ -1,6 +1,6 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/db/UserDaoTest/select_nullable_by_scm_account.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/db/UserDaoTest/select_nullable_by_scm_account.xml index 854325a4e7d..1a5f244b365 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/db/UserDaoTest/select_nullable_by_scm_account.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/db/UserDaoTest/select_nullable_by_scm_account.xml @@ -1,6 +1,6 @@ - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/index/UserResultSetIteratorTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/index/UserResultSetIteratorTest/shared.xml index 0163794b9dc..077fc76c2ee 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/index/UserResultSetIteratorTest/shared.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/index/UserResultSetIteratorTest/shared.xml @@ -1,14 +1,14 @@ diff --git a/sonar-core/src/main/java/org/sonar/core/user/UserDto.java b/sonar-core/src/main/java/org/sonar/core/user/UserDto.java index 3f62cf2fdbe..11cfc59cb08 100644 --- a/sonar-core/src/main/java/org/sonar/core/user/UserDto.java +++ b/sonar-core/src/main/java/org/sonar/core/user/UserDto.java @@ -19,13 +19,22 @@ */ package org.sonar.core.user; +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import org.apache.commons.lang.StringUtils; + import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + /** * @since 3.2 */ public class UserDto { + public static final char SCM_ACCOUNTS_SEPARATOR = '\n'; + private Long id; private String login; private String name; @@ -88,11 +97,39 @@ public class UserDto { return scmAccounts; } - public UserDto setScmAccounts(@Nullable String scmAccounts) { - this.scmAccounts = scmAccounts; + public List getScmAccountsAsList() { + return decodeScmAccounts(scmAccounts); + } + + /** + * List of SCM accounts separated by '|' + */ + public UserDto setScmAccounts(@Nullable String s) { + this.scmAccounts = s; return this; } + public UserDto setScmAccounts(@Nullable List list) { + this.scmAccounts = encodeScmAccounts(list); + return this; + } + + @CheckForNull + public static String encodeScmAccounts(@Nullable List scmAccounts) { + if (scmAccounts != null && !scmAccounts.isEmpty()) { + return String.format("%s%s%s", SCM_ACCOUNTS_SEPARATOR, StringUtils.join(scmAccounts, SCM_ACCOUNTS_SEPARATOR), SCM_ACCOUNTS_SEPARATOR); + } + return null; + } + + public static List decodeScmAccounts(@Nullable String dbValue) { + if (dbValue == null) { + return new ArrayList<>(); + } else { + return Lists.newArrayList(Splitter.on(SCM_ACCOUNTS_SEPARATOR).omitEmptyStrings().split(dbValue)); + } + } + public String getCryptedPassword() { return cryptedPassword; } diff --git a/sonar-core/src/main/java/org/sonar/core/user/UserMapper.java b/sonar-core/src/main/java/org/sonar/core/user/UserMapper.java index 80c44052ea9..6658f10dc6a 100644 --- a/sonar-core/src/main/java/org/sonar/core/user/UserMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/user/UserMapper.java @@ -36,7 +36,7 @@ public interface UserMapper { * Can return multiple results if an email is used by many users (For instance, technical account can use the same email as a none technical account) */ @CheckForNull - List selectNullableByScmAccountOrLoginOrEmail(String scmAccountOrLoginOrEmail); + List selectNullableByScmAccountOrLoginOrEmail(@Param("scmAccount") String scmAccountOrLoginOrEmail, @Param("likeScmAccount") String likeScmAccount); @CheckForNull UserDto selectUser(long userId); diff --git a/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml b/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml index 42a16e13139..aab09322e5d 100644 --- a/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/user/UserMapper.xml @@ -23,25 +23,14 @@ WHERE u.login=#{login} - SELECT FROM users u WHERE u.login=#{scmAccount} OR u.email=#{scmAccount} - OR - - - u.scm_accounts LIKE '%,' + #{scmAccount} + ',%' - - - u.scm_accounts LIKE concat('%,', #{scmAccount}, ',%') - - - u.scm_accounts LIKE '%,' || #{scmAccount} || ',%' - - + OR u.scm_accounts like #{likeScmAccount} - select current_timestamp - - - - update semaphores - set updated_at = current_timestamp, locked_at = current_timestamp + set updated_at = #{now}, locked_at = #{now} where name=#{name} AND updated_at < #{updatedBefore} @@ -34,9 +26,9 @@ from semaphores s where s.name=#{name} - + update semaphores - set updated_at = current_timestamp + set updated_at = #{now} where name=#{name} diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql index fd2663d75e5..60192f301df 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql @@ -311,6 +311,9 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('780'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('781'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('782'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('783'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('784'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('785'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('786'); INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482', null, null); ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl index fadf4581aa9..81d7d1d1779 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl @@ -403,9 +403,9 @@ CREATE TABLE "SEMAPHORES" ( "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "NAME" VARCHAR(4000), "CHECKSUM" VARCHAR(200), - "CREATED_AT" TIMESTAMP, - "UPDATED_AT" TIMESTAMP, - "LOCKED_AT" TIMESTAMP + "CREATED_AT" BIGINT, + "UPDATED_AT" BIGINT, + "LOCKED_AT" BIGINT ); CREATE TABLE "MEASURE_FILTERS" ( diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java b/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java index a2652f1aaa1..5710a80c2a7 100644 --- a/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java @@ -19,15 +19,14 @@ */ package org.sonar.core.persistence; -import org.apache.commons.lang.time.DateUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.utils.Semaphores; +import org.sonar.api.utils.System2; -import java.util.Date; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.atomic.AtomicInteger; @@ -37,10 +36,12 @@ import static org.assertj.core.api.Assertions.assertThat; public class SemaphoreDaoTest extends AbstractDaoTestCase { private SemaphoreDao dao; + private System2 system; @Before public void before() { - dao = new SemaphoreDao(getMyBatis()); + system = System2.INSTANCE; + dao = new SemaphoreDao(getMyBatis(), system); } @Rule @@ -51,7 +52,7 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Semaphore name must not be empty"); - SemaphoreDao dao = new SemaphoreDao(getMyBatis()); + SemaphoreDao dao = new SemaphoreDao(getMyBatis(), system); dao.acquire(null, 5000); } @@ -60,7 +61,7 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Semaphore max age must be positive: -5000"); - SemaphoreDao dao = new SemaphoreDao(getMyBatis()); + SemaphoreDao dao = new SemaphoreDao(getMyBatis(), system); dao.acquire("foo", -5000); } @@ -69,7 +70,7 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Semaphore name must not be empty"); - SemaphoreDao dao = new SemaphoreDao(getMyBatis()); + SemaphoreDao dao = new SemaphoreDao(getMyBatis(), system); dao.release(null); } @@ -97,14 +98,14 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { assertThat(lock.getDurationSinceLocked()).isNull(); SemaphoreDto semaphore = selectSemaphore("foo"); - assertThat(semaphore.getCreatedAt().getTime()).isEqualTo(semaphore.getUpdatedAt().getTime()); + assertThat(semaphore.getCreatedAt()).isEqualTo(semaphore.getUpdatedAt()); Thread.sleep(1000); dao.update(lock); semaphore = selectSemaphore("foo"); - assertThat(semaphore.getCreatedAt().getTime()).isLessThan(semaphore.getUpdatedAt().getTime()); + assertThat(semaphore.getCreatedAt()).isLessThan(semaphore.getUpdatedAt()); dao.release("foo"); assertThat(selectSemaphore("foo")).isNull(); @@ -247,19 +248,11 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { } } - private boolean isRecent(Date date) { - Date future = DateUtils.addMinutes(now(), 1); - Date past = DateUtils.addDays(now(), -1); - return date.after(past) && date.before(future); - } - - private Date now() { - SqlSession session = getMyBatis().openSession(); - try { - return dao.now(session); - } finally { - MyBatis.closeQuietly(session); - } + private boolean isRecent(Long date) { + int oneMinuteInMs = 60 * 1000; + long future = system.now() + oneMinuteInMs; + long past = system.now() - oneMinuteInMs; + return date > past && date < future; } private static class Runner extends Thread { diff --git a/sonar-core/src/test/resources/org/sonar/core/persistence/SemaphoreDaoTest/old_semaphore.xml b/sonar-core/src/test/resources/org/sonar/core/persistence/SemaphoreDaoTest/old_semaphore.xml index 903ea7916c7..23a1d4ff67b 100644 --- a/sonar-core/src/test/resources/org/sonar/core/persistence/SemaphoreDaoTest/old_semaphore.xml +++ b/sonar-core/src/test/resources/org/sonar/core/persistence/SemaphoreDaoTest/old_semaphore.xml @@ -1,3 +1,3 @@ - - \ No newline at end of file + + -- cgit v1.2.3 From 148fc31b4ca59239d7d2f69a16990f45757501cf Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Tue, 10 Feb 2015 18:59:17 +0100 Subject: issues - fix contracts --- .../java/org/sonar/batch/ProjectConfigurator.java | 3 +-- .../components/PastSnapshotFinderByDays.java | 7 ++----- .../org/sonar/core/issue/db/BatchIssueDto.java | 13 +++---------- .../sonar/core/persistence/SemaphoreDaoTest.java | 22 +++++++++++++++++++--- 4 files changed, 25 insertions(+), 20 deletions(-) (limited to 'sonar-core') diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProjectConfigurator.java b/sonar-batch/src/main/java/org/sonar/batch/ProjectConfigurator.java index 21dd83e51ab..fb55fdde0a4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/ProjectConfigurator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/ProjectConfigurator.java @@ -39,7 +39,6 @@ import javax.annotation.Nullable; import java.util.Date; import static org.sonar.api.utils.DateUtils.formatDateTime; -import static org.sonar.api.utils.DateUtils.longToDate; /** * Used by views !! @@ -104,7 +103,7 @@ public class ProjectConfigurator implements BatchComponent { throw new IllegalArgumentException( "'sonar.projectDate' property cannot be older than the date of the last known quality snapshot on this project. Value: '" + settings.getString(CoreProperties.PROJECT_DATE_PROPERTY) + "'. " + - "Latest quality snapshot: '" + formatDateTime(longToDate(lastSnapshot.getCreatedAtMs())) + "Latest quality snapshot: '" + formatDateTime(lastSnapshot.getCreatedAt()) + "'. This property may only be used to rebuild the past in a chronological order."); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/deprecated/components/PastSnapshotFinderByDays.java b/sonar-batch/src/main/java/org/sonar/batch/deprecated/components/PastSnapshotFinderByDays.java index 5183236cc05..21ab5d15e13 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/deprecated/components/PastSnapshotFinderByDays.java +++ b/sonar-batch/src/main/java/org/sonar/batch/deprecated/components/PastSnapshotFinderByDays.java @@ -28,12 +28,9 @@ import org.sonar.api.resources.Qualifiers; import org.sonar.batch.components.PastSnapshot; import javax.annotation.CheckForNull; - import java.util.Date; import java.util.List; -import static org.sonar.api.utils.DateUtils.longToDate; - public class PastSnapshotFinderByDays implements BatchExtension { private DatabaseSession session; @@ -43,7 +40,7 @@ public class PastSnapshotFinderByDays implements BatchExtension { } public PastSnapshot findFromDays(Snapshot projectSnapshot, int days) { - Date targetDate = DateUtils.addDays(longToDate(projectSnapshot.getCreatedAtMs()), -days); + Date targetDate = DateUtils.addDays(projectSnapshot.getCreatedAt(), -days); String hql = "from " + Snapshot.class.getSimpleName() + " where resourceId=:resourceId AND status=:status AND createdAt<:date AND qualifier<>:lib order by createdAt asc"; List snapshots = session.createQuery(hql) .setParameter("date", projectSnapshot.getCreatedAtMs()) @@ -67,7 +64,7 @@ public class PastSnapshotFinderByDays implements BatchExtension { long bestDistance = Long.MAX_VALUE; Snapshot nearest = null; for (Snapshot snapshot : snapshots) { - long distance = distance(longToDate(snapshot.getCreatedAtMs()), targetDate); + long distance = distance(snapshot.getCreatedAt(), targetDate); if (distance <= bestDistance) { bestDistance = distance; nearest = snapshot; diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java b/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java index 85ee2e47bcb..bd8dd1760c7 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java @@ -20,14 +20,8 @@ package org.sonar.core.issue.db; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; - import java.util.Date; -import static org.sonar.api.utils.DateUtils.dateToLong; -import static org.sonar.api.utils.DateUtils.longToDate; - public class BatchIssueDto { private String kee; @@ -152,13 +146,12 @@ public class BatchIssueDto { return this; } - @CheckForNull public Date getCreationDate() { - return longToDate(creationDate); + return new Date(creationDate); } - public BatchIssueDto setCreationDate(@Nullable Date creationDate) { - this.creationDate = dateToLong(creationDate); + public BatchIssueDto setCreationDate(Date creationDate) { + this.creationDate = creationDate.getTime(); return this; } diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java b/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java index 5710a80c2a7..6d9c6d74eb1 100644 --- a/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/persistence/SemaphoreDaoTest.java @@ -35,6 +35,8 @@ import static org.assertj.core.api.Assertions.assertThat; public class SemaphoreDaoTest extends AbstractDaoTestCase { + @Rule + public ExpectedException thrown = ExpectedException.none(); private SemaphoreDao dao; private System2 system; @@ -44,8 +46,14 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { dao = new SemaphoreDao(getMyBatis(), system); } - @Rule - public ExpectedException thrown = ExpectedException.none(); + @Test + public void should_fail_to_acquire_if_null_semaphore_name() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Semaphore name must not be empty"); + + SemaphoreDao dao = new SemaphoreDao(getMyBatis(), system); + dao.acquire(null, 5000); + } @Test public void should_fail_to_acquire_if_blank_semaphore_name() { @@ -53,7 +61,7 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { thrown.expectMessage("Semaphore name must not be empty"); SemaphoreDao dao = new SemaphoreDao(getMyBatis(), system); - dao.acquire(null, 5000); + dao.acquire("", 5000); } @Test @@ -111,6 +119,14 @@ public class SemaphoreDaoTest extends AbstractDaoTestCase { assertThat(selectSemaphore("foo")).isNull(); } + @Test + public void fail_to_update_null_semaphore() throws Exception { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Semaphore must not be null"); + + dao.update(null); + } + @Test public void create_and_acquire_semaphore_when_maxage_is_zeo() throws Exception { Semaphores.Semaphore lock = dao.acquire("foo", 0); -- cgit v1.2.3 From 80e19825ce083c88f99ba1575d66ab721b557190 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Tue, 10 Feb 2015 21:47:02 +0100 Subject: Use BLOB instead of H2 BINARY columns --- .../src/main/resources/org/sonar/core/persistence/schema-h2.ddl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sonar-core') diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl index 81d7d1d1779..d1efca986f6 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl @@ -291,7 +291,7 @@ CREATE TABLE "ACTIVE_RULES" ( CREATE TABLE "NOTIFICATIONS" ( "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), - "DATA" BINARY(167772150) + "DATA" BLOB(167772150) ); CREATE TABLE "USER_ROLES" ( @@ -555,7 +555,7 @@ CREATE TABLE "FILE_SOURCES" ( "PROJECT_UUID" VARCHAR(50) NOT NULL, "FILE_UUID" VARCHAR(50) NOT NULL, "LINE_HASHES" CLOB(2147483647), - "BINARY_DATA" BINARY(167772150), + "BINARY_DATA" BLOB(167772150), "DATA_HASH" VARCHAR(50) NOT NULL, "SRC_HASH" VARCHAR(50) NULL, "CREATED_AT" BIGINT NOT NULL, -- cgit v1.2.3 From 9b6a8ecd0475b2f4035ce29c04387b7fff9970b6 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Wed, 11 Feb 2015 11:17:31 +0100 Subject: SONAR-6130 fix deadlock during purge --- .../src/main/java/org/sonar/core/purge/PurgeDao.java | 19 +++++++++++-------- .../test/java/org/sonar/core/purge/PurgeDaoTest.java | 3 ++- 2 files changed, 13 insertions(+), 9 deletions(-) (limited to 'sonar-core') diff --git a/sonar-core/src/main/java/org/sonar/core/purge/PurgeDao.java b/sonar-core/src/main/java/org/sonar/core/purge/PurgeDao.java index 2e9b914d605..7a9a796516e 100644 --- a/sonar-core/src/main/java/org/sonar/core/purge/PurgeDao.java +++ b/sonar-core/src/main/java/org/sonar/core/purge/PurgeDao.java @@ -32,10 +32,7 @@ import org.sonar.core.persistence.MyBatis; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; +import java.util.*; /** * @since 2.14 @@ -131,16 +128,22 @@ public class PurgeDao { } private void disableOrphanResources(final ResourceDto project, final SqlSession session, final PurgeMapper purgeMapper, final PurgeListener purgeListener) { + final List componentIdUuids = new ArrayList(); session.select("org.sonar.core.purge.PurgeMapper.selectResourceIdsToDisable", project.getId(), new ResultHandler() { @Override public void handleResult(ResultContext resultContext) { - IdUuidPair resourceIdUuid = (IdUuidPair) resultContext.getResultObject(); - if (resourceIdUuid.getId() != null) { - disableResource(resourceIdUuid, purgeMapper); - purgeListener.onComponentDisabling(resourceIdUuid.getUuid()); + IdUuidPair componentIdUuid = (IdUuidPair) resultContext.getResultObject(); + if (componentIdUuid.getId() != null) { + componentIdUuids.add(componentIdUuid); } } }); + + for (IdUuidPair componentIdUuid : componentIdUuids) { + disableResource(componentIdUuid, purgeMapper); + purgeListener.onComponentDisabling(componentIdUuid.getUuid()); + } + session.commit(); } diff --git a/sonar-core/src/test/java/org/sonar/core/purge/PurgeDaoTest.java b/sonar-core/src/test/java/org/sonar/core/purge/PurgeDaoTest.java index 421b5362283..0e2f7b0ba61 100644 --- a/sonar-core/src/test/java/org/sonar/core/purge/PurgeDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/purge/PurgeDaoTest.java @@ -58,6 +58,7 @@ public class PurgeDaoTest extends AbstractDaoTestCase { sut = new PurgeDao(getMyBatis(), new ResourceDao(getMyBatis(), system2), new PurgeProfiler(), system2); } + @After public void after() { MyBatis.closeQuietly(dbSession); @@ -95,7 +96,7 @@ public class PurgeDaoTest extends AbstractDaoTestCase { public void disable_resources_without_last_snapshot() { setupData("disable_resources_without_last_snapshot"); sut.purge(new PurgeConfiguration(1L, new String[0], 30, system2), PurgeListener.EMPTY); - checkTables("disable_resources_without_last_snapshot", new String[]{"issue_close_date", "issue_update_date"}, "projects", "snapshots", "issues"); + checkTables("disable_resources_without_last_snapshot", new String[] {"issue_close_date", "issue_update_date"}, "projects", "snapshots", "issues"); } @Test -- cgit v1.2.3 From 4d8fbfb4f1be2d2333d1cb051626a29c9442cc6e Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Wed, 11 Feb 2015 14:48:08 +0100 Subject: issues - simplify BatchIssueDto --- .../main/java/org/sonar/core/issue/db/BatchIssueDto.java | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'sonar-core') diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java b/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java index bd8dd1760c7..c6c2e6012b9 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java @@ -36,7 +36,7 @@ public class BatchIssueDto { private String componentKey; private String ruleKey; private String ruleRepo; - private Long creationDate; + private Long creationTime; public String getAssigneeLogin() { return assigneeLogin; @@ -147,20 +147,11 @@ public class BatchIssueDto { } public Date getCreationDate() { - return new Date(creationDate); + return new Date(creationTime); } public BatchIssueDto setCreationDate(Date creationDate) { - this.creationDate = creationDate.getTime(); - return this; - } - - public Long getCreationTime() { - return creationDate; - } - - public BatchIssueDto setCreationTime(Long time) { - this.creationDate = time; + this.creationTime = creationDate.getTime(); return this; } } -- cgit v1.2.3 From 6136a7a313d5253ee6443eff52d602bac022ad14 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Tue, 10 Feb 2015 15:23:30 +0100 Subject: SONAR-5183 timezones - project_measures and manual_measures migrations --- .../core/sensors/ManualMeasureDecorator.java | 7 +- .../server/db/migrations/DatabaseMigrations.java | 4 +- .../v51/FeedManualMeasuresLongDates.java | 67 ++++++++++++++++ .../v51/FeedProjectMeasuresLongDates.java | 65 ++++++++++++++++ .../v51/FeedManualMeasuresLongDatesTest.java | 88 +++++++++++++++++++++ .../v51/FeedProjectMeasuresLongDatesTest.java | 87 +++++++++++++++++++++ .../v51/FeedManualMeasuresLongDatesTest/before.xml | 28 +++++++ .../v51/FeedManualMeasuresLongDatesTest/schema.sql | 7 ++ .../FeedProjectMeasuresLongDatesTest/before.xml | 22 ++++++ .../FeedProjectMeasuresLongDatesTest/schema.sql | 5 ++ .../webapp/WEB-INF/app/models/manual_measure.rb | 2 +- .../db/migrate/787_add_measures_long_dates.rb | 30 +++++++ .../db/migrate/788_feed_measures_long_dates.rb | 30 +++++++ .../db/migrate/789_rename_measures_long_dates.rb | 34 ++++++++ .../sonar/core/persistence/DatabaseVersion.java | 2 +- .../java/org/sonar/jpa/entity/ManualMeasure.java | 9 +-- .../org/sonar/core/persistence/rows-h2.sql | 3 + .../org/sonar/core/persistence/schema-h2.ddl | 6 +- .../org/sonar/api/database/model/MeasureModel.java | 91 ++++++++++++---------- .../org/sonar/api/database/model/MeasureMapper.xml | 2 +- 20 files changed, 534 insertions(+), 55 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDates.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest/before.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest/schema.sql create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/before.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/schema.sql create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/787_add_measures_long_dates.rb create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/788_feed_measures_long_dates.rb create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/789_rename_measures_long_dates.rb (limited to 'sonar-core') diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualMeasureDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualMeasureDecorator.java index ce54b9bf4dc..3825b13348f 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualMeasureDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ManualMeasureDecorator.java @@ -32,6 +32,8 @@ import org.sonar.jpa.entity.ManualMeasure; import java.util.List; +import static com.google.common.base.Preconditions.checkState; + @Phase(name = Phase.Name.PRE) public class ManualMeasureDecorator implements Decorator { @@ -60,9 +62,8 @@ public class ManualMeasureDecorator implements Decorator { private Measure copy(ManualMeasure manualMeasure) { Metric metric = metricFinder.findById(manualMeasure.getMetricId()); - if (metric == null) { - throw new IllegalStateException("Unable to find manual metric with id: " + manualMeasure.getMetricId()); - } + checkState(metric != null, "Unable to find manual metric with id: " + manualMeasure.getMetricId()); + Measure measure = new Measure(metric); measure.setValue(manualMeasure.getValue(), 5); measure.setData(manualMeasure.getTextValue()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java index 813d7623906..dd8a452b74c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/DatabaseMigrations.java @@ -88,6 +88,8 @@ public interface DatabaseMigrations { FeedSnapshotsLongDates.class, FeedIssuesLongDates.class, FeedFileSourcesBinaryData.class, - FeedSemaphoresLongDates.class + FeedSemaphoresLongDates.class, + FeedProjectMeasuresLongDates.class, + FeedManualMeasuresLongDates.class ); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDates.java new file mode 100644 index 00000000000..78529fcfa6c --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDates.java @@ -0,0 +1,67 @@ +/* + * 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.db.migrations.v51; + +import org.sonar.api.utils.System2; +import org.sonar.core.persistence.Database; +import org.sonar.server.db.migrations.BaseDataChange; +import org.sonar.server.db.migrations.MassUpdate; +import org.sonar.server.db.migrations.Select; +import org.sonar.server.db.migrations.SqlStatement; + +import java.sql.SQLException; +import java.util.Date; + +public class FeedManualMeasuresLongDates extends BaseDataChange { + + private final System2 system2; + + public FeedManualMeasuresLongDates(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + public void execute(Context context) throws SQLException { + final long now = system2.now(); + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate + .select("SELECT m.created_at, m.updated_at, m.id FROM manual_measures m WHERE created_at_ms IS NULL"); + massUpdate + .update("UPDATE manual_measures SET created_at_ms=?, updated_at_ms=? WHERE id=?"); + massUpdate.rowPluralName("manualMeasures"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + for (int i = 1; i <= 2; i++) { + Date date = row.getDate(i); + update.setLong(i, date == null ? null : Math.min(now, date.getTime())); + } + + Long id = row.getLong(3); + update.setLong(3, id); + + return true; + } + }); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java new file mode 100644 index 00000000000..6cea58309dc --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDates.java @@ -0,0 +1,65 @@ +/* + * 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.db.migrations.v51; + +import org.sonar.api.utils.System2; +import org.sonar.core.persistence.Database; +import org.sonar.server.db.migrations.BaseDataChange; +import org.sonar.server.db.migrations.MassUpdate; +import org.sonar.server.db.migrations.Select; +import org.sonar.server.db.migrations.SqlStatement; + +import java.sql.SQLException; +import java.util.Date; + +public class FeedProjectMeasuresLongDates extends BaseDataChange { + + private final System2 system2; + + public FeedProjectMeasuresLongDates(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + public void execute(Context context) throws SQLException { + final long now = system2.now(); + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate + .select("SELECT m.measure_date, m.id FROM project_measures m WHERE measure_date_ms IS NULL"); + massUpdate + .update("UPDATE project_measures SET measure_date_ms=? WHERE id=?"); + massUpdate.rowPluralName("projectMeasures"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Date date = row.getDate(1); + update.setLong(1, date == null ? null : Math.min(now, date.getTime())); + + Long id = row.getLong(2); + update.setLong(2, id); + + return true; + } + }); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest.java new file mode 100644 index 00000000000..c3b2a47c45d --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest.java @@ -0,0 +1,88 @@ +/* + * 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.db.migrations.v51; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.db.migrations.DatabaseMigration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.api.utils.DateUtils.parseDate; + +public class FeedManualMeasuresLongDatesTest { + @ClassRule + public static DbTester db = new DbTester().schema(FeedManualMeasuresLongDatesTest.class, "schema.sql"); + + @Before + public void before() throws Exception { + db.prepareDbUnit(getClass(), "before.xml"); + } + + @Test + public void execute() throws Exception { + DatabaseMigration migration = newMigration(System2.INSTANCE); + + migration.execute(); + + int count = db + .countSql("select count(*) from manual_measures where " + + "created_at_ms is not null " + + "and updated_at_ms is not null"); + assertThat(count).isEqualTo(2); + } + + @Test + public void take_now_if_date_in_the_future() throws Exception { + System2 system = mock(System2.class); + when(system.now()).thenReturn(1234L); + + DatabaseMigration migration = newMigration(system); + + migration.execute(); + + int count = db + .countSql("select count(*) from manual_measures where " + + "created_at_ms = 1234"); + assertThat(count).isEqualTo(1); + } + + @Test + public void take_snapshot_date_if_in_the_past() throws Exception { + DatabaseMigration migration = newMigration(System2.INSTANCE); + + migration.execute(); + + long snapshotTime = parseDate("2014-09-25").getTime(); + int count = db + .countSql("select count(*) from manual_measures where " + + "created_at_ms=" + snapshotTime); + assertThat(count).isEqualTo(1); + } + + private DatabaseMigration newMigration(System2 system) { + return new FeedManualMeasuresLongDates(db.database(), system); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest.java new file mode 100644 index 00000000000..01b5a150ff1 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest.java @@ -0,0 +1,87 @@ +/* + * 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.db.migrations.v51; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.db.migrations.DatabaseMigration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.api.utils.DateUtils.parseDate; + +public class FeedProjectMeasuresLongDatesTest { + @ClassRule + public static DbTester db = new DbTester().schema(FeedProjectMeasuresLongDatesTest.class, "schema.sql"); + + @Before + public void before() throws Exception { + db.prepareDbUnit(getClass(), "before.xml"); + } + + @Test + public void execute() throws Exception { + DatabaseMigration migration = newMigration(System2.INSTANCE); + + migration.execute(); + + int count = db + .countSql("select count(*) from project_measures where " + + "measure_date_ms is not null"); + assertThat(count).isEqualTo(2); + } + + @Test + public void take_now_if_date_in_the_future() throws Exception { + System2 system = mock(System2.class); + when(system.now()).thenReturn(1234L); + + DatabaseMigration migration = newMigration(system); + + migration.execute(); + + int count = db + .countSql("select count(*) from project_measures where " + + "measure_date_ms = 1234"); + assertThat(count).isEqualTo(1); + } + + @Test + public void take_snapshot_date_if_in_the_past() throws Exception { + DatabaseMigration migration = newMigration(System2.INSTANCE); + + migration.execute(); + + long snapshotTime = parseDate("2014-09-25").getTime(); + int count = db + .countSql("select count(*) from project_measures where " + + "measure_date_ms=" + snapshotTime); + assertThat(count).isEqualTo(1); + } + + private FeedProjectMeasuresLongDates newMigration(System2 system) { + return new FeedProjectMeasuresLongDates(db.database(), system); + } +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest/before.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest/before.xml new file mode 100644 index 00000000000..f717cc25313 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest/before.xml @@ -0,0 +1,28 @@ + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest/schema.sql new file mode 100644 index 00000000000..c04fbbeb80e --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedManualMeasuresLongDatesTest/schema.sql @@ -0,0 +1,7 @@ +CREATE TABLE "MANUAL_MEASURES" ( + "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "CREATED_AT" TIMESTAMP, + "CREATED_AT_MS" BIGINT, + "UPDATED_AT" TIMESTAMP, + "UPDATED_AT_MS" BIGINT +); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/before.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/before.xml new file mode 100644 index 00000000000..ee5eb2d271a --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/before.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/schema.sql b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/schema.sql new file mode 100644 index 00000000000..473af7b357e --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/FeedProjectMeasuresLongDatesTest/schema.sql @@ -0,0 +1,5 @@ +CREATE TABLE "PROJECT_MEASURES" ( + "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "MEASURE_DATE" TIMESTAMP, + "MEASURE_DATE_MS" BIGINT +); diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/models/manual_measure.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/models/manual_measure.rb index d3a63f1cd30..a292be623ab 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/models/manual_measure.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/models/manual_measure.rb @@ -61,7 +61,7 @@ class ManualMeasure < ActiveRecord::Base if snapshot.nil? snapshot=resource.last_snapshot end - snapshot && updated_at && snapshot.created_at true + add_column 'manual_measures', :created_at_ms, :big_integer, :null => true + add_column 'manual_measures', :updated_at_ms, :big_integer, :null => true + end +end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/788_feed_measures_long_dates.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/788_feed_measures_long_dates.rb new file mode 100644 index 00000000000..04ab9acfe33 --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/788_feed_measures_long_dates.rb @@ -0,0 +1,30 @@ +# +# 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. +# + +# +# SonarQube 5.1 +# +class FeedMeasuresLongDates < ActiveRecord::Migration + def self.up + execute_java_migration('org.sonar.server.db.migrations.v51.FeedProjectMeasuresLongDates') + execute_java_migration('org.sonar.server.db.migrations.v51.FeedManualMeasuresLongDates') + end +end + diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/789_rename_measures_long_dates.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/789_rename_measures_long_dates.rb new file mode 100644 index 00000000000..34ad6b0d5c7 --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/789_rename_measures_long_dates.rb @@ -0,0 +1,34 @@ +# +# 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. +# + +# +# SonarQube 5.1 +# +class RenameMeasuresLongDates < ActiveRecord::Migration + def self.up + remove_column 'project_measures', 'measure_date' + remove_column 'manual_measures', 'created_at' + remove_column 'manual_measures', 'updated_at' + rename_column 'manual_measures', 'created_at_ms', 'created_at' + rename_column 'manual_measures', 'updated_at_ms', 'updated_at' + rename_column 'project_measures', 'measure_date_ms', 'measure_date' + end +end + diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java index 371f1e964fc..fc7327dbcb6 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java @@ -33,7 +33,7 @@ import java.util.List; */ public class DatabaseVersion implements BatchComponent, ServerComponent { - public static final int LAST_VERSION = 786; + public static final int LAST_VERSION = 789; /** * List of all the tables.n diff --git a/sonar-core/src/main/java/org/sonar/jpa/entity/ManualMeasure.java b/sonar-core/src/main/java/org/sonar/jpa/entity/ManualMeasure.java index b7e6b7201ff..4c12fb92ca8 100644 --- a/sonar-core/src/main/java/org/sonar/jpa/entity/ManualMeasure.java +++ b/sonar-core/src/main/java/org/sonar/jpa/entity/ManualMeasure.java @@ -23,7 +23,6 @@ import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import javax.persistence.*; -import java.util.Date; @Entity @Table(name = "manual_measures") @@ -51,10 +50,10 @@ public final class ManualMeasure { private String description; @Column(name = "created_at", updatable = true, nullable = true) - private Date createdAt; + private Long createdAt; @Column(name = "updated_at", updatable = true, nullable = true) - private Date updatedAt; + private Long updatedAt; @Column(name = "user_login", updatable = true, nullable = true, length = 40) private String userLogin; @@ -83,11 +82,11 @@ public final class ManualMeasure { return resourceId; } - public Date getCreatedAt() { + public Long getCreatedAt() { return createdAt; } - public Date getUpdatedAt() { + public Long getUpdatedAt() { return updatedAt; } diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql index 60192f301df..eceb883ec31 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql @@ -314,6 +314,9 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('783'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('784'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('785'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('786'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('787'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('788'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('789'); INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482', null, null); ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl index d1efca986f6..484d9265b83 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl @@ -227,7 +227,7 @@ CREATE TABLE "PROJECT_MEASURES" ( "RULES_CATEGORY_ID" INTEGER, "TEXT_VALUE" VARCHAR(4000), "TENDENCY" INTEGER, - "MEASURE_DATE" TIMESTAMP, + "MEASURE_DATE" BIGINT, "PROJECT_ID" INTEGER, "ALERT_STATUS" VARCHAR(5), "ALERT_TEXT" VARCHAR(4000), @@ -275,8 +275,8 @@ CREATE TABLE "MANUAL_MEASURES" ( "TEXT_VALUE" VARCHAR(4000), "USER_LOGIN" VARCHAR(255), "DESCRIPTION" VARCHAR(4000), - "CREATED_AT" TIMESTAMP, - "UPDATED_AT" TIMESTAMP + "CREATED_AT" BIGINT, + "UPDATED_AT" BIGINT ); CREATE TABLE "ACTIVE_RULES" ( diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModel.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModel.java index b7ec1155b86..dc5847f7564 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModel.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModel.java @@ -27,19 +27,14 @@ import org.sonar.api.database.DatabaseSession; import org.sonar.api.measures.Metric; import org.sonar.api.rules.RulePriority; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; +import javax.persistence.*; import java.io.UnsupportedEncodingException; import java.util.Date; +import static org.sonar.api.utils.DateUtils.dateToLong; +import static org.sonar.api.utils.DateUtils.longToDate; + /** * This class is the Hibernate model to store a measure in the DB */ @@ -75,9 +70,8 @@ public class MeasureModel implements Cloneable { @Column(name = "description", updatable = true, nullable = true, length = 4000) private String description; - @Temporal(TemporalType.TIMESTAMP) @Column(name = "measure_date", updatable = true, nullable = true) - private Date measureDate; + private Long measureDate; @Column(name = "rule_id", updatable = true, nullable = true) private Integer ruleId; @@ -119,14 +113,6 @@ public class MeasureModel implements Cloneable { @Column(name = "measure_data", updatable = true, nullable = true, length = 167772150) private byte[] data; - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - /** * Creates a measure based on a metric and a double value */ @@ -162,25 +148,19 @@ public class MeasureModel implements Cloneable { public MeasureModel() { } - /** - * @return the measure double value - */ - public Double getValue() { - return value; + public Long getId() { + return id; } - /** - * @return the measure description - */ - public String getDescription() { - return description; + public void setId(Long id) { + this.id = id; } /** - * Sets the measure description + * @return the measure double value */ - public void setDescription(String description) { - this.description = description; + public Double getValue() { + return value; } /** @@ -196,6 +176,20 @@ public class MeasureModel implements Cloneable { return this; } + /** + * @return the measure description + */ + public String getDescription() { + return description; + } + + /** + * Sets the measure description + */ + public void setDescription(String description) { + this.description = description; + } + /** * @return the measure alert level */ @@ -227,13 +221,6 @@ public class MeasureModel implements Cloneable { return tendency; } - /** - * @return whether the measure is about rule - */ - public boolean isRuleMeasure() { - return ruleId != null || rulePriority != null; - } - /** * Sets the measure tendency * @@ -244,6 +231,13 @@ public class MeasureModel implements Cloneable { return this; } + /** + * @return whether the measure is about rule + */ + public boolean isRuleMeasure() { + return ruleId != null || rulePriority != null; + } + public Integer getMetricId() { return metricId; } @@ -315,7 +309,7 @@ public class MeasureModel implements Cloneable { * @return the date of the measure */ public Date getMeasureDate() { - return measureDate; + return longToDate(measureDate); } /** @@ -324,6 +318,23 @@ public class MeasureModel implements Cloneable { * @return the current object */ public MeasureModel setMeasureDate(Date measureDate) { + this.measureDate = dateToLong(measureDate); + return this; + } + + /** + * @return the date of the measure + */ + public Long getMeasureDateMs() { + return measureDate; + } + + /** + * Sets the date for the measure + * + * @return the current object + */ + public MeasureModel setMeasureDateMs(Long measureDate) { this.measureDate = measureDate; return this; } diff --git a/sonar-plugin-api/src/main/resources/org/sonar/api/database/model/MeasureMapper.xml b/sonar-plugin-api/src/main/resources/org/sonar/api/database/model/MeasureMapper.xml index b9a64536e19..66b00baacae 100644 --- a/sonar-plugin-api/src/main/resources/org/sonar/api/database/model/MeasureMapper.xml +++ b/sonar-plugin-api/src/main/resources/org/sonar/api/database/model/MeasureMapper.xml @@ -10,7 +10,7 @@ variation_value_2, variation_value_3, variation_value_4, variation_value_5, person_id, measure_data) VALUES ( #{value, jdbcType=DOUBLE}, #{metricId, jdbcType=INTEGER}, #{snapshotId, jdbcType=INTEGER}, #{ruleId, jdbcType=INTEGER}, #{textValue, jdbcType=VARCHAR}, #{tendency, jdbcType=INTEGER}, - #{measureDate, jdbcType=TIMESTAMP}, #{projectId, jdbcType=INTEGER}, #{alertStatus, jdbcType=VARCHAR}, #{alertText, jdbcType=VARCHAR}, + #{measureDateMs, jdbcType=BIGINT}, #{projectId, jdbcType=INTEGER}, #{alertStatus, jdbcType=VARCHAR}, #{alertText, jdbcType=VARCHAR}, #{url, jdbcType=VARCHAR}, #{description, jdbcType=VARCHAR}, #{rulePriority.ordinal, jdbcType=INTEGER}, #{characteristicId, jdbcType=INTEGER}, #{variationValue1, jdbcType=DOUBLE}, #{variationValue2, jdbcType=DOUBLE}, #{variationValue3, jdbcType=DOUBLE}, #{variationValue4, jdbcType=DOUBLE}, #{variationValue5, jdbcType=DOUBLE}, #{personId, jdbcType=INTEGER}, #{data} ) -- cgit v1.2.3 From 556ba8154b2c6406b15cd4357cc74a06f3094e3a Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 11 Feb 2015 15:03:51 +0100 Subject: SONAR-6156 The search of projects on contextualized Issues page should support views --- .../sonar/server/component/db/ComponentDao.java | 10 ++ .../server/component/db/ComponentIndexDao.java | 36 ++++++ .../sonar/server/component/ws/ComponentsWs.java | 5 +- .../sonar/server/component/ws/SearchAction.java | 135 +++++++++++++++++++++ .../main/java/org/sonar/server/db/DbClient.java | 7 ++ .../sonar/server/platform/ServerComponents.java | 6 +- .../java/org/sonar/server/user/UserSession.java | 13 +- .../org/sonar/server/batch/IssuesActionTest.java | 3 + .../server/component/db/ComponentDaoTest.java | 21 ++++ .../server/component/db/ComponentIndexDaoTest.java | 65 ++++++++++ .../component/ws/ComponentAppActionTest.java | 2 +- .../server/component/ws/ComponentsWsTest.java | 14 ++- .../component/ws/SearchActionMediumTest.java | 113 +++++++++++++++++ .../org/sonar/server/user/UserSessionTest.java | 42 ++++++- ...ct_ids_from_query_and_view_or_sub_view_uuid.xml | 32 +++++ .../return_only_authorized_projects_from_view.json | 11 ++ .../return_only_authorized_projects_from_view.xml | 30 +++++ .../return_only_first_page.json | 5 + .../return_paged_result.json | 5 + .../return_projects_from_subview.json | 11 ++ .../return_projects_from_view.json | 15 +++ .../component/ws/SearchActionMediumTest/shared.xml | 35 ++++++ .../org/sonar/core/component/ComponentDto.java | 2 +- .../core/component/db/ComponentIndexMapper.java | 30 +++++ .../sonar/core/component/db/ComponentMapper.java | 11 +- .../java/org/sonar/core/persistence/MyBatis.java | 79 ++---------- .../java/org/sonar/core/user/AuthorizationDao.java | 61 ++++++---- .../org/sonar/core/user/AuthorizationMapper.java | 38 ++++++ .../core/component/db/ComponentIndexMapper.xml | 16 +++ .../sonar/core/component/db/ComponentMapper.xml | 13 ++ .../org/sonar/core/user/AuthorizationMapper.xml | 27 +++++ .../org/sonar/core/user/AuthorizationDaoTest.java | 60 +++++---- .../user_should_be_authorized.xml | 3 - 33 files changed, 817 insertions(+), 139 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentIndexDao.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentIndexDaoTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionMediumTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentIndexDaoTest/select_project_ids_from_query_and_view_or_sub_view_uuid.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.json create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_first_page.json create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_paged_result.json create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_subview.json create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_view.json create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/shared.xml create mode 100644 sonar-core/src/main/java/org/sonar/core/component/db/ComponentIndexMapper.java create mode 100644 sonar-core/src/main/java/org/sonar/core/user/AuthorizationMapper.java create mode 100644 sonar-core/src/main/resources/org/sonar/core/component/db/ComponentIndexMapper.xml (limited to 'sonar-core') diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentDao.java b/server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentDao.java index 85361463f9d..27cd270de81 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentDao.java @@ -103,6 +103,15 @@ public class ComponentDao extends BaseDao return mapper(session).selectModuleFilesTree(rootComponentUuid, Scopes.FILE); } + public List getByIds(final DbSession session, Collection ids) { + return DaoUtils.executeLargeInputs(ids, new Function, List>() { + @Override + public List apply(List partition) { + return mapper(session).findByIds(partition); + } + }); + } + public List getByUuids(final DbSession session, Collection uuids) { return DaoUtils.executeLargeInputs(uuids, new Function, List>() { @Override @@ -154,4 +163,5 @@ public class ComponentDao extends BaseDao public List selectProjectsFromView(DbSession session, String viewUuid, String projectViewUuid) { return mapper(session).selectProjectsFromView("%." + viewUuid + ".%", projectViewUuid); } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentIndexDao.java b/server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentIndexDao.java new file mode 100644 index 00000000000..7243b6125e2 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/component/db/ComponentIndexDao.java @@ -0,0 +1,36 @@ +/* + * 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.db.ComponentIndexMapper; +import org.sonar.core.persistence.DaoComponent; +import org.sonar.core.persistence.DbSession; + +import java.util.List; + +public class ComponentIndexDao implements ServerComponent, DaoComponent { + + public List selectProjectIdsFromQueryAndViewOrSubViewUuid(DbSession session, String query, String viewOrSubViewUuid) { + return session.getMapper(ComponentIndexMapper.class).selectProjectIdsFromQueryAndViewOrSubViewUuid(query + "%", "%." + viewOrSubViewUuid + ".%"); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWs.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWs.java index edf407a500d..94cadaa25f7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWs.java @@ -27,9 +27,11 @@ import org.sonar.api.server.ws.WebService; public class ComponentsWs implements WebService { private final ComponentAppAction appAction; + private final SearchAction searchAction; - public ComponentsWs(ComponentAppAction appAction) { + public ComponentsWs(ComponentAppAction appAction, SearchAction searchAction) { this.appAction = appAction; + this.searchAction = searchAction; } @Override @@ -39,6 +41,7 @@ public class ComponentsWs implements WebService { .setDescription("Components management"); appAction.define(controller); + searchAction.define(controller); defineSuggestionsAction(controller); controller.done(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java new file mode 100644 index 00000000000..50599c6d058 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java @@ -0,0 +1,135 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.component.ws; + +import com.google.common.collect.Sets; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.RequestHandler; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.db.DbClient; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.user.UserSession; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static com.google.common.collect.Sets.newLinkedHashSet; +import static org.sonar.api.server.ws.WebService.Param.PAGE; +import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE; + +public class SearchAction implements RequestHandler { + + private static final short MINIMUM_SEARCH_CHARACTERS = 2; + + private static final String PARAM_COMPONENT_UUID = "componentUuid"; + private static final String PARAM_QUERY = "q"; + + private final DbClient dbClient; + + public SearchAction(DbClient dbClient) { + this.dbClient = dbClient; + } + + void define(WebService.NewController controller) { + WebService.NewAction action = controller.createAction("search") + .setDescription("Search for components. Currently limited to projects in a view or a sub-view") + .setSince("5.1") + .setInternal(true) + .setHandler(this); + + action + .createParam(PARAM_COMPONENT_UUID) + .setRequired(true) + .setDescription("View or sub view UUID") + .setExampleValue("d6d9e1e5-5e13-44fa-ab82-3ec29efa8935"); + + action + .createParam(PARAM_QUERY) + .setRequired(true) + .setDescription("UTF-8 search query") + .setExampleValue("sonar"); + + action.addPagingParams(10); + } + + @Override + public void handle(Request request, Response response) { + String query = request.mandatoryParam(PARAM_QUERY); + if (query.length() < MINIMUM_SEARCH_CHARACTERS) { + throw new IllegalArgumentException(String.format("Minimum search is %s characters", MINIMUM_SEARCH_CHARACTERS)); + } + String viewOrSubUuid = request.mandatoryParam(PARAM_COMPONENT_UUID); + + JsonWriter json = response.newJsonWriter(); + json.beginObject(); + + DbSession session = dbClient.openSession(false); + try { + ComponentDto componentDto = dbClient.componentDao().getByUuid(session, viewOrSubUuid); + UserSession.get().checkProjectUuidPermission(UserRole.USER, componentDto.projectUuid()); + + Set projectIds = newLinkedHashSet(dbClient.componentIndexDao().selectProjectIdsFromQueryAndViewOrSubViewUuid(session, query, componentDto.uuid())); + Collection authorizedProjectIds = dbClient.authorizationDao().keepAuthorizedProjectIds(session, projectIds, UserSession.get().userId(), UserRole.USER); + + SearchOptions options = new SearchOptions(); + options.setPage(request.mandatoryParamAsInt(PAGE), request.mandatoryParamAsInt(PAGE_SIZE)); + Set pagedProjectIds = pagedProjectIds(authorizedProjectIds, options); + + List projects = dbClient.componentDao().getByIds(session, pagedProjectIds); + + options.writeJson(json, authorizedProjectIds.size()); + json.name("components").beginArray(); + for (ComponentDto project : projects) { + json.beginObject(); + json.prop("uuid", project.uuid()); + json.prop("name", project.name()); + json.endObject(); + } + json.endArray(); + } finally { + MyBatis.closeQuietly(session); + } + + json.endObject(); + json.close(); + } + + private Set pagedProjectIds(Collection projectIds, SearchOptions options) { + Set results = Sets.newLinkedHashSet(); + int index = 0; + for (Long projectId : projectIds) { + if (index >= options.getOffset() && results.size() < options.getLimit()) { + results.add(projectId); + } else if (results.size() >= options.getLimit()) { + break; + } + index++; + } + return results; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java index ea6ac8f2980..3c34603fe53 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java @@ -36,6 +36,7 @@ import org.sonar.core.user.AuthorDao; 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.SnapshotDao; import org.sonar.server.computation.db.AnalysisReportDao; import org.sonar.server.dashboard.db.DashboardDao; @@ -87,6 +88,7 @@ public class DbClient implements ServerComponent { private final WidgetPropertyDao widgetPropertyDao; private final FileSourceDao fileSourceDao; private final AuthorDao authorDao; + private final ComponentIndexDao componentIndexDao; public DbClient(Database db, MyBatis myBatis, DaoComponent... daoComponents) { this.db = db; @@ -120,6 +122,7 @@ public class DbClient implements ServerComponent { widgetPropertyDao = getDao(map, WidgetPropertyDao.class); fileSourceDao = getDao(map, FileSourceDao.class); authorDao = getDao(map, AuthorDao.class); + componentIndexDao = getDao(map, ComponentIndexDao.class); } public Database database() { @@ -226,6 +229,10 @@ public class DbClient implements ServerComponent { return authorDao; } + public ComponentIndexDao componentIndexDao() { + return componentIndexDao; + } + private K getDao(Map map, Class clazz) { return (K) map.get(clazz); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 9de6fee0478..f6f1357628b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -85,6 +85,7 @@ import org.sonar.server.component.ComponentService; import org.sonar.server.component.DefaultComponentFinder; import org.sonar.server.component.DefaultRubyComponentService; import org.sonar.server.component.db.ComponentDao; +import org.sonar.server.component.db.ComponentIndexDao; import org.sonar.server.component.db.SnapshotDao; import org.sonar.server.component.ws.*; import org.sonar.server.computation.AnalysisReportQueue; @@ -243,9 +244,10 @@ class ServerComponents { MetricDao.class, ComponentDao.class, SnapshotDao.class, - DbClient.class, MeasureFilterDao.class, AnalysisReportDao.class, + ComponentIndexDao.class, + DbClient.class, // Elasticsearch SearchClient.class, @@ -502,11 +504,11 @@ class ServerComponents { pico.addSingleton(DefaultComponentFinder.class); pico.addSingleton(DefaultRubyComponentService.class); pico.addSingleton(ComponentService.class); - pico.addSingleton(ComponentDao.class); pico.addSingleton(ResourcesWs.class); pico.addSingleton(ComponentsWs.class); pico.addSingleton(ProjectsWs.class); pico.addSingleton(ComponentAppAction.class); + pico.addSingleton(org.sonar.server.component.ws.SearchAction.class); pico.addSingleton(EventsWs.class); pico.addSingleton(ComponentCleanerService.class); diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java b/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java index ba6c33c9dc3..06d92231bd4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/UserSession.java @@ -179,6 +179,16 @@ public class UserSession { return this; } + /** + * Ensures that user implies the specified project permission. If not a {@link org.sonar.server.exceptions.ForbiddenException} is thrown. + */ + public UserSession checkProjectUuidPermission(String projectPermission, String projectUuid) { + if (!hasProjectPermissionByUuid(projectPermission, projectUuid)) { + throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE); + } + return this; + } + /** * Does the user have the given project permission ? */ @@ -222,12 +232,11 @@ public class UserSession { } /** - * Does the user have the given project permission for a component ? + * Does the user have the given project permission for a component key ? */ public boolean hasComponentPermission(String permission, String componentKey) { String projectKey = projectKeyByComponentKey.get(componentKey); if (projectKey == null) { - // TODO use method using UUID ResourceDto project = resourceDao().getRootProjectByComponentKey(componentKey); if (project == null) { return false; diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java index f7db3d0126b..e5dcb669c8a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java @@ -24,6 +24,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.sonar.api.platform.Server; import org.sonar.api.web.UserRole; import org.sonar.core.permission.GlobalPermissions; @@ -36,9 +37,11 @@ import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.issue.db.IssueDao; import org.sonar.server.user.MockUserSession; import org.sonar.server.ws.WsTester; +import org.sonar.test.DbTests; import static org.mockito.Mockito.mock; +@Category(DbTests.class) public class IssuesActionTest { private final static String PROJECT_KEY = "struts"; diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentDaoTest.java index 53b7462d1df..fde5e2162b9 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentDaoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentDaoTest.java @@ -163,6 +163,27 @@ public class ComponentDaoTest extends AbstractDaoTestCase { assertThat(dao.getByKeys(session, "unknown")).isEmpty(); } + @Test + public void get_by_ids() { + setupData("shared"); + + List results = dao.getByIds(session, newArrayList(4L)); + assertThat(results).hasSize(1); + + ComponentDto result = results.get(0); + assertThat(result).isNotNull(); + assertThat(result.key()).isEqualTo("org.struts:struts-core:src/org/struts/RequestContext.java"); + assertThat(result.path()).isEqualTo("src/org/struts/RequestContext.java"); + assertThat(result.name()).isEqualTo("RequestContext.java"); + assertThat(result.longName()).isEqualTo("org.struts.RequestContext"); + assertThat(result.qualifier()).isEqualTo("FIL"); + assertThat(result.scope()).isEqualTo("FIL"); + assertThat(result.language()).isEqualTo("java"); + assertThat(result.parentProjectId()).isEqualTo(2); + + assertThat(dao.getByIds(session, newArrayList(555L))).isEmpty(); + } + @Test public void get_by_uuids() { setupData("shared"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentIndexDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentIndexDaoTest.java new file mode 100644 index 00000000000..76d9248a85a --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/component/db/ComponentIndexDaoTest.java @@ -0,0 +1,65 @@ +/* + * 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.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.test.DbTests; + +import static org.assertj.core.api.Assertions.assertThat; + +@Category(DbTests.class) +public class ComponentIndexDaoTest { + + @Rule + public DbTester dbTester = new DbTester(); + + DbSession session; + + ComponentIndexDao dao; + + @Before + public void createDao() throws Exception { + session = dbTester.myBatis().openSession(false); + dao = new ComponentIndexDao(); + } + + @After + public void tearDown() throws Exception { + session.close(); + } + + @Test + public void select_project_ids_from_query_and_view_or_sub_view_uuid() throws Exception { + dbTester.prepareDbUnit(getClass(), "select_project_ids_from_query_and_view_or_sub_view_uuid.xml"); + String viewUuid = "EFGH"; + + assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "project", viewUuid)).containsOnly(1L, 2L); + assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "one", viewUuid)).containsOnly(1L); + assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "two", viewUuid)).containsOnly(2L); + assertThat(dao.selectProjectIdsFromQueryAndViewOrSubViewUuid(session, "unknown", viewUuid)).isEmpty(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java index bf54f209aaa..19496fb97ad 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java @@ -101,7 +101,7 @@ public class ComponentAppActionTest { when(measureDao.findByComponentKeyAndMetricKeys(anyString(), anyListOf(String.class), eq(session))).thenReturn(measures); - tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, durations, i18n))); + tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, durations, i18n), mock(SearchAction.class))); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java index 0e88b962bec..6d216685dc7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java @@ -38,7 +38,7 @@ public class ComponentsWsTest { @Before public void setUp() throws Exception { - WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(Durations.class), mock(I18n.class)))); + WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(Durations.class), mock(I18n.class)), new SearchAction(mock(DbClient.class)))); controller = tester.controller("api/components"); } @@ -47,7 +47,7 @@ public class ComponentsWsTest { assertThat(controller).isNotNull(); assertThat(controller.description()).isNotEmpty(); assertThat(controller.since()).isEqualTo("4.2"); - assertThat(controller.actions()).hasSize(2); + assertThat(controller.actions()).hasSize(3); } @Test @@ -71,4 +71,14 @@ public class ComponentsWsTest { assertThat(action.params()).hasSize(2); } + @Test + public void define_search_action() throws Exception { + WebService.Action action = controller.action("search"); + assertThat(action).isNotNull(); + assertThat(action.isInternal()).isTrue(); + assertThat(action.isPost()).isFalse(); + assertThat(action.handler()).isNotNull(); + assertThat(action.params()).hasSize(4); + } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionMediumTest.java new file mode 100644 index 00000000000..ab80ff1e2be --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchActionMediumTest.java @@ -0,0 +1,113 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.component.ws; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.web.UserRole; +import org.sonar.core.persistence.DbTester; +import org.sonar.core.user.AuthorizationDao; +import org.sonar.server.component.db.ComponentDao; +import org.sonar.server.component.db.ComponentIndexDao; +import org.sonar.server.db.DbClient; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.ws.WsTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown; +import static org.mockito.Mockito.mock; + +public class SearchActionMediumTest { + + @Rule + public DbTester dbTester = new DbTester(); + + WsTester tester; + + @Before + public void setUp() throws Exception { + DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), + new ComponentDao(), new AuthorizationDao(dbTester.myBatis()), new ComponentIndexDao() + ); + tester = new WsTester(new ComponentsWs(mock(ComponentAppAction.class), new SearchAction(dbClient))); + } + + @Test + public void return_projects_from_view() throws Exception { + dbTester.prepareDbUnit(getClass(), "shared.xml"); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH"); + + WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st"); + request.execute().assertJson(getClass(), "return_projects_from_view.json"); + } + + @Test + public void return_projects_from_subview() throws Exception { + dbTester.prepareDbUnit(getClass(), "shared.xml"); + MockUserSession.set().setLogin("john").addComponentUuidPermission(UserRole.USER, "EFGH", "FGHI"); + + WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "FGHI").setParam("q", "st"); + request.execute().assertJson(getClass(), "return_projects_from_subview.json"); + } + + @Test + public void return_only_authorized_projects_from_view() throws Exception { + dbTester.prepareDbUnit(getClass(), "return_only_authorized_projects_from_view.xml"); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH"); + + WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st"); + request.execute().assertJson(getClass(), "return_only_authorized_projects_from_view.json"); + } + + @Test + public void return_paged_result() throws Exception { + dbTester.prepareDbUnit(getClass(), "shared.xml"); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH"); + + WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st").setParam("p", "2").setParam("ps", "1"); + request.execute().assertJson(getClass(), "return_paged_result.json", false); + } + + @Test + public void return_only_first_page() throws Exception { + dbTester.prepareDbUnit(getClass(), "shared.xml"); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH"); + + WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "st").setParam("p", "1").setParam("ps", "1"); + request.execute().assertJson(getClass(), "return_only_first_page.json", false); + } + + @Test + public void fail_when_search_param_is_too_short() throws Exception { + dbTester.prepareDbUnit(getClass(), "shared.xml"); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, "EFGH"); + + WsTester.TestRequest request = tester.newGetRequest("api/components", "search").setParam("componentUuid", "EFGH").setParam("q", "s"); + + try { + request.execute(); + failBecauseExceptionWasNotThrown(IllegalArgumentException.class); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage("Minimum search is 2 characters"); + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionTest.java index e85708125fa..299b2631a71 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/UserSessionTest.java @@ -22,10 +22,12 @@ package org.sonar.server.user; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; import org.sonar.core.user.AuthorizationDao; +import org.sonar.server.component.ComponentTesting; import org.sonar.server.exceptions.ForbiddenException; import javax.annotation.Nullable; @@ -148,6 +150,28 @@ public class UserSessionTest { session.checkProjectPermission(UserRole.USER, "com.foo:Bar"); } + @Test + public void check_project_uuid_permission_ok() throws Exception { + AuthorizationDao authorizationDao = mock(AuthorizationDao.class); + UserSession session = new SpyUserSession("marius", authorizationDao).setUserId(1); + + ComponentDto project = ComponentTesting.newProjectDto(); + when(authorizationDao.selectAuthorizedRootProjectsUuids(1, UserRole.USER)).thenReturn(newArrayList(project.uuid())); + + session.checkProjectUuidPermission(UserRole.USER, project.uuid()); + } + + @Test(expected = ForbiddenException.class) + public void check_project_uuid_permission_ko() throws Exception { + AuthorizationDao authorizationDao = mock(AuthorizationDao.class); + UserSession session = new SpyUserSession("marius", authorizationDao).setUserId(1); + + ComponentDto project = ComponentTesting.newProjectDto(); + when(authorizationDao.selectAuthorizedRootProjectsUuids(1, UserRole.USER)).thenReturn(newArrayList(project.uuid())); + + session.checkProjectUuidPermission(UserRole.USER, "another project"); + } + @Test public void has_component_permission() throws Exception { AuthorizationDao authorizationDao = mock(AuthorizationDao.class); @@ -164,7 +188,7 @@ public class UserSessionTest { } @Test - public void check_component_permission_ok() throws Exception { + public void check_component_key_permission_ok() throws Exception { AuthorizationDao authorizationDao = mock(AuthorizationDao.class); ResourceDao resourceDao = mock(ResourceDao.class); UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1); @@ -176,7 +200,7 @@ public class UserSessionTest { } @Test(expected = ForbiddenException.class) - public void check_component_permission_ko() throws Exception { + public void check_component_key_permission_ko() throws Exception { AuthorizationDao authorizationDao = mock(AuthorizationDao.class); ResourceDao resourceDao = mock(ResourceDao.class); UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1); @@ -188,7 +212,7 @@ public class UserSessionTest { } @Test(expected = ForbiddenException.class) - public void check_component_permission_when_project_not_found() throws Exception { + public void check_component_key_permission_when_project_not_found() throws Exception { AuthorizationDao authorizationDao = mock(AuthorizationDao.class); ResourceDao resourceDao = mock(ResourceDao.class); UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1); @@ -198,6 +222,18 @@ public class UserSessionTest { session.checkComponentPermission(UserRole.USER, "com.foo:Bar:BarFile.xoo"); } + @Test(expected = ForbiddenException.class) + public void check_component_dto_permission_ko() throws Exception { + AuthorizationDao authorizationDao = mock(AuthorizationDao.class); + ResourceDao resourceDao = mock(ResourceDao.class); + UserSession session = new SpyUserSession("marius", authorizationDao, resourceDao).setUserId(1); + + ComponentDto project = ComponentTesting.newProjectDto(); + when(authorizationDao.selectAuthorizedRootProjectsKeys(1, UserRole.USER)).thenReturn(newArrayList(project.uuid())); + + session.checkComponentPermission(UserRole.USER, "another"); + } + static class SpyUserSession extends UserSession { private AuthorizationDao authorizationDao; private ResourceDao resourceDao; diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentIndexDaoTest/select_project_ids_from_query_and_view_or_sub_view_uuid.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentIndexDaoTest/select_project_ids_from_query_and_view_or_sub_view_uuid.xml new file mode 100644 index 00000000000..aff69473ce1 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentIndexDaoTest/select_project_ids_from_query_and_view_or_sub_view_uuid.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.json new file mode 100644 index 00000000000..9676629feeb --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.json @@ -0,0 +1,11 @@ +{ + "total": 1, + "p": 1, + "ps": 10, + "components": [ + { + "uuid": "JKLM", + "name": "Struts" + } + ] +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.xml new file mode 100644 index 00000000000..0b5c0343840 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_authorized_projects_from_view.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_first_page.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_first_page.json new file mode 100644 index 00000000000..e913f5e4c67 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_only_first_page.json @@ -0,0 +1,5 @@ +{ + "total": 2, + "p": 1, + "ps": 1 +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_paged_result.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_paged_result.json new file mode 100644 index 00000000000..d871108edce --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_paged_result.json @@ -0,0 +1,5 @@ +{ + "total": 2, + "p": 2, + "ps": 1 +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_subview.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_subview.json new file mode 100644 index 00000000000..9676629feeb --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_subview.json @@ -0,0 +1,11 @@ +{ + "total": 1, + "p": 1, + "ps": 10, + "components": [ + { + "uuid": "JKLM", + "name": "Struts" + } + ] +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_view.json b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_view.json new file mode 100644 index 00000000000..f7f99cc9d73 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/return_projects_from_view.json @@ -0,0 +1,15 @@ +{ + "total": 2, + "p": 1, + "ps": 10, + "components": [ + { + "uuid": "JKLM", + "name": "Struts" + }, + { + "uuid": "KLMN", + "name": "Elasticsearch" + } + ] +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/shared.xml new file mode 100644 index 00000000000..687ef3d7b14 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/ws/SearchActionMediumTest/shared.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java b/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java index ca3a0b67a30..ecad07f8bbd 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java +++ b/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java @@ -105,7 +105,7 @@ public class ComponentDto extends Dto implements Component { } /** - * Return the root project id. On a root project, return itself + * Return the root project uuid. On a root project, return itself */ public String projectUuid() { return projectUuid; diff --git a/sonar-core/src/main/java/org/sonar/core/component/db/ComponentIndexMapper.java b/sonar-core/src/main/java/org/sonar/core/component/db/ComponentIndexMapper.java new file mode 100644 index 00000000000..114b8da6ba0 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/component/db/ComponentIndexMapper.java @@ -0,0 +1,30 @@ +/* + * 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.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface ComponentIndexMapper { + + List selectProjectIdsFromQueryAndViewOrSubViewUuid(@Param("query") String query, @Param("viewOrSubViewUuid") String viewOrSubViewUuid); +} diff --git a/sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java b/sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java index 269ede5a461..1f057c62dc8 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/component/db/ComponentMapper.java @@ -35,21 +35,12 @@ import java.util.List; */ public interface ComponentMapper { - /** - * Warning, projectId is always null - */ @CheckForNull ComponentDto selectByKey(String key); - /** - * Warning, projectId is always null - */ @CheckForNull ComponentDto selectById(long id); - /** - * Warning, projectId is always null - */ @CheckForNull ComponentDto selectByUuid(String uuid); @@ -65,6 +56,8 @@ public interface ComponentMapper { List findByKeys(@Param("keys") Collection keys); + List findByIds(@Param("ids") Collection ids); + List findByUuids(@Param("uuids") Collection uuids); List selectExistingUuids(@Param("uuids") Collection uuids); diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java index 0390d6fabdf..2b5fa87277f 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java @@ -25,11 +25,7 @@ import com.google.common.io.Closeables; import org.apache.ibatis.builder.xml.XMLMapperBuilder; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.mapping.Environment; -import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.session.ExecutorType; -import org.apache.ibatis.session.SqlSession; -import org.apache.ibatis.session.SqlSessionFactory; -import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.apache.ibatis.session.*; import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.apache.ibatis.type.JdbcType; import org.slf4j.LoggerFactory; @@ -43,19 +39,13 @@ 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.db.ComponentIndexMapper; import org.sonar.core.component.db.ComponentMapper; import org.sonar.core.component.db.SnapshotMapper; import org.sonar.core.computation.db.AnalysisReportDto; import org.sonar.core.computation.db.AnalysisReportMapper; import org.sonar.core.config.Logback; -import org.sonar.core.dashboard.ActiveDashboardDto; -import org.sonar.core.dashboard.ActiveDashboardMapper; -import org.sonar.core.dashboard.DashboardDto; -import org.sonar.core.dashboard.DashboardMapper; -import org.sonar.core.dashboard.WidgetDto; -import org.sonar.core.dashboard.WidgetMapper; -import org.sonar.core.dashboard.WidgetPropertyDto; -import org.sonar.core.dashboard.WidgetPropertyMapper; +import org.sonar.core.dashboard.*; import org.sonar.core.dependency.DependencyDto; import org.sonar.core.dependency.DependencyMapper; import org.sonar.core.dependency.ResourceSnapshotDto; @@ -64,33 +54,11 @@ import org.sonar.core.duplication.DuplicationMapper; import org.sonar.core.duplication.DuplicationUnitDto; import org.sonar.core.graph.jdbc.GraphDto; import org.sonar.core.graph.jdbc.GraphDtoMapper; -import org.sonar.core.issue.db.ActionPlanDto; -import org.sonar.core.issue.db.ActionPlanMapper; -import org.sonar.core.issue.db.ActionPlanStatsDto; -import org.sonar.core.issue.db.ActionPlanStatsMapper; -import org.sonar.core.issue.db.BatchIssueDto; -import org.sonar.core.issue.db.IssueChangeDto; -import org.sonar.core.issue.db.IssueChangeMapper; -import org.sonar.core.issue.db.IssueDto; -import org.sonar.core.issue.db.IssueFilterDto; -import org.sonar.core.issue.db.IssueFilterFavouriteDto; -import org.sonar.core.issue.db.IssueFilterFavouriteMapper; -import org.sonar.core.issue.db.IssueFilterMapper; -import org.sonar.core.issue.db.IssueMapper; -import org.sonar.core.measure.db.MeasureDto; -import org.sonar.core.measure.db.MeasureFilterDto; -import org.sonar.core.measure.db.MeasureFilterMapper; -import org.sonar.core.measure.db.MeasureMapper; -import org.sonar.core.measure.db.MetricDto; -import org.sonar.core.measure.db.MetricMapper; +import org.sonar.core.issue.db.*; +import org.sonar.core.measure.db.*; import org.sonar.core.notification.db.NotificationQueueDto; import org.sonar.core.notification.db.NotificationQueueMapper; -import org.sonar.core.permission.GroupWithPermissionDto; -import org.sonar.core.permission.PermissionTemplateDto; -import org.sonar.core.permission.PermissionTemplateGroupDto; -import org.sonar.core.permission.PermissionTemplateMapper; -import org.sonar.core.permission.PermissionTemplateUserDto; -import org.sonar.core.permission.UserWithPermissionDto; +import org.sonar.core.permission.*; import org.sonar.core.persistence.dialect.Dialect; import org.sonar.core.persistence.migration.v44.Migration44Mapper; import org.sonar.core.persistence.migration.v45.Migration45Mapper; @@ -100,22 +68,9 @@ import org.sonar.core.properties.PropertyDto; import org.sonar.core.purge.IdUuidPair; import org.sonar.core.purge.PurgeMapper; import org.sonar.core.purge.PurgeableSnapshotDto; -import org.sonar.core.qualitygate.db.ProjectQgateAssociationDto; -import org.sonar.core.qualitygate.db.ProjectQgateAssociationMapper; -import org.sonar.core.qualitygate.db.QualityGateConditionDto; -import org.sonar.core.qualitygate.db.QualityGateConditionMapper; -import org.sonar.core.qualitygate.db.QualityGateDto; -import org.sonar.core.qualitygate.db.QualityGateMapper; -import org.sonar.core.qualityprofile.db.ActiveRuleDto; -import org.sonar.core.qualityprofile.db.ActiveRuleMapper; -import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; -import org.sonar.core.qualityprofile.db.QualityProfileDto; -import org.sonar.core.qualityprofile.db.QualityProfileMapper; -import org.sonar.core.resource.ResourceDto; -import org.sonar.core.resource.ResourceIndexDto; -import org.sonar.core.resource.ResourceIndexerMapper; -import org.sonar.core.resource.ResourceKeyUpdaterMapper; -import org.sonar.core.resource.ResourceMapper; +import org.sonar.core.qualitygate.db.*; +import org.sonar.core.qualityprofile.db.*; +import org.sonar.core.resource.*; import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleMapper; import org.sonar.core.rule.RuleParamDto; @@ -125,19 +80,7 @@ import org.sonar.core.technicaldebt.db.CharacteristicMapper; import org.sonar.core.technicaldebt.db.RequirementMigrationDto; import org.sonar.core.template.LoadedTemplateDto; import org.sonar.core.template.LoadedTemplateMapper; -import org.sonar.core.user.AuthorDto; -import org.sonar.core.user.AuthorMapper; -import org.sonar.core.user.GroupDto; -import org.sonar.core.user.GroupMapper; -import org.sonar.core.user.GroupMembershipDto; -import org.sonar.core.user.GroupMembershipMapper; -import org.sonar.core.user.GroupRoleDto; -import org.sonar.core.user.RoleMapper; -import org.sonar.core.user.UserDto; -import org.sonar.core.user.UserGroupDto; -import org.sonar.core.user.UserGroupMapper; -import org.sonar.core.user.UserMapper; -import org.sonar.core.user.UserRoleDto; +import org.sonar.core.user.*; import javax.annotation.Nullable; @@ -262,7 +205,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, + AnalysisReportMapper.class, ComponentIndexMapper.class, Migration45Mapper.class, Migration50Mapper.class }; loadMappers(conf, mappers); diff --git a/sonar-core/src/main/java/org/sonar/core/user/AuthorizationDao.java b/sonar-core/src/main/java/org/sonar/core/user/AuthorizationDao.java index 530c9609f4d..31b778a4a5c 100644 --- a/sonar-core/src/main/java/org/sonar/core/user/AuthorizationDao.java +++ b/sonar-core/src/main/java/org/sonar/core/user/AuthorizationDao.java @@ -19,14 +19,17 @@ */ package org.sonar.core.user; -import com.google.common.collect.ImmutableMap; +import com.google.common.base.Function; import com.google.common.collect.Sets; import org.apache.ibatis.session.SqlSession; import org.sonar.api.ServerComponent; import org.sonar.core.persistence.DaoComponent; +import org.sonar.core.persistence.DaoUtils; +import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import javax.annotation.Nullable; + import java.util.*; import static com.google.common.collect.Maps.newHashMap; @@ -40,38 +43,48 @@ public class AuthorizationDao implements ServerComponent, DaoComponent { this.mybatis = mybatis; } - public Set keepAuthorizedComponentKeys(Set componentKeys, @Nullable Integer userId, String role) { - SqlSession session = mybatis.openSession(false); - try { - return keepAuthorizedComponentKeys(componentKeys, userId, role, session); + public Collection keepAuthorizedProjectIds(final DbSession session, final Collection componentIds, @Nullable final Integer userId, final String role) { + if (componentIds.isEmpty()) { + return Collections.emptySet(); + } + return DaoUtils.executeLargeInputs(componentIds, new Function, List>() { + @Override + public List apply(List partition) { + if (userId == null) { + return session.getMapper(AuthorizationMapper.class).keepAuthorizedProjectIdsForAnonymous(role, componentIds); + } else { + return session.getMapper(AuthorizationMapper.class).keepAuthorizedProjectIdsForUser(userId, role, componentIds); + } + } + }); + } + /** + * Used by the Views Plugin + */ + public boolean isAuthorizedComponentKey(String componentKey, @Nullable Integer userId, String role) { + DbSession session = mybatis.openSession(false); + try { + return keepAuthorizedComponentKeys(session, Sets.newHashSet(componentKey), userId, role).size() == 1; } finally { MyBatis.closeQuietly(session); } } - public Set keepAuthorizedComponentKeys(Set componentKeys, @Nullable Integer userId, String role, SqlSession session) { + private Set keepAuthorizedComponentKeys(final DbSession session, final Set componentKeys, @Nullable final Integer userId, final String role) { if (componentKeys.isEmpty()) { return Collections.emptySet(); } - String sql; - Map params; - if (userId == null) { - sql = "keepAuthorizedComponentKeysForAnonymous"; - params = ImmutableMap.of("role", role, "componentKeys", componentKeys); - } else { - sql = "keepAuthorizedComponentKeysForUser"; - params = ImmutableMap.of(USER_ID_PARAM, userId, "role", role, "componentKeys", componentKeys); - } - - return Sets.newHashSet(session.selectList(sql, params)); - } - - /** - * Used by the Views Plugin - */ - public boolean isAuthorizedComponentKey(String componentKey, @Nullable Integer userId, String role) { - return keepAuthorizedComponentKeys(Sets.newHashSet(componentKey), userId, role).size() == 1; + return Sets.newHashSet(DaoUtils.executeLargeInputs(componentKeys, new Function, List>() { + @Override + public List apply(List partition) { + if (userId == null) { + return session.getMapper(AuthorizationMapper.class).keepAuthorizedComponentKeysForAnonymous(role, componentKeys); + } else { + return session.getMapper(AuthorizationMapper.class).keepAuthorizedComponentKeysForUser(userId, role, componentKeys); + } + } + })); } public Collection selectAuthorizedRootProjectsKeys(@Nullable Integer userId, String role) { diff --git a/sonar-core/src/main/java/org/sonar/core/user/AuthorizationMapper.java b/sonar-core/src/main/java/org/sonar/core/user/AuthorizationMapper.java new file mode 100644 index 00000000000..10d137dffbd --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/user/AuthorizationMapper.java @@ -0,0 +1,38 @@ +/* + * 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.user; + +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; +import java.util.List; + +public interface AuthorizationMapper { + + List keepAuthorizedProjectIdsForAnonymous(@Param("role") String role, @Param("componentIds") Collection componentIds); + + List keepAuthorizedProjectIdsForUser(@Param("userId") Integer userId, @Param("role") String role, @Param("componentIds") Collection componentIds); + + List keepAuthorizedComponentKeysForAnonymous(@Param("role") String role, @Param("componentKeys") Collection componentKeys); + + List keepAuthorizedComponentKeysForUser(@Param("userId") Integer userId, @Param("role") String role, @Param("componentKeys") Collection componentKeys); + +} diff --git a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentIndexMapper.xml b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentIndexMapper.xml new file mode 100644 index 00000000000..6b80ef94aac --- /dev/null +++ b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentIndexMapper.xml @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml index 98426094cd2..ee96cd81563 100644 --- a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml @@ -86,6 +86,19 @@ + + + + + + diff --git a/sonar-core/src/test/java/org/sonar/core/user/AuthorizationDaoTest.java b/sonar-core/src/test/java/org/sonar/core/user/AuthorizationDaoTest.java index 2e267d391ad..521d0af66a3 100644 --- a/sonar-core/src/test/java/org/sonar/core/user/AuthorizationDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/user/AuthorizationDaoTest.java @@ -20,19 +20,33 @@ package org.sonar.core.user; import com.google.common.collect.Sets; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.sonar.core.persistence.AbstractDaoTestCase; +import org.sonar.core.persistence.DbSession; import java.util.Collection; -import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; public class AuthorizationDaoTest extends AbstractDaoTestCase { private static final int USER = 100; - private static final String PROJECT = "pj-w-snapshot", PACKAGE = "pj-w-snapshot:package", FILE = "pj-w-snapshot:file", FILE_IN_OTHER_PROJECT = "another", - EMPTY_PROJECT = "pj-wo-snapshot"; + private static final Long PROJECT_ID = 300L, EMPTY_PROJECT_ID = 400L; + private static final String PROJECT = "pj-w-snapshot"; + + DbSession session; + + @Before + public void setUp() throws Exception { + session = getMyBatis().openSession(false); + } + + @After + public void tearDown() throws Exception { + session.close(); + } @Test public void user_should_be_authorized() { @@ -40,15 +54,15 @@ public class AuthorizationDaoTest extends AbstractDaoTestCase { setupData("user_should_be_authorized"); AuthorizationDao authorization = new AuthorizationDao(getMyBatis()); - Set componentIds = authorization.keepAuthorizedComponentKeys( - Sets.newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT), + Collection componentIds = authorization.keepAuthorizedProjectIds(session, + Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID), USER, "user"); - assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT); + assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID); // user does not have the role "admin" - componentIds = authorization.keepAuthorizedComponentKeys( - Sets.newHashSet(PROJECT, PACKAGE, FILE), + componentIds = authorization.keepAuthorizedProjectIds(session, + Sets.newHashSet(PROJECT_ID), USER, "admin"); assertThat(componentIds).isEmpty(); } @@ -72,15 +86,15 @@ public class AuthorizationDaoTest extends AbstractDaoTestCase { setupData("group_should_be_authorized"); AuthorizationDao authorization = new AuthorizationDao(getMyBatis()); - Set componentIds = authorization.keepAuthorizedComponentKeys( - Sets.newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT), + Collection componentIds = authorization.keepAuthorizedProjectIds(session, + Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID), USER, "user"); - assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT); + assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID); // group does not have the role "admin" - componentIds = authorization.keepAuthorizedComponentKeys( - Sets.newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT), + componentIds = authorization.keepAuthorizedProjectIds(session, + Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID), USER, "admin"); assertThat(componentIds).isEmpty(); } @@ -91,15 +105,15 @@ public class AuthorizationDaoTest extends AbstractDaoTestCase { setupData("group_should_have_global_authorization"); AuthorizationDao authorization = new AuthorizationDao(getMyBatis()); - Set componentIds = authorization.keepAuthorizedComponentKeys( - Sets.newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT), + Collection componentIds = authorization.keepAuthorizedProjectIds(session, + Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID), USER, "user"); - assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT); + assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID); // group does not have the role "admin" - componentIds = authorization.keepAuthorizedComponentKeys( - Sets.newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT), + componentIds = authorization.keepAuthorizedProjectIds(session, + Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID), USER, "admin"); assertThat(componentIds).isEmpty(); } @@ -109,15 +123,15 @@ public class AuthorizationDaoTest extends AbstractDaoTestCase { setupData("anonymous_should_be_authorized"); AuthorizationDao authorization = new AuthorizationDao(getMyBatis()); - Set componentIds = authorization.keepAuthorizedComponentKeys( - Sets.newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT, EMPTY_PROJECT), + Collection componentIds = authorization.keepAuthorizedProjectIds(session, + Sets.newHashSet(PROJECT_ID, EMPTY_PROJECT_ID), null, "user"); - assertThat(componentIds).containsOnly(PROJECT, PACKAGE, FILE, EMPTY_PROJECT); + assertThat(componentIds).containsOnly(PROJECT_ID, EMPTY_PROJECT_ID); // group does not have the role "admin" - componentIds = authorization.keepAuthorizedComponentKeys( - Sets.newHashSet(PROJECT, PACKAGE, FILE, FILE_IN_OTHER_PROJECT), + componentIds = authorization.keepAuthorizedProjectIds(session, + Sets.newHashSet(PROJECT_ID), null, "admin"); assertThat(componentIds).isEmpty(); } diff --git a/sonar-core/src/test/resources/org/sonar/core/user/AuthorizationDaoTest/user_should_be_authorized.xml b/sonar-core/src/test/resources/org/sonar/core/user/AuthorizationDaoTest/user_should_be_authorized.xml index b6371a7e0b1..3771e09738d 100644 --- a/sonar-core/src/test/resources/org/sonar/core/user/AuthorizationDaoTest/user_should_be_authorized.xml +++ b/sonar-core/src/test/resources/org/sonar/core/user/AuthorizationDaoTest/user_should_be_authorized.xml @@ -6,9 +6,6 @@ - - - -- cgit v1.2.3 From 069ea95835b904dcef634c90b4c059ba45183c87 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Tue, 10 Feb 2015 18:33:18 +0100 Subject: Fix quality flaws --- .../sonar/server/activity/RubyQProfileActivityService.java | 2 +- .../src/main/java/org/sonar/server/search/BaseDoc.java | 6 ++++++ .../src/main/java/org/sonar/server/search/BaseIndex.java | 11 +++++------ .../main/java/org/sonar/batch/debt/SqaleRatingDecorator.java | 2 +- .../src/main/java/org/sonar/core/i18n/DurationLabel.java | 6 +++--- .../src/main/java/org/sonar/core/issue/db/IssueMapper.java | 2 +- 6 files changed, 17 insertions(+), 12 deletions(-) (limited to 'sonar-core') diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/RubyQProfileActivityService.java b/server/sonar-server/src/main/java/org/sonar/server/activity/RubyQProfileActivityService.java index 9180dbf027d..744a3c05fd6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/activity/RubyQProfileActivityService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/RubyQProfileActivityService.java @@ -69,7 +69,7 @@ public class RubyQProfileActivityService implements ServerComponent, Startable { queryContext.setPage(pageIndex, 50); Result result = service.searchActivities(query, queryContext); - return new QProfileActivityResult(result.getHits(), Paging.create(queryContext.getLimit(), pageIndex, Long.valueOf(result.getTotal()).intValue())); + return new QProfileActivityResult(result.getHits(), Paging.create(queryContext.getLimit(), pageIndex, (int) result.getTotal())); } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/BaseDoc.java b/server/sonar-server/src/main/java/org/sonar/server/search/BaseDoc.java index 68139b8e552..68db56a5fe2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/BaseDoc.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/BaseDoc.java @@ -25,6 +25,8 @@ import javax.annotation.Nullable; import java.util.Date; import java.util.Map; +import static com.google.common.collect.Maps.newHashMap; + /** * Base implementation for business objects based on elasticsearch document */ @@ -32,6 +34,10 @@ public abstract class BaseDoc { protected final Map fields; + protected BaseDoc() { + this.fields = newHashMap(); + } + protected BaseDoc(Map fields) { this.fields = fields; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java b/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java index bc7821588c4..460e7b2ff48 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/BaseIndex.java @@ -129,16 +129,15 @@ public abstract class BaseIndex, KEY extends Serial @Override public DOMAIN next() { - if (hits.isEmpty()) { - fillQueue(); + if (!hasNext()) { + throw new NoSuchElementException(); } return toDoc(hits.poll().getSource()); } @Override public void remove() { - throw new IllegalStateException("Cannot remove item from scroll Iterable!!!" + - " Use Service or DAO classes instead"); + throw new UnsupportedOperationException("Cannot remove item from scroll"); } }; } @@ -195,7 +194,7 @@ public abstract class BaseIndex, KEY extends Serial .setQuery(QueryBuilders.filteredQuery( QueryBuilders.matchAllQuery(), getLastSynchronizationBuilder(params) - )) + )) .setSize(0) .addAggregation(AggregationBuilders.max("latest") .field(BaseNormalizer.UPDATED_AT_FIELD)); @@ -204,7 +203,7 @@ public abstract class BaseIndex, KEY extends Serial Max max = response.getAggregations().get("latest"); if (max.getValue() > 0) { - Date date = new DateTime(Double.valueOf(max.getValue()).longValue()).toDate(); + Date date = new DateTime((long) max.getValue()).toDate(); LOG.debug("Index {}:{} has last update of {}", this.getIndexName(), this.getIndexType(), date); return date; } else { diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java index 426ba550e72..2e3a6348466 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/debt/SqaleRatingDecorator.java @@ -44,7 +44,7 @@ import java.util.List; /** * Decorator that computes Sqale Rating metric */ -public final class SqaleRatingDecorator implements Decorator { +public class SqaleRatingDecorator implements Decorator { private final SqaleRatingSettings sqaleRatingSettings; private final Metric[] metrics; diff --git a/sonar-core/src/main/java/org/sonar/core/i18n/DurationLabel.java b/sonar-core/src/main/java/org/sonar/core/i18n/DurationLabel.java index 2e3b9a0debd..4ec0d8db26d 100644 --- a/sonar-core/src/main/java/org/sonar/core/i18n/DurationLabel.java +++ b/sonar-core/src/main/java/org/sonar/core/i18n/DurationLabel.java @@ -52,15 +52,15 @@ class DurationLabel { } else if (nbHours < 48) { return message("day"); } else if (nbDays < 30) { - return message("days", Double.valueOf(Math.floor(nbDays)).longValue()); + return message("days", (long)(Math.floor(nbDays))); } else if (nbDays < 60) { return message( "month"); } else if (nbDays < 365) { - return message("months", Double.valueOf(Math.floor(nbDays / 30)).longValue()); + return message("months", (long)(Math.floor(nbDays / 30))); } else if (nbYears < 2) { return message("year"); } - return message("years", Double.valueOf(Math.floor(nbYears)).longValue()); + return message("years", (long)(Math.floor(nbYears))); } private static Result message(String key) { diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java index 2d97a4160d7..59e6e4b10c3 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java @@ -23,7 +23,7 @@ import org.apache.ibatis.annotations.Param; import org.sonar.core.rule.RuleDto; import javax.annotation.Nullable; -import java.util.Date; + import java.util.List; public interface IssueMapper { -- cgit v1.2.3 From 1f52154068f1750bcce6cf41715ac056e12372dc Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 12 Feb 2015 11:22:17 +0100 Subject: Fix quality flaws --- .../server/db/migrations/v51/FeedFileSourcesBinaryData.java | 6 +++--- .../server/component/DefaultRubyComponentServiceTest.java | 2 +- .../main/java/org/sonar/core/plugins/PluginClassloaders.java | 2 +- .../src/main/java/org/sonar/core/rule/CacheRuleFinder.java | 2 +- .../src/test/java/org/sonar/core/resource/ResourceDaoTest.java | 2 +- .../main/java/org/sonar/api/checks/AnnotationCheckFactory.java | 2 +- .../src/main/java/net/sourceforge/pmd/cpd/Tokens.java | 10 +++++----- .../src/main/java/org/sonar/api/batch/ProjectClasspath.java | 2 +- .../src/main/java/org/sonar/api/component/Component.java | 2 +- .../src/main/java/org/sonar/api/component/Module.java | 2 +- .../src/main/java/org/sonar/api/component/Perspective.java | 2 +- .../src/main/java/org/sonar/api/component/SourceFile.java | 2 +- .../src/main/java/org/sonar/api/profiles/RulesProfile.java | 2 +- 13 files changed, 19 insertions(+), 19 deletions(-) (limited to 'sonar-core') diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java index b80abfd3789..ec57d336812 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/FeedFileSourcesBinaryData.java @@ -36,8 +36,8 @@ import org.sonar.server.db.migrations.Select; import org.sonar.server.db.migrations.SqlStatement; import org.sonar.server.source.db.FileSourceDb; +import javax.annotation.Nonnull; import javax.annotation.Nullable; - import java.sql.SQLException; import java.util.Iterator; @@ -154,9 +154,9 @@ public class FeedFileSourcesBinaryData extends BaseDataChange { } private static Iterable splitIntegers(String s) { - return Iterables.transform(Splitter.on(',').split(s), new Function() { + return Iterables.transform(Splitter.on(',').omitEmptyStrings().trimResults().split(s), new Function() { @Override - public Integer apply(String input) { + public Integer apply(@Nonnull String input) { return Integer.parseInt(input); } }); diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java index c05ad5ac675..12a7625cf80 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java @@ -57,7 +57,7 @@ public class DefaultRubyComponentServiceTest { @Test public void find_by_key() { - Component component = mock(Component.class); + Component component = mock(Component.class); when(resourceDao.findByKey("struts")).thenReturn(component); assertThat(service.findByKey("struts")).isEqualTo(component); diff --git a/sonar-core/src/main/java/org/sonar/core/plugins/PluginClassloaders.java b/sonar-core/src/main/java/org/sonar/core/plugins/PluginClassloaders.java index 3565ee5639d..829af0cab7c 100644 --- a/sonar-core/src/main/java/org/sonar/core/plugins/PluginClassloaders.java +++ b/sonar-core/src/main/java/org/sonar/core/plugins/PluginClassloaders.java @@ -240,7 +240,7 @@ public class PluginClassloaders { } public void clean() { - for (ClassRealm realm : (Collection) world.getRealms()) { + for (ClassRealm realm : world.getRealms()) { try { world.disposeRealm(realm.getId()); } catch (Exception e) { diff --git a/sonar-core/src/main/java/org/sonar/core/rule/CacheRuleFinder.java b/sonar-core/src/main/java/org/sonar/core/rule/CacheRuleFinder.java index 96aab429ea5..564acb64baa 100644 --- a/sonar-core/src/main/java/org/sonar/core/rule/CacheRuleFinder.java +++ b/sonar-core/src/main/java/org/sonar/core/rule/CacheRuleFinder.java @@ -90,7 +90,7 @@ public final class CacheRuleFinder implements RuleFinder { Hibernate.initialize(rule.getParams()); } - protected final Rule doFindById(int ruleId) { + private Rule doFindById(int ruleId) { DatabaseSession session = sessionFactory.getSession(); return session.getSingleResult( session.createQuery("FROM " + Rule.class.getSimpleName() + " r WHERE r.id=:id and r.status<>:status") diff --git a/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java b/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java index ef6948a8c21..56b56b5fe15 100644 --- a/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java @@ -345,7 +345,7 @@ public class ResourceDaoTest extends AbstractDaoTestCase { setupData("fixture"); assertThat(dao.findByKey("org.struts:struts")).isNotNull(); - Component component = dao.findByKey("org.struts:struts-core:src/org/struts/RequestContext.java"); + Component component = dao.findByKey("org.struts:struts-core:src/org/struts/RequestContext.java"); assertThat(component).isNotNull(); assertThat(component.path()).isEqualTo("src/org/struts/RequestContext.java"); assertThat(dao.findByKey("unknown")).isNull(); diff --git a/sonar-deprecated/src/main/java/org/sonar/api/checks/AnnotationCheckFactory.java b/sonar-deprecated/src/main/java/org/sonar/api/checks/AnnotationCheckFactory.java index 5ea3909e9a4..1629acf498f 100644 --- a/sonar-deprecated/src/main/java/org/sonar/api/checks/AnnotationCheckFactory.java +++ b/sonar-deprecated/src/main/java/org/sonar/api/checks/AnnotationCheckFactory.java @@ -67,7 +67,7 @@ public final class AnnotationCheckFactory extends CheckFactory { } @Override - protected Object createCheck(ActiveRule activeRule) { + public Object createCheck(ActiveRule activeRule) { Object object = checksByKey.get(activeRule.getConfigKey()); if (object != null) { return instantiate(activeRule, object); diff --git a/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index d7fbe3303d4..7bdf7c64364 100644 --- a/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -34,22 +34,22 @@ import java.util.List; */ public class Tokens { - private List tokens = new ArrayList(); + private List entries = new ArrayList<>(); public void add(TokenEntry tokenEntry) { - this.tokens.add(tokenEntry); + this.entries.add(tokenEntry); } public Iterator iterator() { - return tokens.iterator(); + return entries.iterator(); } public int size() { - return tokens.size(); + return entries.size(); } public List getTokens() { - return tokens; + return entries; } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/ProjectClasspath.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/ProjectClasspath.java index 6af187f6315..f7d55073bc5 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/ProjectClasspath.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/ProjectClasspath.java @@ -80,7 +80,7 @@ public class ProjectClasspath implements BatchComponent { try { List files = Lists.newArrayList(); if (pom.getCompileClasspathElements() != null) { - for (String classPathString : (List) pom.getCompileClasspathElements()) { + for (String classPathString : pom.getCompileClasspathElements()) { files.add(new File(classPathString)); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/component/Component.java b/sonar-plugin-api/src/main/java/org/sonar/api/component/Component.java index e68ed4cfa42..098b469a172 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/component/Component.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/component/Component.java @@ -21,7 +21,7 @@ package org.sonar.api.component; import javax.annotation.CheckForNull; -public interface Component { +public interface Component { String key(); /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/component/Module.java b/sonar-plugin-api/src/main/java/org/sonar/api/component/Module.java index 6fe58004867..c8fa4d4f62b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/component/Module.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/component/Module.java @@ -19,7 +19,7 @@ */ package org.sonar.api.component; -public interface Module extends Component { +public interface Module extends Component { String getDescription(); String getBranch(); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/component/Perspective.java b/sonar-plugin-api/src/main/java/org/sonar/api/component/Perspective.java index a1cc5af2bad..9f5395c0a02 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/component/Perspective.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/component/Perspective.java @@ -19,6 +19,6 @@ */ package org.sonar.api.component; -public interface Perspective> { +public interface Perspective { C component(); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/component/SourceFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/component/SourceFile.java index 37347d5f29d..702b817084a 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/component/SourceFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/component/SourceFile.java @@ -19,5 +19,5 @@ */ package org.sonar.api.component; -public interface SourceFile extends Component { +public interface SourceFile extends Component { } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java index 45b4053dbfe..15f8bd4b4cf 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java @@ -133,7 +133,7 @@ public class RulesProfile implements Cloneable { } /** - * @deprecated profile versioning is dropped in 4.4. Always returns -1. + * @deprecated profile versioning is dropped in 4.4. Always returns null. */ @CheckForNull @Deprecated -- cgit v1.2.3 From fa0b5ce4d701431df47885059c3fb9c4922aa33d Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 12 Feb 2015 10:26:46 +0100 Subject: Add unit tests --- .../org/sonar/core/persistence/DaoUtilsTest.java | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'sonar-core') diff --git a/sonar-core/src/test/java/org/sonar/core/persistence/DaoUtilsTest.java b/sonar-core/src/test/java/org/sonar/core/persistence/DaoUtilsTest.java index db891b609ec..03e1d41d380 100644 --- a/sonar-core/src/test/java/org/sonar/core/persistence/DaoUtilsTest.java +++ b/sonar-core/src/test/java/org/sonar/core/persistence/DaoUtilsTest.java @@ -19,11 +19,16 @@ */ package org.sonar.core.persistence; +import com.google.common.base.Function; +import com.google.common.collect.Iterables; import org.junit.Test; +import java.util.Collections; import java.util.List; +import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class DaoUtilsTest { @@ -38,6 +43,44 @@ public class DaoUtilsTest { public void repeatCondition() throws Exception { assertThat(DaoUtils.repeatCondition("uuid=?", 1, "or")).isEqualTo("uuid=?"); assertThat(DaoUtils.repeatCondition("uuid=?", 3, "or")).isEqualTo("uuid=? or uuid=? or uuid=?"); + } + + @Test + public void execute_large_inputs() throws Exception { + List inputs = newArrayList(); + List expectedOutputs = newArrayList(); + for (int i = 0; i < 2010; i++) { + inputs.add(i); + expectedOutputs.add(Integer.toString(i)); + } + + List outputs = DaoUtils.executeLargeInputs(inputs, new Function, List>() { + @Override + public List apply(List input) { + // Check that each partition is only done on 1000 elements max + assertThat(input.size()).isLessThanOrEqualTo(1000); + return newArrayList(Iterables.transform(input, new Function() { + @Override + public String apply(Integer input) { + return Integer.toString(input); + } + })); + } + }); + + assertThat(outputs).isEqualTo(expectedOutputs); + } + + @Test + public void execute_large_inputs_on_empty_list() throws Exception { + List outputs = DaoUtils.executeLargeInputs(Collections.emptyList(), new Function, List>() { + @Override + public List apply(List input) { + fail("No partition should be made on empty list"); + return Collections.emptyList(); + } + }); + assertThat(outputs).isEmpty(); } } -- cgit v1.2.3 From 33ad2136864fd9cb1143ab6524c9ef89810cc967 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lievremont Date: Thu, 12 Feb 2015 11:20:11 +0100 Subject: SONAR-6119 Move module UUID path migration, include self on parents --- .../v51/UpdateProjectsModuleUuidPath.java | 83 ++++++++++++++-------- .../migrate_components-result.xml | 52 ++++++++++++-- .../migrate_components.xml | 46 +++++++++++- .../not_migrate_already_migrated_components.xml | 50 +++++++++++-- .../767_update_projects_module_uuid_path.rb | 30 -------- .../790_update_projects_module_uuid_path.rb | 31 ++++++++ .../sonar/core/persistence/DatabaseVersion.java | 2 +- .../org/sonar/core/persistence/rows-h2.sql | 2 +- 8 files changed, 225 insertions(+), 71 deletions(-) delete mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/767_update_projects_module_uuid_path.rb create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/790_update_projects_module_uuid_path.rb (limited to 'sonar-core') diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPath.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPath.java index 468887c322b..9aba4c963a2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPath.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPath.java @@ -21,11 +21,9 @@ package org.sonar.server.db.migrations.v51; import org.sonar.core.persistence.Database; -import org.sonar.server.db.migrations.BaseDataChange; -import org.sonar.server.db.migrations.MassUpdate; +import org.sonar.server.db.migrations.*; import org.sonar.server.db.migrations.MassUpdate.Handler; import org.sonar.server.db.migrations.Select.Row; -import org.sonar.server.db.migrations.SqlStatement; import javax.annotation.Nullable; @@ -33,6 +31,7 @@ import java.sql.SQLException; /** * SONAR-6054 + * SONAR-6119 */ public class UpdateProjectsModuleUuidPath extends BaseDataChange { @@ -44,37 +43,65 @@ public class UpdateProjectsModuleUuidPath extends BaseDataChange { @Override public void execute(Context context) throws SQLException { - MassUpdate update = context.prepareMassUpdate().rowPluralName("projects"); - update.select("SELECT p.id, p.module_uuid_path FROM projects p"); + MassUpdate update = context.prepareMassUpdate().rowPluralName("components"); + update.select("SELECT p.id, p.module_uuid_path, p.uuid, p.scope, p.qualifier FROM projects p"); update.update("UPDATE projects SET module_uuid_path=? WHERE id=?"); - update.execute(new Handler() { - @Override - public boolean handle(Row row, SqlStatement update) throws SQLException { - Long id = row.getLong(1); - String moduleUuidPath = row.getString(2); - if (needUpdate(moduleUuidPath)) { - update.setString(1, newModuleUuidPath(moduleUuidPath)); - update.setLong(2, id); - return true; - } - return false; - } - }); + update.execute(new ModuleUuidPathUpdateHandler()); } - private static boolean needUpdate(@Nullable String moduleUuidPath) { - return moduleUuidPath == null || !(moduleUuidPath.startsWith(SEP) && moduleUuidPath.endsWith(SEP)); - } + private static final class ModuleUuidPathUpdateHandler implements Handler { + @Override + public boolean handle(Row row, SqlStatement update) throws SQLException { + Long id = row.getLong(1); + String moduleUuidPath = row.getString(2); + String uuid = row.getString(3); + String scope = row.getString(4); + String qualifier = row.getString(5); + + boolean needUpdate = false; + String newModuleUuidPath = moduleUuidPath; + + if (needUpdateForSeparators(moduleUuidPath)) { + newModuleUuidPath = newModuleUuidPathWithSeparators(moduleUuidPath); + needUpdate = true; + } + + if (needUpdateToIncludeItself(newModuleUuidPath, uuid, scope, qualifier)) { + newModuleUuidPath = newModuleUuidPathIncludingItself(newModuleUuidPath, uuid); + needUpdate = true; + } + + if (needUpdate) { + update.setString(1, newModuleUuidPath); + update.setLong(2, id); + } + return needUpdate; + } + + private static boolean needUpdateForSeparators(@Nullable String moduleUuidPath) { + return moduleUuidPath == null || !(moduleUuidPath.startsWith(SEP) && moduleUuidPath.endsWith(SEP)); + } - private static String newModuleUuidPath(@Nullable String oldModuleUuidPath) { - if (oldModuleUuidPath == null || oldModuleUuidPath.isEmpty()) { - return SEP; - } else { - StringBuilder newModuleUuidPath = new StringBuilder(oldModuleUuidPath); - newModuleUuidPath.insert(0, SEP); + private static String newModuleUuidPathWithSeparators(@Nullable String oldModuleUuidPath) { + if (oldModuleUuidPath == null || oldModuleUuidPath.isEmpty()) { + return SEP; + } else { + StringBuilder newModuleUuidPath = new StringBuilder(oldModuleUuidPath); + newModuleUuidPath.insert(0, SEP); + newModuleUuidPath.append(SEP); + return newModuleUuidPath.toString(); + } + } + + private static boolean needUpdateToIncludeItself(String moduleUuidPath, @Nullable String uuid, @Nullable String scope, @Nullable String qualifier) { + return "PRJ".equals(scope) && !("DEV_PRJ".equals(qualifier)) && !(moduleUuidPath.contains(uuid)); + } + + private static String newModuleUuidPathIncludingItself(String moduleUuidPath, String uuid) { + StringBuilder newModuleUuidPath = new StringBuilder(moduleUuidPath); + newModuleUuidPath.append(uuid); newModuleUuidPath.append(SEP); return newModuleUuidPath.toString(); } } - } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/migrate_components-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/migrate_components-result.xml index 3f568ad6d41..86d58cac2e9 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/migrate_components-result.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/migrate_components-result.xml @@ -2,21 +2,21 @@ - + @@ -38,11 +38,53 @@ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="src/org/struts/RequestContext.java" created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/> + + + + + + + + + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/migrate_components.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/migrate_components.xml index 1a99736dfd9..355ec9308e7 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/migrate_components.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/migrate_components.xml @@ -14,9 +14,9 @@ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/> - + @@ -38,6 +38,34 @@ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="src/org/struts/RequestContext.java" created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/> + + + + + + + + + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/not_migrate_already_migrated_components.xml b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/not_migrate_already_migrated_components.xml index 3f568ad6d41..4455653fdfd 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/not_migrate_already_migrated_components.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/db/migrations/v51/UpdateProjectsModuleUuidPathTest/not_migrate_already_migrated_components.xml @@ -2,21 +2,21 @@ @@ -38,11 +38,53 @@ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="src/org/struts/RequestContext.java" created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/> + + + + + + + + + + + + + + + + + + diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/767_update_projects_module_uuid_path.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/767_update_projects_module_uuid_path.rb deleted file mode 100644 index ede79b41641..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/767_update_projects_module_uuid_path.rb +++ /dev/null @@ -1,30 +0,0 @@ -# -# SonarQube, open source software quality management tool. -# Copyright (C) 2008-2014 SonarSource -# mailto:contact AT sonarsource DOT com -# -# SonarQube is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 3 of the License, or (at your option) any later version. -# -# SonarQube is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# - -# -# SonarQube 5.1 -# SONAR-6054 -# -class UpdateProjectsModuleUuidPath < ActiveRecord::Migration - - def self.up - execute_java_migration('org.sonar.server.db.migrations.v51.UpdateProjectsModuleUuidPath') - end -end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/790_update_projects_module_uuid_path.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/790_update_projects_module_uuid_path.rb new file mode 100644 index 00000000000..b25be54b77b --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/790_update_projects_module_uuid_path.rb @@ -0,0 +1,31 @@ +# +# 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. +# + +# +# SonarQube 5.1 +# SONAR-6054 +# SONAR-6117 +# +class UpdateProjectsModuleUuidPath < ActiveRecord::Migration + + def self.up + execute_java_migration('org.sonar.server.db.migrations.v51.UpdateProjectsModuleUuidPath') + end +end diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java index fc7327dbcb6..048e602afe9 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java @@ -33,7 +33,7 @@ import java.util.List; */ public class DatabaseVersion implements BatchComponent, ServerComponent { - public static final int LAST_VERSION = 789; + public static final int LAST_VERSION = 790; /** * List of all the tables.n diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql index eceb883ec31..af6b0b2df00 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql @@ -295,7 +295,6 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('763'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('764'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('765'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('766'); -INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('767'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('768'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('769'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('770'); @@ -317,6 +316,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('786'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('787'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('788'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('789'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('790'); INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482', null, null); ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; -- cgit v1.2.3 From d11e107895b68fbf056523f6551903c9c02021fb Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Fri, 13 Feb 2015 10:40:05 +0100 Subject: SONAR-6122 Update /batch/projects WS with new module UUID path --- .../server/component/db/ComponentDaoTest/delete-result.xml | 4 ++-- .../component/db/ComponentDaoTest/find_project_uuids.xml | 4 ++-- .../server/component/db/ComponentDaoTest/multi-modules.xml | 6 +++--- .../db/ComponentDaoTest/select_module_files_tree.xml | 6 +++--- .../sonar/server/component/db/ComponentDaoTest/shared.xml | 4 ++-- .../server/component/db/ComponentDaoTest/shared_views.xml | 12 ++++++------ .../org/sonar/core/component/db/ComponentIndexMapper.java | 2 +- .../org/sonar/core/component/db/ComponentIndexMapper.xml | 2 +- .../org/sonar/core/component/db/ComponentMapper.xml | 11 +++++------ 9 files changed, 25 insertions(+), 26 deletions(-) (limited to 'sonar-core') diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/delete-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/delete-result.xml index 3794b036623..d8b3fcdfe0a 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/delete-result.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/delete-result.xml @@ -6,7 +6,7 @@ diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/multi-modules.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/multi-modules.xml index 9ba70bee6f4..6f68d6d45cd 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/multi-modules.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/multi-modules.xml @@ -2,7 +2,7 @@ @@ -28,7 +28,7 @@ diff --git a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/shared.xml index 4474f2ebdea..45d8372c00b 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/shared.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/component/db/ComponentDaoTest/shared.xml @@ -6,7 +6,7 @@ - - - - selectProjectIdsFromQueryAndViewOrSubViewUuid(@Param("query") String query, @Param("viewOrSubViewUuid") String viewOrSubViewUuid); + List selectProjectIdsFromQueryAndViewOrSubViewUuid(@Param("query") String query, @Param("viewUuidQuery") String viewUuidQuery); } diff --git a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentIndexMapper.xml b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentIndexMapper.xml index 6b80ef94aac..1e88b4100b9 100644 --- a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentIndexMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentIndexMapper.xml @@ -6,7 +6,7 @@ SELECT r.resource_id FROM resource_index r INNER JOIN projects copy ON copy.copy_resource_id = r.resource_id - AND copy.module_uuid_path LIKE #{viewOrSubViewUuid} + AND copy.module_uuid_path LIKE #{viewUuidQuery} AND r.kee LIKE #{query} ORDER BY r.name_size diff --git a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml index ee96cd81563..e48052debb5 100644 --- a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml @@ -143,23 +143,22 @@ INNER JOIN projects root_project ON root_project.uuid = p.project_uuid AND root_project.enabled = ${_true} - INNER JOIN projects module ON module.project_uuid = root_project.uuid AND module.enabled = ${_true} AND module.uuid = #{moduleUuid} + INNER JOIN projects module ON module.project_uuid = root_project.uuid AND module.enabled = ${_true} AND module.uuid = #{moduleUuid} AND module.scope='PRJ' p.enabled = ${_true} AND p.scope = #{scope} - AND (p.uuid = module.uuid OR + AND - p.module_uuid_path LIKE module.module_uuid_path + module.uuid + '.%' + p.module_uuid_path LIKE module.module_uuid_path + '%' - p.module_uuid_path LIKE concat(module.module_uuid_path, module.uuid, '.%') + p.module_uuid_path LIKE concat(module.module_uuid_path, '%') - p.module_uuid_path LIKE module.module_uuid_path || module.uuid || '.%' + p.module_uuid_path LIKE module.module_uuid_path || '%' - ) -- cgit v1.2.3 From 13c31ee241629122418aba38f6da034f3a288d87 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Fri, 13 Feb 2015 11:21:31 +0100 Subject: SONAR-6123 Update generation of module UUID path on views and developers --- .../sonar/server/component/ComponentService.java | 10 ++++----- .../component/ComponentServiceMediumTest.java | 26 +++++++++++++--------- .../main/java/org/sonar/core/user/AuthorDao.java | 7 +++--- .../add_missing_module_uuid_path-result.xml | 4 ++-- 4 files changed, 25 insertions(+), 22 deletions(-) (limited to 'sonar-core') diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java index b1c13be5985..c2a045f5d37 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java @@ -106,11 +106,10 @@ public class ComponentService implements ServerComponent { } public void updateKey(String projectOrModuleKey, String newKey) { - UserSession.get().checkComponentPermission(UserRole.ADMIN, projectOrModuleKey); - DbSession session = dbClient.openSession(false); try { ComponentDto projectOrModule = getByKey(session, projectOrModuleKey); + UserSession.get().checkProjectUuidPermission(UserRole.ADMIN, projectOrModule.projectUuid()); resourceKeyUpdaterDao.updateKey(projectOrModule.getId(), newKey); session.commit(); @@ -121,10 +120,10 @@ public class ComponentService implements ServerComponent { } public Map checkModuleKeysBeforeRenaming(String projectKey, String stringToReplace, String replacementString) { - UserSession.get().checkProjectPermission(UserRole.ADMIN, projectKey); DbSession session = dbClient.openSession(false); try { ComponentDto project = getByKey(projectKey); + UserSession.get().checkProjectUuidPermission(UserRole.ADMIN, project.projectUuid()); return resourceKeyUpdaterDao.checkModuleKeysBeforeRenaming(project.getId(), stringToReplace, replacementString); } finally { session.close(); @@ -132,12 +131,11 @@ public class ComponentService implements ServerComponent { } public void bulkUpdateKey(String projectKey, String stringToReplace, String replacementString) { - UserSession.get().checkProjectPermission(UserRole.ADMIN, projectKey); - // Open a batch session DbSession session = dbClient.openSession(true); try { ComponentDto project = getByKey(session, projectKey); + UserSession.get().checkProjectUuidPermission(UserRole.ADMIN, project.projectUuid()); resourceKeyUpdaterDao.bulkUpdateKey(session, project.getId(), stringToReplace, replacementString); session.commit(); } finally { @@ -163,6 +161,8 @@ public class ComponentService implements ServerComponent { ComponentDto component = dbClient.componentDao().insert(session, new ComponentDto() .setUuid(uuid) + .setModuleUuid(null) + .setModuleUuidPath(ComponentDto.MODULE_UUID_PATH_SEP + uuid + ComponentDto.MODULE_UUID_PATH_SEP) .setProjectUuid(uuid) .setKey(keyWithBranch) .setDeprecatedKey(keyWithBranch) diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceMediumTest.java index a4ee4d1150c..3ae19ea4b37 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceMediumTest.java @@ -48,6 +48,10 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; +/** + * It's not possible to replace usage of ServerTester by DbTester because of the usage of {@link InternalPermissionService}. + * Maybe we should create a mock of the permission service ? + */ public class ComponentServiceMediumTest { @ClassRule @@ -104,7 +108,7 @@ public class ComponentServiceMediumTest { session.commit(); - MockUserSession.set().setLogin("john").addComponentPermission(UserRole.ADMIN, project.key(), project.key()); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); service.updateKey(project.key(), "sample2:root"); session.commit(); @@ -128,7 +132,7 @@ public class ComponentServiceMediumTest { session.commit(); - MockUserSession.set().setLogin("john").addComponentPermission(UserRole.ADMIN, project.key(), module.key()); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); service.updateKey(module.key(), "sample:root2:module"); session.commit(); @@ -151,7 +155,7 @@ public class ComponentServiceMediumTest { session.commit(); - MockUserSession.set().setLogin("john").addComponentPermission(UserRole.ADMIN, provisionedProject.key(), provisionedProject.key()); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.ADMIN, provisionedProject.uuid()); service.updateKey(provisionedProject.key(), "provisionedProject2"); session.commit(); @@ -163,7 +167,7 @@ public class ComponentServiceMediumTest { @Test(expected = ForbiddenException.class) public void fail_to_update_project_key_without_admin_permission() throws Exception { ComponentDto project = createProject("sample:root"); - MockUserSession.set().setLogin("john").addComponentPermission(UserRole.USER, project.key(), project.key()); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, project.uuid()); service.updateKey(project.key(), "sample2:root"); } @@ -178,7 +182,7 @@ public class ComponentServiceMediumTest { session.commit(); - MockUserSession.set().setLogin("john").addProjectPermissions(UserRole.ADMIN, project.key()); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); Map result = service.checkModuleKeysBeforeRenaming(project.key(), "sample", "sample2"); assertThat(result).hasSize(2); @@ -197,7 +201,7 @@ public class ComponentServiceMediumTest { session.commit(); - MockUserSession.set().setLogin("john").addProjectPermissions(UserRole.ADMIN, project.key()); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); Map result = service.checkModuleKeysBeforeRenaming(project.key(), "sample:root", "foo"); assertThat(result).hasSize(2); @@ -208,7 +212,7 @@ public class ComponentServiceMediumTest { @Test(expected = ForbiddenException.class) public void fail_to_check_module_keys_before_renaming_without_admin_permission() throws Exception { ComponentDto project = createProject("sample:root"); - MockUserSession.set().setLogin("john").addComponentPermission(UserRole.USER, project.key(), project.key()); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.USER, project.uuid()); service.checkModuleKeysBeforeRenaming(project.key(), "sample", "sample2"); } @@ -223,7 +227,7 @@ public class ComponentServiceMediumTest { session.commit(); - MockUserSession.set().setLogin("john").addProjectPermissions(UserRole.ADMIN, project.key()); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); service.bulkUpdateKey(project.key(), "sample", "sample2"); session.commit(); @@ -247,7 +251,7 @@ public class ComponentServiceMediumTest { session.commit(); - MockUserSession.set().setLogin("john").addComponentPermission(UserRole.ADMIN, provisionedProject.key(), provisionedProject.key()); + MockUserSession.set().setLogin("john").addProjectUuidPermissions(UserRole.ADMIN, provisionedProject.uuid()); service.bulkUpdateKey(provisionedProject.key(), "provisionedProject", "provisionedProject2"); session.commit(); @@ -276,7 +280,7 @@ public class ComponentServiceMediumTest { assertThat(project.uuid()).isNotNull(); assertThat(project.projectUuid()).isEqualTo(project.uuid()); assertThat(project.moduleUuid()).isNull(); - assertThat(project.moduleUuidPath()).isNull(); + assertThat(project.moduleUuidPath()).isEqualTo("." + project.uuid() + "."); assertThat(project.name()).isEqualTo("Struts project"); assertThat(project.longName()).isEqualTo("Struts project"); assertThat(project.scope()).isEqualTo("PRJ"); @@ -309,7 +313,7 @@ public class ComponentServiceMediumTest { assertThat(project.uuid()).isNotNull(); assertThat(project.projectUuid()).isEqualTo(project.uuid()); assertThat(project.moduleUuid()).isNull(); - assertThat(project.moduleUuidPath()).isNull(); + assertThat(project.moduleUuidPath()).isEqualTo("." + project.uuid() + "."); assertThat(project.name()).isEqualTo("All Projects"); assertThat(project.longName()).isEqualTo("All Projects"); assertThat(project.scope()).isEqualTo("PRJ"); diff --git a/sonar-core/src/main/java/org/sonar/core/user/AuthorDao.java b/sonar-core/src/main/java/org/sonar/core/user/AuthorDao.java index a5a9756c8ee..7b8d140d282 100644 --- a/sonar-core/src/main/java/org/sonar/core/user/AuthorDao.java +++ b/sonar-core/src/main/java/org/sonar/core/user/AuthorDao.java @@ -19,14 +19,13 @@ */ package org.sonar.core.user; -import org.sonar.core.persistence.DaoComponent; - import com.google.common.base.Function; import com.google.common.base.Strings; import org.apache.ibatis.session.SqlSession; import org.sonar.api.BatchComponent; import org.sonar.api.ServerComponent; import org.sonar.core.component.ComponentDto; +import org.sonar.core.persistence.DaoComponent; import org.sonar.core.persistence.DaoUtils; import org.sonar.core.persistence.MyBatis; import org.sonar.core.resource.ResourceDao; @@ -84,9 +83,9 @@ public class AuthorDao implements BatchComponent, ServerComponent, DaoComponent public void insertAuthorAndDeveloper(String login, ResourceDto resourceDto) { SqlSession session = mybatis.openSession(false); try { - // Hack in order to set "." on DEV + // Hack in order to set the right module uuid path on DEVs if (Strings.isNullOrEmpty(resourceDto.getModuleUuidPath())) { - resourceDto.setModuleUuidPath(ComponentDto.MODULE_UUID_PATH_SEP); + resourceDto.setModuleUuidPath(ComponentDto.MODULE_UUID_PATH_SEP + resourceDto.getUuid() + ComponentDto.MODULE_UUID_PATH_SEP); } resourceDao.insertUsingExistingSession(resourceDto, session); insertAuthor(login, resourceDto.getId(), session); diff --git a/sonar-core/src/test/resources/org/sonar/core/user/AuthorDaoTest/add_missing_module_uuid_path-result.xml b/sonar-core/src/test/resources/org/sonar/core/user/AuthorDaoTest/add_missing_module_uuid_path-result.xml index b8737c88118..742ae70faad 100644 --- a/sonar-core/src/test/resources/org/sonar/core/user/AuthorDaoTest/add_missing_module_uuid_path-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/user/AuthorDaoTest/add_missing_module_uuid_path-result.xml @@ -1,7 +1,7 @@ - + - + -- cgit v1.2.3 From 94c01944ba8e03b3206ace25853e84094cbacfda Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Fri, 13 Feb 2015 17:02:44 +0100 Subject: SONAR-6117 ComponentDto.isRootProject() should not used module uuid path --- .../batch/IssuesActionTest/return_only_manual_severity.xml | 4 ++-- .../resources/org/sonar/server/batch/IssuesActionTest/shared.xml | 4 ++-- .../src/main/java/org/sonar/core/component/ComponentDto.java | 2 +- .../src/test/java/org/sonar/core/component/ComponentDtoTest.java | 9 +++++++++ 4 files changed, 14 insertions(+), 5 deletions(-) (limited to 'sonar-core') diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/return_only_manual_severity.xml b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/return_only_manual_severity.xml index e9067a6ca97..1143952cf72 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/return_only_manual_severity.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/return_only_manual_severity.xml @@ -1,10 +1,10 @@ - + - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml index cdeb8b848f7..8154b366410 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml @@ -2,11 +2,11 @@ + module_uuid="[null]" module_uuid_path=".ABCD." path="[null]"/> - diff --git a/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java b/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java index ecad07f8bbd..fe72cc88037 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java +++ b/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java @@ -235,7 +235,7 @@ public class ComponentDto extends Dto implements Component { } public boolean isRootProject() { - return MODULE_UUID_PATH_SEP.equals(moduleUuidPath) && Scopes.PROJECT.equals(scope); + return moduleUuid == null && Scopes.PROJECT.equals(scope); } @Override diff --git a/sonar-core/src/test/java/org/sonar/core/component/ComponentDtoTest.java b/sonar-core/src/test/java/org/sonar/core/component/ComponentDtoTest.java index 52fe57359e7..172164cb1c2 100644 --- a/sonar-core/src/test/java/org/sonar/core/component/ComponentDtoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/component/ComponentDtoTest.java @@ -21,6 +21,8 @@ package org.sonar.core.component; import org.junit.Test; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.Scopes; import static org.assertj.core.api.Assertions.assertThat; @@ -70,4 +72,11 @@ public class ComponentDtoTest { assertThat(dto.hashCode()).isEqualTo(dtoWithSameId.hashCode()); assertThat(dto.hashCode()).isNotEqualTo(dtoWithDifferentId.hashCode()); } + + @Test + public void is_root_project() throws Exception { + assertThat(new ComponentDto().setModuleUuid("ABCD").isRootProject()).isFalse(); + assertThat(new ComponentDto().setModuleUuid("ABCD").setScope(Scopes.DIRECTORY).isRootProject()).isFalse(); + assertThat(new ComponentDto().setModuleUuid(null).setScope(Scopes.PROJECT).setQualifier(Qualifiers.PROJECT).isRootProject()).isTrue(); + } } -- cgit v1.2.3 From 24839b644682909ac76a5736c0ff474e0a89daa1 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lievremont Date: Wed, 11 Feb 2015 11:45:04 +0100 Subject: Fix quality flaws --- .../src/main/java/org/sonar/server/search/NodeHealth.java | 10 +++++++--- .../java/org/sonar/batch/qualitygate/QualityGateProvider.java | 2 +- .../org/sonar/core/qualitygate/db/QualityGateConditionDto.java | 2 +- .../main/java/org/sonar/markdown/HtmlBlockquoteChannel.java | 6 +++--- 4 files changed, 12 insertions(+), 8 deletions(-) (limited to 'sonar-core') diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/NodeHealth.java b/server/sonar-server/src/main/java/org/sonar/server/search/NodeHealth.java index 722be550697..e10254c4894 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/NodeHealth.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/NodeHealth.java @@ -107,7 +107,7 @@ public class NodeHealth { } public String getProcessCpuPercent() { - return formatPercent(Long.valueOf(cpuPercent), 100L); + return formatPercent(cpuPercent, 100L); } void setOpenFiles(long avgOpenFileDescriptors) { @@ -175,6 +175,10 @@ public class NodeHealth { // Uptime setJvmUptimeMillis(nodesStats.getJvm().getUptime().getMillis()); + initPerformanceStats(nodesStats); + } + + private void initPerformanceStats(NodeStats nodesStats) { // Performance Stat performanceStats = new ArrayList(); @@ -249,13 +253,13 @@ public class NodeHealth { .setValue(filterCacheEviction)); } - static public class Performance { + public static class Performance { public static enum Status { OK, WARN, ERROR } - final private String name; + private final String name; private String message; private double value; private double warnThreshold; diff --git a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateProvider.java b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateProvider.java index 5333c767ee1..4ede40d4ed9 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateProvider.java @@ -84,7 +84,7 @@ public class QualityGateProvider extends ProviderAdapter { private QualityGate fetch(String qualityGateSetting, ServerClient client, MetricFinder metricFinder) { String jsonText = null; try { - long qGateId = Long.valueOf(qualityGateSetting); + long qGateId = Long.parseLong(qualityGateSetting); jsonText = client.request(SHOW_URL + "?id=" + qGateId, false); } catch (NumberFormatException configIsNameInsteadOfId) { jsonText = client.request(SHOW_URL + "?name=" + qualityGateSetting, false); diff --git a/sonar-core/src/main/java/org/sonar/core/qualitygate/db/QualityGateConditionDto.java b/sonar-core/src/main/java/org/sonar/core/qualitygate/db/QualityGateConditionDto.java index 7fc5fc7a6e6..008822ec40f 100644 --- a/sonar-core/src/main/java/org/sonar/core/qualitygate/db/QualityGateConditionDto.java +++ b/sonar-core/src/main/java/org/sonar/core/qualitygate/db/QualityGateConditionDto.java @@ -86,7 +86,7 @@ public class QualityGateConditionDto { private long metricId; - private transient String metricKey; + private String metricKey; private Integer period; diff --git a/sonar-markdown/src/main/java/org/sonar/markdown/HtmlBlockquoteChannel.java b/sonar-markdown/src/main/java/org/sonar/markdown/HtmlBlockquoteChannel.java index da1594d9a30..67acf60e22d 100644 --- a/sonar-markdown/src/main/java/org/sonar/markdown/HtmlBlockquoteChannel.java +++ b/sonar-markdown/src/main/java/org/sonar/markdown/HtmlBlockquoteChannel.java @@ -62,12 +62,12 @@ class HtmlBlockquoteChannel extends Channel { private class QuotedLineElementChannel extends RegexChannel { - protected QuotedLineElementChannel() { + private QuotedLineElementChannel() { super(">\\s[^\r\n]*+"); } @Override - protected void consume(CharSequence token, MarkdownOutput output) { + public void consume(CharSequence token, MarkdownOutput output) { if (!pendingBlockConstruction) { output.append("
"); pendingBlockConstruction = true; @@ -101,7 +101,7 @@ class HtmlBlockquoteChannel extends Channel { } @Override - protected void consume(CharSequence token, MarkdownOutput output) { + public void consume(CharSequence token, MarkdownOutput output) { output.append(token); } } -- cgit v1.2.3