diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-03-02 13:27:28 +0100 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-03-03 21:03:39 +0100 |
commit | 82faa625e3e6471335321b5d5fb2258809afaa13 (patch) | |
tree | 03cf62e4c1407fc10a25306aca48c3f6a5782401 /server | |
parent | b88fd7049dd271e115bbc1f48b49e5d830ee6ef9 (diff) | |
download | sonarqube-82faa625e3e6471335321b5d5fb2258809afaa13.tar.gz sonarqube-82faa625e3e6471335321b5d5fb2258809afaa13.zip |
SONAR-6229 Indexing of activity logs consumes too much memory during server startup
Diffstat (limited to 'server')
49 files changed, 1173 insertions, 1422 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/Activity.java b/server/sonar-server/src/main/java/org/sonar/server/activity/Activity.java new file mode 100644 index 00000000000..f0a50a40c5e --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/Activity.java @@ -0,0 +1,70 @@ +/* + * 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.activity; + +import javax.annotation.Nullable; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class Activity { + + public static enum Type { + QPROFILE, SERVER, ANALYSIS_REPORT + } + + private Type type; + private String action; + private String message; + private final Map<String,Object> data = new LinkedHashMap<>(); + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public String getMessage() { + return message; + } + + public void setMessage(@Nullable String message) { + this.message = message; + } + + public Map<String, Object> getData() { + return data; + } + + public Activity setData(String key, Object val) { + this.data.put(key, val); + return this; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/ActivityService.java b/server/sonar-server/src/main/java/org/sonar/server/activity/ActivityService.java index bb91bf20967..a108c0db063 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/activity/ActivityService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/ActivityService.java @@ -19,60 +19,33 @@ */ package org.sonar.server.activity; -import org.sonar.core.activity.Activity; -import org.sonar.core.activity.ActivityLog; +import org.sonar.api.ServerComponent; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.api.utils.internal.Uuids; import org.sonar.core.activity.db.ActivityDto; -import org.sonar.core.persistence.DbSession; -import org.sonar.server.activity.index.ActivityIndex; -import org.sonar.server.activity.index.ActivityQuery; +import org.sonar.server.activity.index.ActivityIndexer; import org.sonar.server.db.DbClient; -import org.sonar.server.search.IndexClient; -import org.sonar.server.search.QueryContext; -import org.sonar.server.search.Result; import org.sonar.server.user.UserSession; -import javax.annotation.Nullable; - -/** - * Log service is used to log Activity classes which represents an event to DB and Index. - * - * @see org.sonar.core.activity.ActivityLog - */ -public class ActivityService { +public class ActivityService implements ServerComponent { private final DbClient dbClient; - private final IndexClient indexClient; + private final ActivityIndexer indexer; - public ActivityService(DbClient dbClient, IndexClient indexClient) { + public ActivityService(DbClient dbClient, ActivityIndexer indexer) { this.dbClient = dbClient; - this.indexClient = indexClient; - } - - @Nullable - private String getAuthor() { - return (UserSession.get().login() != null) ? UserSession.get().login() : null; - } - - private void save(DbSession session, ActivityDto log) { - dbClient.activityDao().insert(session, - log.setAuthor(getAuthor())); - } - - public void write(DbSession session, Activity.Type type, String message) { - save(session, ActivityDto.createFor(message).setType(type)); - } - - public <L extends ActivityLog> void write(DbSession session, Activity.Type type, L log) { - this.save(session, ActivityDto.createFor(log) - .setType(type)); - } - - public ActivityQuery newActivityQuery() { - return new ActivityQuery(); + this.indexer = indexer; } - public Result<Activity> search(ActivityQuery query, QueryContext options) { - ActivityIndex index = indexClient.get(ActivityIndex.class); - return new Result<Activity>(index, index.search(query, options)); + public void save(Activity activity) { + ActivityDto dto = new ActivityDto() + .setKey(Uuids.create()) + .setAuthor(UserSession.get().login()) + .setAction(activity.getAction()) + .setMessage(activity.getMessage()) + .setData(KeyValueFormat.format(activity.getData())) + .setType(activity.getType().name()); + dbClient.activityDao().insert(dto); + indexer.index(); } } 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 744a3c05fd6..d0e3c7a0794 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 @@ -23,10 +23,10 @@ package org.sonar.server.activity; import org.picocontainer.Startable; import org.sonar.api.ServerComponent; import org.sonar.api.utils.Paging; +import org.sonar.server.es.SearchOptions; import org.sonar.server.qualityprofile.QProfileActivity; import org.sonar.server.qualityprofile.QProfileActivityQuery; import org.sonar.server.qualityprofile.QProfileService; -import org.sonar.server.search.QueryContext; import org.sonar.server.search.Result; import org.sonar.server.util.RubyUtils; @@ -51,11 +51,8 @@ public class RubyQProfileActivityService implements ServerComponent, Startable { */ public QProfileActivityResult search(Map<String, Object> params) { QProfileActivityQuery query = new QProfileActivityQuery(); - QueryContext queryContext = new QueryContext().setMaxLimit(); - List<String> profileKeys = RubyUtils.toStrings(params.get("profileKeys")); - if (profileKeys != null) { - query.setQprofileKeys(profileKeys); - } + + query.setQprofileKey((String)params.get("profileKey")); Date since = RubyUtils.toDate(params.get("since")); if (since != null) { query.setSince(since); @@ -64,12 +61,14 @@ public class RubyQProfileActivityService implements ServerComponent, Startable { if (to != null) { query.setTo(to); } + + SearchOptions options = new SearchOptions(); Integer page = RubyUtils.toInteger(params.get("p")); int pageIndex = page != null ? page : 1; - queryContext.setPage(pageIndex, 50); + options.setPage(pageIndex, 50); - Result<QProfileActivity> result = service.searchActivities(query, queryContext); - return new QProfileActivityResult(result.getHits(), Paging.create(queryContext.getLimit(), pageIndex, (int) result.getTotal())); + Result<QProfileActivity> result = service.searchActivities(query, options); + return new QProfileActivityResult(result.getHits(), Paging.create(options.getLimit(), pageIndex, (int) result.getTotal())); } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/db/ActivityDao.java b/server/sonar-server/src/main/java/org/sonar/server/activity/db/ActivityDao.java index 7d4a636947f..c6a9a271632 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/activity/db/ActivityDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/db/ActivityDao.java @@ -19,46 +19,39 @@ */ package org.sonar.server.activity.db; +import org.sonar.api.ServerComponent; import org.sonar.api.utils.System2; import org.sonar.core.activity.db.ActivityDto; import org.sonar.core.activity.db.ActivityMapper; +import org.sonar.core.persistence.DaoComponent; import org.sonar.core.persistence.DbSession; -import org.sonar.server.db.BaseDao; -import org.sonar.server.search.IndexDefinition; +import org.sonar.core.persistence.MyBatis; -import java.util.List; +import java.util.Date; -/** - * @since 4.4 - */ -public class ActivityDao extends BaseDao<ActivityMapper, ActivityDto, String> { +public class ActivityDao implements DaoComponent, ServerComponent { - public ActivityDao(System2 system) { - super(IndexDefinition.LOG, ActivityMapper.class, system); - } + private final MyBatis mybatis; + private final System2 system; - @Override - protected ActivityDto doGetNullableByKey(DbSession session, String key) { - throw new IllegalStateException("Cannot execute getByKey on Activities in DB"); + public ActivityDao(MyBatis mybatis, System2 system) { + this.mybatis = mybatis; + this.system = system; } - @Override - protected ActivityDto doInsert(DbSession session, ActivityDto item) { - mapper(session).insert(item); - return item; + public void insert(ActivityDto dto) { + DbSession session = mybatis.openSession(false); + try { + insert(session, dto); + session.commit(); + } finally { + MyBatis.closeQuietly(session); + } } - @Override - protected ActivityDto doUpdate(DbSession session, ActivityDto item) { - throw new IllegalStateException("Cannot update Log!"); + public void insert(DbSession session, ActivityDto dto) { + dto.setCreatedAt(new Date(system.now())); + session.getMapper(ActivityMapper.class).insert(dto); } - @Override - protected void doDeleteByKey(DbSession session, String key) { - throw new IllegalStateException("Cannot delete Log!"); - } - - public List<ActivityDto> findAll(DbSession session) { - return mapper(session).selectAll(); - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityDoc.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityDoc.java index a438fa3a61d..46e122bae51 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityDoc.java +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityDoc.java @@ -19,54 +19,88 @@ */ package org.sonar.server.activity.index; +import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang.builder.ReflectionToStringBuilder; -import org.sonar.core.activity.Activity; import org.sonar.server.search.BaseDoc; -import org.sonar.server.search.IndexUtils; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import java.util.Date; +import java.util.HashMap; import java.util.Map; -/** - * @since 4.4 - */ -public class ActivityDoc extends BaseDoc implements Activity { +public class ActivityDoc extends BaseDoc { - protected ActivityDoc(Map<String, Object> fields) { + public ActivityDoc(Map<String, Object> fields) { super(fields); } - @Override - public Date time() { - return IndexUtils.parseDateTime((String) getField(ActivityNormalizer.LogFields.CREATED_AT.field())); + @VisibleForTesting + ActivityDoc() { + super(new HashMap<String, Object>()); + } + + public void setCreatedAt(Date date) { + setField(ActivityIndexDefinition.FIELD_CREATED_AT, date); + } + + public Date getCreatedAt() { + return getFieldAsDate(ActivityIndexDefinition.FIELD_CREATED_AT); + } + + public String getKey() { + return this.getField(ActivityIndexDefinition.FIELD_KEY); + } + + public void setKey(String s) { + setField(ActivityIndexDefinition.FIELD_KEY, s); + } + + @CheckForNull + public String getLogin() { + return this.getNullableField(ActivityIndexDefinition.FIELD_LOGIN); + } + + public void setLogin(@Nullable String s) { + setField(ActivityIndexDefinition.FIELD_LOGIN, s); + } + + public String getType() { + return ((String) getField(ActivityIndexDefinition.FIELD_TYPE)); + } + + public void setType(String s) { + setField(ActivityIndexDefinition.FIELD_TYPE, s); + } + + @CheckForNull + public String getAction() { + return this.getNullableField(ActivityIndexDefinition.FIELD_ACTION); } - @Override - public String login() { - return this.getNullableField(ActivityNormalizer.LogFields.LOGIN.field()); + public void setAction(@Nullable String s) { + setField(ActivityIndexDefinition.FIELD_ACTION, s); } - @Override - public Type type() { - return Type.valueOf((String) getField(ActivityNormalizer.LogFields.TYPE.field())); + @CheckForNull + public Map<String, String> getDetails() { + return this.getNullableField(ActivityIndexDefinition.FIELD_DETAILS); } - @Override - public String action() { - return this.getNullableField(ActivityNormalizer.LogFields.ACTION.field()); + public void setDetails(Map<String, String> details) { + setField(ActivityIndexDefinition.FIELD_DETAILS, details); } - @Override - public Map<String, String> details() { - return this.getNullableField(ActivityNormalizer.LogFields.DETAILS.field()); + @CheckForNull + public String getMessage() { + return this.getNullableField(ActivityIndexDefinition.FIELD_MESSAGE); } - @Override - public String message() { - return this.getNullableField(ActivityNormalizer.LogFields.MESSAGE.field()); + public void setMessage(@Nullable String s) { + setField(ActivityIndexDefinition.FIELD_MESSAGE, s); } - @Override public String toString() { return ReflectionToStringBuilder.toString(this); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndex.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndex.java index 0e1ced87c50..ee082d63d43 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndex.java @@ -19,119 +19,86 @@ */ package org.sonar.server.activity.index; +import com.google.common.base.Function; import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.action.search.SearchType; -import org.elasticsearch.common.settings.ImmutableSettings; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.index.query.*; +import org.elasticsearch.index.query.AndFilterBuilder; +import org.elasticsearch.index.query.FilterBuilders; +import org.elasticsearch.index.query.OrFilterBuilder; +import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.sort.SortOrder; -import org.sonar.core.activity.Activity; -import org.sonar.core.activity.db.ActivityDto; -import org.sonar.server.search.*; +import org.sonar.core.util.NonNullInputFunction; +import org.sonar.server.es.BaseIndex; +import org.sonar.server.es.EsClient; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; -import javax.annotation.Nullable; - -import java.util.HashMap; +import java.util.Date; import java.util.Map; -/** - * @since 4.4 - */ -public class ActivityIndex extends BaseIndex<Activity, ActivityDto, String> { - - public ActivityIndex(ActivityNormalizer normalizer, SearchClient node) { - super(IndexDefinition.LOG, normalizer, node); - } - - @Override - protected String getKeyValue(String key) { - return key; - } - - @Override - protected Map mapKey() { - Map<String, Object> mapping = new HashMap<String, Object>(); - mapping.put("path", ActivityNormalizer.LogFields.KEY.field()); - return mapping; - } - - @Override - protected ImmutableSettings.Builder addCustomIndexSettings(ImmutableSettings.Builder settings) { - return settings - .put("analysis.analyzer.default.type", "keyword"); - } +public class ActivityIndex extends BaseIndex { - @Override - protected Map mapProperties() { - Map<String, Object> mapping = new HashMap<String, Object>(); - for (IndexField field : ActivityNormalizer.LogFields.ALL_FIELDS) { - mapping.put(field.field(), mapField(field)); + /** + * Convert an Elasticsearch result (a map) to an {@link org.sonar.server.activity.index.ActivityDoc}. It's + * used for {@link org.sonar.server.es.SearchResult}. + */ + private static final Function<Map<String, Object>, ActivityDoc> DOC_CONVERTER = new NonNullInputFunction<Map<String, Object>, ActivityDoc>() { + @Override + protected ActivityDoc doApply(Map<String, Object> input) { + return new ActivityDoc(input); } - return mapping; - } - - @Override - protected Activity toDoc(final Map<String, Object> fields) { - return new ActivityDoc(fields); - } + }; - public Result<Activity> findAll() { - SearchRequestBuilder request = getClient().prepareSearch(this.getIndexName()) - .setQuery(QueryBuilders.matchAllQuery()) - .setTypes(this.getIndexType()) - .setSize(Integer.MAX_VALUE); - SearchResponse response = request.get(); - return new Result<Activity>(this, response); + public ActivityIndex(EsClient esClient) { + super(esClient); } - public SearchResponse search(ActivityQuery query, QueryContext options) { - return search(query, options, null); + public SearchResult<ActivityDoc> search(ActivityQuery query, SearchOptions options) { + SearchResponse response = doSearch(query, options); + return new SearchResult<>(response, DOC_CONVERTER); } - public SearchResponse search(ActivityQuery query, QueryContext options, - @Nullable FilterBuilder domainFilter) { - - // Prepare query - SearchRequestBuilder esSearch = getClient() - .prepareSearch(this.getIndexName()) - .setTypes(this.getIndexType()) - .setIndices(this.getIndexName()); - - // Integrate Pagination - esSearch.setFrom(options.getOffset()); - esSearch.setSize(options.getLimit()); + public SearchResponse doSearch(ActivityQuery query, SearchOptions options) { + SearchRequestBuilder requestBuilder = getClient() + .prepareSearch(ActivityIndexDefinition.INDEX) + .setTypes(ActivityIndexDefinition.TYPE); - // Sort Date Desc - esSearch.addSort(ActivityNormalizer.LogFields.CREATED_AT.field(), SortOrder.DESC); + requestBuilder.setFrom(options.getOffset()); + requestBuilder.setSize(options.getLimit()); + requestBuilder.addSort(ActivityIndexDefinition.FIELD_CREATED_AT, SortOrder.DESC); AndFilterBuilder filter = FilterBuilders.andFilter(); - - // implement Type Filtering - OrFilterBuilder typeFilter = FilterBuilders.orFilter(); - for (Activity.Type type : query.getTypes()) { - typeFilter.add(FilterBuilders.termFilter(ActivityNormalizer.LogFields.TYPE.field(), type)); + if (!query.getTypes().isEmpty()) { + OrFilterBuilder typeFilter = FilterBuilders.orFilter(); + for (String type : query.getTypes()) { + typeFilter.add(FilterBuilders.termFilter(ActivityIndexDefinition.FIELD_TYPE, type)); + } + filter.add(typeFilter); } - filter.add(typeFilter); - - // Implement date Filter - filter.add(FilterBuilders.rangeFilter(ActivityNormalizer.LogFields.CREATED_AT.field()) - .from(query.getSince()) - .to(query.getTo())); - // Add any additional domain filter - if (domainFilter != null) { - filter.add(domainFilter); + if (!query.getDataOrFilters().isEmpty()) { + for (Map.Entry<String, Object> entry : query.getDataOrFilters().entrySet()) { + OrFilterBuilder orFilter = FilterBuilders.orFilter(); + orFilter.add(FilterBuilders.nestedFilter(ActivityIndexDefinition.FIELD_DETAILS, + FilterBuilders.termFilter(ActivityIndexDefinition.FIELD_DETAILS + "." + entry.getKey(), entry.getValue()))); + filter.add(orFilter); + } } - esSearch.setQuery(QueryBuilders.filteredQuery( - QueryBuilders.matchAllQuery(), filter)); - - if (options.isScroll()) { - esSearch.setSearchType(SearchType.SCAN); - esSearch.setScroll(TimeValue.timeValueMinutes(3)); + Date since = query.getSince(); + if (since != null) { + filter.add(FilterBuilders.rangeFilter(ActivityIndexDefinition.FIELD_CREATED_AT) + .gt(since) + .cache(false)); + } + Date to = query.getTo(); + if (to != null) { + filter.add(FilterBuilders.rangeFilter(ActivityIndexDefinition.FIELD_CREATED_AT) + .lt(to) + .cache(false)); } - return esSearch.get(); + requestBuilder.setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), filter)); + return requestBuilder.get(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexDefinition.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexDefinition.java new file mode 100644 index 00000000000..9fd5756c51b --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexDefinition.java @@ -0,0 +1,76 @@ +/* + * 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.activity.index; + +import com.google.common.collect.ImmutableMap; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.sonar.api.config.Settings; +import org.sonar.process.ProcessConstants; +import org.sonar.server.es.IndexDefinition; +import org.sonar.server.es.NewIndex; + +/** + * Definition of ES index "activities", including settings and fields. + */ +public class ActivityIndexDefinition implements IndexDefinition { + + public static final String INDEX = "activities"; + public static final String TYPE = "activity"; + public static final String FIELD_KEY = "key"; + public static final String FIELD_TYPE = "type"; + public static final String FIELD_ACTION = "action"; + public static final String FIELD_CREATED_AT = "createdAt"; + public static final String FIELD_LOGIN = "login"; + public static final String FIELD_DETAILS = "details"; + public static final String FIELD_MESSAGE = "message"; + + private final Settings settings; + + public ActivityIndexDefinition(Settings settings) { + this.settings = settings; + } + + @Override + public void define(IndexDefinitionContext context) { + NewIndex index = context.create(INDEX); + // refresh is always handled by ActivityIndexer + index.getSettings().put("index.refresh_interval", "-1"); + index.getSettings().put("analysis.analyzer.default.type", "keyword"); + + // shards + boolean clusterMode = settings.getBoolean(ProcessConstants.CLUSTER_ACTIVATE); + if (clusterMode) { + index.getSettings().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 4); + index.getSettings().put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1); + // else keep defaults (one shard) + } + + // type "activity" + NewIndex.NewIndexType mapping = index.createType(TYPE); + mapping.setAttribute("_id", ImmutableMap.of("path", FIELD_KEY)); + mapping.stringFieldBuilder(FIELD_KEY).build(); + mapping.stringFieldBuilder(FIELD_TYPE).build(); + mapping.stringFieldBuilder(FIELD_ACTION).build(); + mapping.stringFieldBuilder(FIELD_LOGIN).build(); + mapping.createDynamicNestedField(FIELD_DETAILS); + mapping.stringFieldBuilder(FIELD_MESSAGE).build(); + mapping.createDateTimeField(FIELD_CREATED_AT); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexer.java new file mode 100644 index 00000000000..1a668bd5a8f --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndexer.java @@ -0,0 +1,89 @@ +/* + * 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.activity.index; + +import org.elasticsearch.action.update.UpdateRequest; +import org.sonar.core.persistence.DbSession; +import org.sonar.server.db.DbClient; +import org.sonar.server.es.BaseIndexer; +import org.sonar.server.es.BulkIndexer; +import org.sonar.server.es.EsClient; + +import java.sql.Connection; +import java.util.Iterator; + +/** + * Add to Elasticsearch index {@link org.sonar.server.activity.index.ActivityIndexDefinition} the rows of + * db table ACTIVITIES that are not indexed yet + * <p/> + * TODO idea of improvement - index asynchronously with UpdateRequest#replicationType(ReplicationType.ASYNC) + */ +public class ActivityIndexer extends BaseIndexer { + + private final DbClient dbClient; + + public ActivityIndexer(DbClient dbClient, EsClient esClient) { + super(esClient, 0L, ActivityIndexDefinition.INDEX, ActivityIndexDefinition.TYPE); + this.dbClient = dbClient; + } + + @Override + protected long doIndex(long lastUpdatedAt) { + final BulkIndexer bulk = new BulkIndexer(esClient, ActivityIndexDefinition.INDEX); + bulk.setLarge(lastUpdatedAt == 0L); + + DbSession dbSession = dbClient.openSession(false); + Connection dbConnection = dbSession.getConnection(); + try { + ActivityResultSetIterator rowIt = ActivityResultSetIterator.create(dbClient, dbConnection, lastUpdatedAt); + long maxUpdatedAt = doIndex(bulk, rowIt); + rowIt.close(); + return maxUpdatedAt; + + } finally { + dbSession.close(); + } + } + + public long index(Iterator<ActivityDoc> activities) { + final BulkIndexer bulk = new BulkIndexer(esClient, ActivityIndexDefinition.INDEX); + return doIndex(bulk, activities); + } + + private long doIndex(BulkIndexer bulk, Iterator<ActivityDoc> activities) { + long maxUpdatedAt = 0L; + bulk.start(); + while (activities.hasNext()) { + ActivityDoc activity = activities.next(); + bulk.add(newUpsertRequest(activity)); + + // it's more efficient to sort programmatically than in SQL on some databases (MySQL for instance) + maxUpdatedAt = Math.max(maxUpdatedAt, activity.getCreatedAt().getTime()); + } + bulk.stop(); + return maxUpdatedAt; + } + + private UpdateRequest newUpsertRequest(ActivityDoc doc) { + return new UpdateRequest(ActivityIndexDefinition.INDEX, ActivityIndexDefinition.TYPE, doc.getKey()) + .doc(doc.getFields()) + .upsert(doc.getFields()); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityNormalizer.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityNormalizer.java deleted file mode 100644 index 2e64ed487a7..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityNormalizer.java +++ /dev/null @@ -1,81 +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.activity.index; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import org.elasticsearch.action.support.replication.ReplicationType; -import org.elasticsearch.action.update.UpdateRequest; -import org.sonar.api.utils.KeyValueFormat; -import org.sonar.core.activity.db.ActivityDto; -import org.sonar.server.db.DbClient; -import org.sonar.server.search.BaseNormalizer; -import org.sonar.server.search.IndexField; -import org.sonar.server.search.Indexable; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @since 4.4 - */ -public class ActivityNormalizer extends BaseNormalizer<ActivityDto, String> { - - public static final class LogFields extends Indexable { - - public static final IndexField KEY = add(IndexField.Type.STRING, "key"); - public static final IndexField TYPE = addSortable(IndexField.Type.STRING, "type"); - public static final IndexField ACTION = addSortable(IndexField.Type.STRING, "action"); - public static final IndexField CREATED_AT = addSortable(IndexField.Type.DATE, "createdAt"); - public static final IndexField UPDATED_AT = addSortable(IndexField.Type.DATE, BaseNormalizer.UPDATED_AT_FIELD); - public static final IndexField LOGIN = addSearchable(IndexField.Type.STRING, "login"); - public static final IndexField DETAILS = addSearchable(IndexField.Type.OBJECT, "details"); - public static final IndexField MESSAGE = addSearchable(IndexField.Type.STRING, "message"); - public static final Set<IndexField> ALL_FIELDS = ImmutableSet.of(KEY, TYPE, ACTION, CREATED_AT, UPDATED_AT, LOGIN, DETAILS, MESSAGE); - } - - public ActivityNormalizer(DbClient db) { - super(db); - } - - @Override - public List<UpdateRequest> normalize(ActivityDto dto) { - - Map<String, Object> logDoc = new HashMap<String, Object>(); - logDoc.put(LogFields.KEY.field(), dto.getKey()); - logDoc.put(LogFields.TYPE.field(), dto.getType()); - logDoc.put(LogFields.ACTION.field(), dto.getAction()); - logDoc.put(LogFields.LOGIN.field(), dto.getAuthor()); - logDoc.put(LogFields.MESSAGE.field(), dto.getMessage()); - logDoc.put(LogFields.CREATED_AT.field(), dto.getCreatedAt()); - logDoc.put(LogFields.UPDATED_AT.field(), dto.getUpdatedAt()); - - logDoc.put(LogFields.DETAILS.field(), KeyValueFormat.parse(dto.getData())); - - /* Creating updateRequest */ - return ImmutableList.of(new UpdateRequest() - .id(dto.getKey()) - .replicationType(ReplicationType.ASYNC) - .doc(logDoc) - .upsert(logDoc)); - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityQuery.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityQuery.java index 53c8749ae21..b8e25d91c15 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityQuery.java +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityQuery.java @@ -19,49 +19,60 @@ */ package org.sonar.server.activity.index; -import com.google.common.collect.Lists; -import org.sonar.core.activity.Activity; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.Collection; import java.util.Date; +import java.util.LinkedHashMap; +import java.util.Map; -/** - * @since 4.4 - */ public class ActivityQuery { private Date since; private Date to; - private Collection<Activity.Type> types; - - public ActivityQuery() { - types = Lists.newArrayList(); - } + private final Collection<String> types = new ArrayList<>(); + private final Map<String, Object> dataOrFilters = new LinkedHashMap<>(); + @CheckForNull public Date getSince() { return since; } - public ActivityQuery setSince(Date since) { + public ActivityQuery setSince(@Nullable Date since) { this.since = since; return this; } + @CheckForNull public Date getTo() { return to; } - public ActivityQuery setTo(Date to) { + public ActivityQuery setTo(@Nullable Date to) { this.to = to; return this; } - public Collection<Activity.Type> getTypes() { + public Collection<String> getTypes() { return types; } - public ActivityQuery setTypes(Collection<Activity.Type> types) { - this.types = types; + public ActivityQuery setTypes(@Nullable Collection<String> types) { + this.types.clear(); + if (types != null) { + this.types.addAll(types); + } + return this; + } + + public Map<String, Object> getDataOrFilters() { + return dataOrFilters; + } + + public ActivityQuery addDataOrFilter(String dataKey, Object dataValue) { + dataOrFilters.put(dataKey, dataValue); return this; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java new file mode 100644 index 00000000000..701d649ff8f --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityResultSetIterator.java @@ -0,0 +1,86 @@ +/* + * 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.activity.index; + +import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.server.db.DbClient; +import org.sonar.server.db.ResultSetIterator; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.HashMap; + +/** + * Scrolls over table ACTIVITIES and reads documents to populate + * the index "activities/activity" + */ +class ActivityResultSetIterator extends ResultSetIterator<ActivityDoc> { + + private static final String[] FIELDS = { + // column 1 + "log_key", + "log_action", + "log_message", + "data_field", + "user_login", + "log_type", + "created_at" + }; + + private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from activities "; + + private static final String SQL_AFTER_DATE = SQL_ALL + " where created_at>=?"; + + private ActivityResultSetIterator(PreparedStatement stmt) throws SQLException { + super(stmt); + } + + static ActivityResultSetIterator create(DbClient dbClient, Connection connection, long afterDate) { + try { + String sql = afterDate > 0L ? SQL_AFTER_DATE : SQL_ALL; + PreparedStatement stmt = dbClient.newScrollingSelectStatement(connection, sql); + if (afterDate > 0L) { + stmt.setTimestamp(1, new Timestamp(afterDate)); + } + return new ActivityResultSetIterator(stmt); + } catch (SQLException e) { + throw new IllegalStateException("Fail to prepare SQL request to select activities", e); + } + } + + @Override + protected ActivityDoc read(ResultSet rs) throws SQLException { + ActivityDoc doc = new ActivityDoc(new HashMap<String, Object>(10)); + + // all the fields must be present, even if value is null + doc.setKey(rs.getString(1)); + doc.setAction(rs.getString(2)); + doc.setMessage(rs.getString(3)); + doc.setDetails(KeyValueFormat.parse(rs.getString(4))); + doc.setLogin(rs.getString(5)); + doc.setType(rs.getString(6)); + doc.setCreatedAt(rs.getTimestamp(7)); + return doc; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java b/server/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java index aee5b31cdc6..07a756335cf 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java @@ -20,9 +20,8 @@ package org.sonar.server.activity.ws; import org.sonar.api.utils.text.JsonWriter; -import org.sonar.core.activity.Activity; import org.sonar.server.activity.index.ActivityDoc; -import org.sonar.server.activity.index.ActivityNormalizer; +import org.sonar.server.activity.index.ActivityIndexDefinition; import org.sonar.server.search.QueryContext; import org.sonar.server.search.ws.BaseMapping; @@ -34,16 +33,16 @@ import java.util.Map; public class ActivityMapping extends BaseMapping<ActivityDoc, Object> { public ActivityMapping() { - map("type", ActivityNormalizer.LogFields.TYPE.field()); - map("action", ActivityNormalizer.LogFields.ACTION.field()); - mapDateTime("createdAt", ActivityNormalizer.LogFields.CREATED_AT.field()); - map("login", ActivityNormalizer.LogFields.LOGIN.field()); - map("message", ActivityNormalizer.LogFields.MESSAGE.field()); - map("details", new IndexMapper<ActivityDoc, Object>(ActivityNormalizer.LogFields.DETAILS.field()) { + map("type", ActivityIndexDefinition.FIELD_TYPE); + map("action", ActivityIndexDefinition.FIELD_ACTION); + mapDateTime("createdAt", ActivityIndexDefinition.FIELD_CREATED_AT); + map("login", ActivityIndexDefinition.FIELD_LOGIN); + map("message", ActivityIndexDefinition.FIELD_MESSAGE); + map("details", new IndexMapper<ActivityDoc, Object>(ActivityIndexDefinition.FIELD_DETAILS) { @Override public void write(JsonWriter json, ActivityDoc activity, Object context) { json.name("details").beginObject(); - for (Map.Entry<String, String> detail : activity.details().entrySet()) { + for (Map.Entry<String, String> detail : activity.getDetails().entrySet()) { json.prop(detail.getKey(), detail.getValue()); } json.endObject(); @@ -51,8 +50,8 @@ public class ActivityMapping extends BaseMapping<ActivityDoc, Object> { }); } - public void write(Activity activity, JsonWriter writer, QueryContext context) { - doWrite((ActivityDoc) activity, null, writer, context); + public void write(ActivityDoc activity, JsonWriter writer, QueryContext context) { + doWrite(activity, null, writer, context); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java index 256028e0209..0bb0647f753 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java @@ -19,34 +19,30 @@ */ package org.sonar.server.activity.ws; -import org.apache.commons.lang.StringUtils; 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.core.activity.Activity; -import org.sonar.server.activity.ActivityService; +import org.sonar.server.activity.Activity; +import org.sonar.server.activity.index.ActivityDoc; +import org.sonar.server.activity.index.ActivityIndex; import org.sonar.server.activity.index.ActivityQuery; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; import org.sonar.server.search.QueryContext; -import org.sonar.server.search.Result; -import org.sonar.server.search.ws.SearchOptions; -/** - * @since 4.4 - */ public class SearchAction implements RequestHandler { public static final String PARAM_TYPE = "type"; - public static final String SEARCH_ACTION = "search"; - private final ActivityService logService; - private final ActivityMapping mapping; + private final ActivityIndex activityIndex; + private final ActivityMapping docToJsonMapping; - public SearchAction(ActivityService logService, ActivityMapping mapping) { - this.logService = logService; - this.mapping = mapping; + public SearchAction(ActivityIndex activityIndex, ActivityMapping docToJsonMapping) { + this.activityIndex = activityIndex; + this.docToJsonMapping = docToJsonMapping; } void define(WebService.NewController controller) { @@ -57,36 +53,34 @@ public class SearchAction implements RequestHandler { .setInternal(true) .setHandler(this); - // Other parameters action.createParam(PARAM_TYPE) - .setDescription("Types of activities to search") - .setPossibleValues(Activity.Type.values()) - .setDefaultValue(StringUtils.join(Activity.Type.values(), ",")); + .setDescription("Activity type") + .setPossibleValues(Activity.Type.values()); - // Generic search parameters - SearchOptions.defineFieldsParam(action, mapping.supportedFields()); - - SearchOptions.definePageParams(action); + action.addPagingParams(10); + action.addFieldsParam(docToJsonMapping.supportedFields()); } @Override public void handle(Request request, Response response) { - ActivityQuery query = logService.newActivityQuery(); - SearchOptions searchOptions = SearchOptions.create(request); - QueryContext queryContext = mapping.newQueryOptions(searchOptions); + ActivityQuery query = new ActivityQuery(); + query.setTypes(request.paramAsStrings(PARAM_TYPE)); + + SearchOptions options = new SearchOptions(); + options.setPage(request.mandatoryParamAsInt(WebService.Param.PAGE), request.mandatoryParamAsInt(WebService.Param.PAGE_SIZE)); - Result<Activity> results = logService.search(query, queryContext); + SearchResult<ActivityDoc> results = activityIndex.search(query, options); JsonWriter json = response.newJsonWriter().beginObject(); - searchOptions.writeStatistics(json, results); - writeLogs(results, json, queryContext); + options.writeJson(json, results.getTotal()); + writeActivities(results, json, new QueryContext().setFieldsToReturn(request.paramAsStrings(WebService.Param.FIELDS))); json.endObject().close(); } - private void writeLogs(Result<Activity> result, JsonWriter json, QueryContext context) { + private void writeActivities(SearchResult<ActivityDoc> docs, JsonWriter json, QueryContext context) { json.name("logs").beginArray(); - for (Activity log : result.getHits()) { - mapping.write(log, json, context); + for (ActivityDoc doc : docs.getDocs()) { + docToJsonMapping.write(doc, json, context); } json.endArray(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java index 8787e16ec94..cdaca50a7d1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java @@ -31,11 +31,11 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; import org.sonar.batch.protocol.output.BatchReportReader; -import org.sonar.core.activity.Activity; import org.sonar.core.component.ComponentDto; import org.sonar.core.computation.db.AnalysisReportDto; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; +import org.sonar.server.activity.Activity; import org.sonar.server.activity.ActivityService; import org.sonar.server.computation.step.ComputationStep; import org.sonar.server.computation.step.ComputationSteps; @@ -44,6 +44,9 @@ import org.sonar.server.db.DbClient; import java.io.File; import java.io.IOException; +import static org.sonar.api.utils.DateUtils.formatDateTimeNullSafe; +import static org.sonar.api.utils.DateUtils.longToDate; + public class ComputationService implements ServerComponent { private static final Logger LOG = Loggers.get(ComputationService.class); @@ -87,7 +90,7 @@ public class ComputationService implements ServerComponent { } finally { item.dto.setFinishedAt(system.now()); - logActivity(item.dto, project); + saveActivity(item.dto, project); profiler.stopInfo(); } } @@ -117,14 +120,19 @@ public class ComputationService implements ServerComponent { } } - private void logActivity(AnalysisReportDto report, ComponentDto project) { - DbSession session = dbClient.openSession(false); - try { - report.setFinishedAt(System2.INSTANCE.now()); - activityService.write(session, Activity.Type.ANALYSIS_REPORT, new ReportActivity(report, project)); - session.commit(); - } finally { - MyBatis.closeQuietly(session); - } + private void saveActivity(AnalysisReportDto report, ComponentDto project) { + Activity activity = new Activity(); + activity.setType(Activity.Type.ANALYSIS_REPORT); + activity.setAction("LOG_ANALYSIS_REPORT"); + activity + .setData("key", String.valueOf(report.getId())) + .setData("projectKey", project.key()) + .setData("projectName", project.name()) + .setData("projectUuid", project.uuid()) + .setData("status", String.valueOf(report.getStatus())) + .setData("submittedAt", formatDateTimeNullSafe(longToDate(report.getCreatedAt()))) + .setData("startedAt", formatDateTimeNullSafe(longToDate(report.getStartedAt()))) + .setData("finishedAt", formatDateTimeNullSafe(longToDate(report.getFinishedAt()))); + activityService.save(activity); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportActivity.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ReportActivity.java deleted file mode 100644 index b4d7165a82f..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ReportActivity.java +++ /dev/null @@ -1,63 +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.computation; - -import com.google.common.collect.ImmutableMap; -import org.sonar.core.activity.ActivityLog; -import org.sonar.core.component.ComponentDto; -import org.sonar.core.computation.db.AnalysisReportDto; - -import java.util.Map; - -import static org.sonar.api.utils.DateUtils.formatDateTimeNullSafe; -import static org.sonar.api.utils.DateUtils.longToDate; - -public class ReportActivity implements ActivityLog { - - private static final String ACTION = "LOG_ANALYSIS_REPORT"; - - private final AnalysisReportDto report; - private final ComponentDto project; - - public ReportActivity(AnalysisReportDto report, ComponentDto project) { - this.report = report; - this.project = project; - } - - @Override - public Map<String, String> getDetails() { - return ImmutableMap.<String, String>builder() - .put("key", String.valueOf(report.getId())) - .put("projectKey", project.key()) - .put("projectName", project.name()) - .put("projectUuid", project.uuid()) - .put("status", String.valueOf(report.getStatus())) - .put("submittedAt", formatDateTimeNullSafe(longToDate(report.getCreatedAt()))) - .put("startedAt", formatDateTimeNullSafe(longToDate(report.getStartedAt()))) - .put("finishedAt", formatDateTimeNullSafe(longToDate(report.getFinishedAt()))) - .build(); - } - - @Override - public String getAction() { - return ACTION; - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/HistoryWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/HistoryWsAction.java index eec1336a157..82135a2c15f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/ws/HistoryWsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/ws/HistoryWsAction.java @@ -25,29 +25,28 @@ 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.core.activity.Activity; import org.sonar.core.permission.GlobalPermissions; -import org.sonar.server.activity.ActivityService; +import org.sonar.server.activity.Activity; +import org.sonar.server.activity.index.ActivityDoc; +import org.sonar.server.activity.index.ActivityIndex; import org.sonar.server.activity.index.ActivityQuery; -import org.sonar.server.activity.ws.ActivityMapping; -import org.sonar.server.search.QueryContext; -import org.sonar.server.search.Result; -import org.sonar.server.search.ws.SearchOptions; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; +import org.sonar.server.issue.ws.IssuesWs; import org.sonar.server.user.UserSession; import java.util.Arrays; import java.util.Map; +// FIXME replace by api/activities/search public class HistoryWsAction implements ComputationWsAction, RequestHandler { public static final String PARAM_TYPE = "type"; - private final ActivityService activityService; - private final ActivityMapping mapping; + private final ActivityIndex activityIndex; - public HistoryWsAction(ActivityService activityService, ActivityMapping mapping) { - this.activityService = activityService; - this.mapping = mapping; + public HistoryWsAction(ActivityIndex activityIndex) { + this.activityIndex = activityIndex; } @Override @@ -59,38 +58,31 @@ public class HistoryWsAction implements ComputationWsAction, RequestHandler { .setInternal(true) .setHandler(this); - // Generic search parameters - SearchOptions.defineFieldsParam(action, mapping.supportedFields()); - SearchOptions.definePageParams(action); + action.addPagingParams(10); } @Override public void handle(Request request, Response response) { - checkUserRights(); - - ActivityQuery query = activityService.newActivityQuery(); - query.setTypes(Arrays.asList(Activity.Type.ANALYSIS_REPORT)); + UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); - SearchOptions searchOptions = SearchOptions.create(request); - QueryContext queryContext = mapping.newQueryOptions(searchOptions); + ActivityQuery query = new ActivityQuery(); + query.setTypes(Arrays.asList(Activity.Type.ANALYSIS_REPORT.name())); - Result<Activity> results = activityService.search(query, queryContext); + SearchOptions options = new SearchOptions(); + options.setPage(request.mandatoryParamAsInt(IssuesWs.Param.PAGE), request.mandatoryParamAsInt(IssuesWs.Param.PAGE_SIZE)); + SearchResult<ActivityDoc> results = activityIndex.search(query, options); JsonWriter json = response.newJsonWriter().beginObject(); - searchOptions.writeStatistics(json, results); + options.writeJson(json, results.getTotal()); writeReports(results, json); json.endObject().close(); } - private void checkUserRights() { - UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN); - } - - private void writeReports(Result<Activity> result, JsonWriter json) { + private void writeReports(SearchResult<ActivityDoc> result, JsonWriter json) { json.name("reports").beginArray(); - for (Activity reportActivity : result.getHits()) { + for (ActivityDoc doc : result.getDocs()) { json.beginObject(); - for (Map.Entry<String, String> detail : reportActivity.details().entrySet()) { + for (Map.Entry<String, String> detail : doc.getDetails().entrySet()) { json.prop(detail.getKey(), detail.getValue()); } json.endObject(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/ChangeLogMigration.java b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/ChangeLogMigration.java index 41b28a488dd..ca1ca126cd8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/ChangeLogMigration.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/migrations/v44/ChangeLogMigration.java @@ -21,13 +21,15 @@ package org.sonar.server.db.migrations.v44; import org.apache.commons.lang.StringUtils; import org.sonar.api.rule.RuleKey; -import org.sonar.core.activity.Activity; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.api.utils.internal.Uuids; import org.sonar.core.activity.db.ActivityDto; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.migration.v44.ChangeLog; import org.sonar.core.persistence.migration.v44.Migration44Mapper; import org.sonar.core.qualityprofile.db.ActiveRuleKey; import org.sonar.core.rule.SeverityUtil; +import org.sonar.server.activity.Activity; import org.sonar.server.activity.db.ActivityDao; import org.sonar.server.db.DbClient; import org.sonar.server.db.migrations.DatabaseMigration; @@ -99,11 +101,16 @@ public class ChangeLogMigration implements DatabaseMigration { } private void saveActiveRuleChange(DbSession session, ActiveRuleChange ruleChange, String author, Date currentTimeStamp) { - ActivityDto activity = ActivityDto.createFor(ruleChange); - activity.setType(Activity.Type.QPROFILE); - activity.setAuthor(author); - activity.setCreatedAt(currentTimeStamp); - dao.insert(session, activity); + Activity activity = ruleChange.toActivity(); + ActivityDto dto = new ActivityDto(); + dto.setKey(Uuids.create()); + dto.setType(Activity.Type.QPROFILE.name()); + dto.setAction(activity.getAction()); + dto.setMessage(activity.getMessage()); + dto.setAuthor(author); + dto.setData(KeyValueFormat.format(activity.getData())); + dto.setCreatedAt(currentTimeStamp); + dao.insert(session, dto); } private void processRuleChange(ActiveRuleChange ruleChange, ChangeLog change) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/es/NewIndex.java b/server/sonar-server/src/main/java/org/sonar/server/es/NewIndex.java index 4459fd5b3db..22493030b9e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/es/NewIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/es/NewIndex.java @@ -96,6 +96,10 @@ public class NewIndex { return setProperty(fieldName, ImmutableMap.of("type", "long")); } + public NewIndexType createDynamicNestedField(String fieldName) { + return setProperty(fieldName, ImmutableMap.of("type", "nested", "dynamic", "true")); + } + public NewIndexType createShortField(String fieldName) { return setProperty(fieldName, ImmutableMap.of("type", "short")); } 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 7f24b0a56cd..76de0d82363 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 @@ -150,7 +150,7 @@ class IssueResultSetIterator extends ResultSetIterator<IssueDoc> { String key = rs.getString(1); String projectUuid = rs.getString(2); - // all the keys must be present, even if value is null + // all the fields must be present, even if value is null doc.setKey(key); doc.setProjectUuid(projectUuid); doc.setTechnicalUpdateDate(new Date(rs.getLong(3))); 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 c5e4b4770a0..9898640cb76 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 @@ -63,7 +63,13 @@ 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.LinkedHashMap; +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; @@ -263,7 +269,7 @@ public class SearchAction implements BaseIssuesWsAction { @Override 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.setPage(request.mandatoryParamAsInt(WebService.Param.PAGE), request.mandatoryParamAsInt(WebService.Param.PAGE_SIZE)); options.addFacets(request.paramAsStrings(WebService.Param.FACETS)); IssueQuery query = issueQueryService.createFromRequest(request); 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 cf8302be667..dd409d3e8ae 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 @@ -78,7 +78,8 @@ import org.sonar.server.activity.ActivityService; import org.sonar.server.activity.RubyQProfileActivityService; import org.sonar.server.activity.db.ActivityDao; import org.sonar.server.activity.index.ActivityIndex; -import org.sonar.server.activity.index.ActivityNormalizer; +import org.sonar.server.activity.index.ActivityIndexDefinition; +import org.sonar.server.activity.index.ActivityIndexer; import org.sonar.server.activity.ws.ActivitiesWebService; import org.sonar.server.activity.ws.ActivityMapping; import org.sonar.server.authentication.ws.AuthenticationWs; @@ -423,10 +424,6 @@ class ServerComponents { IssueIndex.class, IssueDao.class, - // Activity - ActivityService.class, - ActivityNormalizer.class, - ActivityIndex.class, ActivityDao.class )); components.addAll(CorePropertyDefinitions.all()); @@ -491,6 +488,12 @@ class ServerComponents { pico.addSingleton(IndexRegistry.class); pico.addSingleton(IndexCreator.class); + // Activity + pico.addSingleton(ActivityService.class); + pico.addSingleton(ActivityIndexDefinition.class); + pico.addSingleton(ActivityIndexer.class); + pico.addSingleton(ActivityIndex.class); + // batch pico.addSingleton(BatchIndex.class); pico.addSingleton(GlobalRepositoryAction.class); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleChange.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleChange.java index 44391663eec..ae29ee90da5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleChange.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRuleChange.java @@ -22,16 +22,15 @@ package org.sonar.server.qualityprofile; import com.google.common.base.Objects; import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; -import org.sonar.core.activity.ActivityLog; import org.sonar.core.qualityprofile.db.ActiveRuleKey; +import org.sonar.server.activity.Activity; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.util.HashMap; import java.util.Map; -public class ActiveRuleChange implements ActivityLog { +public class ActiveRuleChange { public static enum Type { ACTIVATED, DEACTIVATED, UPDATED @@ -91,26 +90,26 @@ public class ActiveRuleChange implements ActivityLog { return this; } - @Override - public Map<String, String> getDetails() { - HashMap<String, String> details = new HashMap<String, String>(); - - details.put("key", getKey().toString()); - details.put("ruleKey", getKey().ruleKey().toString()); - details.put("profileKey", getKey().qProfile().toString()); + public Activity toActivity() { + Activity activity = new Activity(); + activity.setType(Activity.Type.QPROFILE); + activity.setAction(type.name()); + activity.setData("key", getKey().toString()); + activity.setData("ruleKey", getKey().ruleKey().toString()); + activity.setData("profileKey", getKey().qProfile().toString()); for (Map.Entry<String, String> param : parameters.entrySet()) { if (!param.getKey().isEmpty()) { - details.put("param_" + param.getKey(), param.getValue()); + activity.setData("param_" + param.getKey(), param.getValue()); } } if (StringUtils.isNotEmpty(severity)) { - details.put("severity", severity); + activity.setData("severity", severity); } if (inheritance != null) { - details.put("inheritance", inheritance.name()); + activity.setData("inheritance", inheritance.name()); } - return details; + return activity; } public static ActiveRuleChange createFor(Type type, ActiveRuleKey key) { @@ -127,9 +126,4 @@ public class ActiveRuleChange implements ActivityLog { .add("parameters", parameters) .toString(); } - - @Override - public String getAction() { - return type.name(); - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActivity.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActivity.java index 8b294dfa62f..fa49d3695e4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActivity.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActivity.java @@ -21,19 +21,17 @@ package org.sonar.server.qualityprofile; import com.google.common.collect.Maps; import org.sonar.api.rule.RuleKey; -import org.sonar.core.activity.Activity; import org.sonar.server.activity.index.ActivityDoc; -import org.sonar.server.activity.index.ActivityNormalizer; +import org.sonar.server.activity.index.ActivityIndexDefinition; import javax.annotation.CheckForNull; import javax.annotation.Nullable; - import java.util.Map; /** * @since 4.4 */ -public class QProfileActivity extends ActivityDoc implements Activity { +public class QProfileActivity extends ActivityDoc { private String ruleName = null; private String authorName = null; @@ -77,8 +75,8 @@ public class QProfileActivity extends ActivityDoc implements Activity { @Override @CheckForNull - public String login() { - return getNullableField(ActivityNormalizer.LogFields.LOGIN.field()); + public String getLogin() { + return getNullableField(ActivityIndexDefinition.FIELD_LOGIN); } @CheckForNull diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActivityQuery.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActivityQuery.java index cc303dab5a1..5cce6da31fe 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActivityQuery.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileActivityQuery.java @@ -19,32 +19,30 @@ */ package org.sonar.server.qualityprofile; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import org.sonar.core.activity.Activity; +import org.sonar.server.activity.Activity; import org.sonar.server.activity.index.ActivityQuery; -import java.util.Collection; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import java.util.Arrays; /** * @since 4.4 */ public class QProfileActivityQuery extends ActivityQuery { - Collection<String> qprofileKeys; - public QProfileActivityQuery() { super(); - this.setTypes(ImmutableSet.of(Activity.Type.QPROFILE)); - qprofileKeys = Lists.newArrayList(); + setTypes(Arrays.asList(Activity.Type.QPROFILE.name())); } - public Collection<String> getQprofileKeys() { - return qprofileKeys; + @CheckForNull + public String getQprofileKey() { + return (String)getDataOrFilters().get("profileKey"); } - public QProfileActivityQuery setQprofileKeys(Collection<String> qprofileKeys) { - this.qprofileKeys = qprofileKeys; + public QProfileActivityQuery setQprofileKey(@Nullable String qprofileKey) { + addDataOrFilter("profileKey", qprofileKey); return this; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java index 0e0a7acb4ac..42a4ad0f0a3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java @@ -20,9 +20,6 @@ package org.sonar.server.qualityprofile; import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.index.query.FilterBuilders; -import org.elasticsearch.index.query.OrFilterBuilder; -import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.sonar.api.ServerComponent; import org.sonar.core.permission.GlobalPermissions; @@ -33,15 +30,13 @@ import org.sonar.core.rule.RuleDto; import org.sonar.core.user.UserDto; import org.sonar.server.activity.index.ActivityIndex; import org.sonar.server.db.DbClient; +import org.sonar.server.es.SearchOptions; import org.sonar.server.rule.index.RuleQuery; -import org.sonar.server.search.IndexClient; -import org.sonar.server.search.QueryContext; import org.sonar.server.search.Result; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; import javax.annotation.Nullable; - import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; @@ -53,7 +48,7 @@ import java.util.Map; public class QProfileService implements ServerComponent { private final DbClient db; - private final IndexClient index; + private final ActivityIndex activityIndex; private final RuleActivator ruleActivator; private final QProfileFactory factory; private final QProfileBackuper backuper; @@ -61,10 +56,10 @@ public class QProfileService implements ServerComponent { private final QProfileReset reset; private final QProfileExporters exporters; - public QProfileService(DbClient db, IndexClient index, RuleActivator ruleActivator, QProfileFactory factory, + public QProfileService(DbClient db, ActivityIndex activityIndex, RuleActivator ruleActivator, QProfileFactory factory, QProfileBackuper backuper, QProfileCopier copier, QProfileReset reset, QProfileExporters exporters) { this.db = db; - this.index = index; + this.activityIndex = activityIndex; this.ruleActivator = ruleActivator; this.factory = factory; this.backuper = backuper; @@ -214,23 +209,17 @@ public class QProfileService implements ServerComponent { UserSession.get().checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN); } - public Result<QProfileActivity> searchActivities(QProfileActivityQuery query, QueryContext options) { + public Result<QProfileActivity> searchActivities(QProfileActivityQuery query, SearchOptions options) { DbSession session = db.openSession(false); try { - OrFilterBuilder activityFilter = FilterBuilders.orFilter(); - for (String profileKey : query.getQprofileKeys()) { - activityFilter.add(FilterBuilders.nestedFilter("details", - QueryBuilders.matchQuery("details.profileKey", profileKey))); - } - - SearchResponse response = index.get(ActivityIndex.class).search(query, options, activityFilter); + SearchResponse response = activityIndex.doSearch(query, options); Result<QProfileActivity> result = new Result<QProfileActivity>(response); for (SearchHit hit : response.getHits().getHits()) { QProfileActivity profileActivity = new QProfileActivity(hit.getSource()); RuleDto ruleDto = db.ruleDao().getNullableByKey(session, profileActivity.ruleKey()); profileActivity.ruleName(ruleDto != null ? ruleDto.getName() : null); - String login = profileActivity.login(); + String login = profileActivity.getLogin(); if (login != null) { UserDto user = db.userDao().selectActiveUserByLogin(login, session); profileActivity.authorName(user != null ? user.getName() : null); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java index 96bc7511f2f..757334f7100 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java @@ -23,7 +23,6 @@ import com.google.common.base.Splitter; import com.google.common.collect.Lists; import org.sonar.api.ServerComponent; import org.sonar.api.server.rule.RuleParamType; -import org.sonar.core.activity.Activity; import org.sonar.core.persistence.DbSession; import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.core.qualityprofile.db.ActiveRuleKey; @@ -63,16 +62,16 @@ public class RuleActivator implements ServerComponent { private final TypeValidations typeValidations; private final RuleActivatorContextFactory contextFactory; private final IndexClient index; - private final ActivityService log; + private final ActivityService activityService; public RuleActivator(DbClient db, IndexClient index, RuleActivatorContextFactory contextFactory, TypeValidations typeValidations, - ActivityService log) { + ActivityService activityService) { this.db = db; this.index = index; this.contextFactory = contextFactory; this.typeValidations = typeValidations; - this.log = log; + this.activityService = activityService; } public List<ActiveRuleChange> activate(DbSession dbSession, RuleActivation activation, String profileKey) { @@ -240,7 +239,7 @@ public class RuleActivator implements ServerComponent { } else if (change.getType() == ActiveRuleChange.Type.UPDATED) { activeRule = doUpdate(change, context, dbSession); } - log.write(dbSession, Activity.Type.QPROFILE, change); + activityService.save(change.toActivity()); return activeRule; } 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 1a03ce18662..65d82a82998 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 @@ -55,6 +55,10 @@ import javax.annotation.Nullable; import java.io.Serializable; import java.util.*; +/** + * @deprecated replaced by {@link org.sonar.server.es.BaseIndex} + */ +@Deprecated public abstract class BaseIndex<DOMAIN, DTO extends Dto<KEY>, KEY extends Serializable> implements Index<DOMAIN, DTO, KEY> { @@ -215,7 +219,7 @@ public abstract class BaseIndex<DOMAIN, DTO extends Dto<KEY>, KEY extends Serial protected abstract String getKeyValue(KEY key); public final Settings getIndexSettings() { - ImmutableSettings.Builder settings = this.addCustomIndexSettings(this.getBaseIndexSettings()); + ImmutableSettings.Builder settings = this.getBaseIndexSettings(); // In case there is a replication factor set by the index, // it is removed since we're using global cluster state @@ -225,10 +229,6 @@ public abstract class BaseIndex<DOMAIN, DTO extends Dto<KEY>, KEY extends Serial return settings.build(); } - protected ImmutableSettings.Builder addCustomIndexSettings(ImmutableSettings.Builder baseIndexSettings) { - return baseIndexSettings; - } - protected abstract Map mapProperties(); protected abstract Map mapKey(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/IndexDefinition.java b/server/sonar-server/src/main/java/org/sonar/server/search/IndexDefinition.java index fc9b36ebe17..ffafa74a18a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/IndexDefinition.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/IndexDefinition.java @@ -40,7 +40,6 @@ public class IndexDefinition { public static final IndexDefinition RULE = new IndexDefinition("rules", "rule"); public static final IndexDefinition ACTIVE_RULE = new IndexDefinition("rules", "activeRule"); public static final IndexDefinition ISSUES = new IndexDefinition("issues", "issue"); - public static final IndexDefinition LOG = new IndexDefinition("logs", "sonarLog"); // Only used for test static final IndexDefinition TEST = new IndexDefinition("test", "test"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java b/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java index 2aaa1e874b7..b4499a4d20c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java @@ -23,7 +23,7 @@ package org.sonar.server.search; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.core.persistence.DbSession; -import org.sonar.server.activity.index.ActivityIndex; +import org.sonar.server.activity.index.ActivityIndexer; import org.sonar.server.db.Dao; import org.sonar.server.db.DbClient; import org.sonar.server.issue.index.IssueAuthorizationIndexer; @@ -47,9 +47,11 @@ public class IndexSynchronizer { private final IssueIndexer issueIndexer; private final UserIndexer userIndexer; private final ViewIndexer viewIndexer; + private final ActivityIndexer activityIndexer; public IndexSynchronizer(DbClient db, IndexClient index, SourceLineIndexer sourceLineIndexer, - IssueAuthorizationIndexer issueAuthorizationIndexer, IssueIndexer issueIndexer, UserIndexer userIndexer, ViewIndexer viewIndexer) { + IssueAuthorizationIndexer issueAuthorizationIndexer, IssueIndexer issueIndexer, + UserIndexer userIndexer, ViewIndexer viewIndexer, ActivityIndexer activityIndexer) { this.db = db; this.index = index; this.sourceLineIndexer = sourceLineIndexer; @@ -57,6 +59,7 @@ public class IndexSynchronizer { this.issueIndexer = issueIndexer; this.userIndexer = userIndexer; this.viewIndexer = viewIndexer; + this.activityIndexer = activityIndexer; } public void execute() { @@ -64,12 +67,14 @@ public class IndexSynchronizer { try { synchronize(session, db.ruleDao(), index.get(RuleIndex.class)); synchronize(session, db.activeRuleDao(), index.get(ActiveRuleIndex.class)); - synchronize(session, db.activityDao(), index.get(ActivityIndex.class)); session.commit(); } finally { session.close(); } + LOG.info("Index activities"); + activityIndexer.index(); + LOG.info("Index issues"); issueAuthorizationIndexer.index(); issueIndexer.index(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityBackendMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityBackendMediumTest.java deleted file mode 100644 index d89298a63db..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityBackendMediumTest.java +++ /dev/null @@ -1,215 +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.activity; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import org.elasticsearch.action.search.SearchResponse; -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.sonar.core.activity.Activity; -import org.sonar.core.activity.ActivityLog; -import org.sonar.core.persistence.DbSession; -import org.sonar.server.activity.db.ActivityDao; -import org.sonar.server.activity.index.ActivityIndex; -import org.sonar.server.activity.index.ActivityQuery; -import org.sonar.server.db.DbClient; -import org.sonar.server.platform.Platform; -import org.sonar.server.search.QueryContext; -import org.sonar.server.search.Result; -import org.sonar.server.tester.ServerTester; - -import java.util.Iterator; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ActivityBackendMediumTest { - - @ClassRule - public static ServerTester tester = new ServerTester(); - - ActivityService service = tester.get(ActivityService.class); - ActivityDao dao = tester.get(ActivityDao.class); - ActivityIndex index = tester.get(ActivityIndex.class); - DbSession dbSession; - - @Before - public void before() { - tester.clearDbAndIndexes(); - dbSession = tester.get(DbClient.class).openSession(false); - } - - @After - public void after() { - dbSession.close(); - } - - @Test - public void insert_find_text_log() throws InterruptedException { - final String testValue = "hello world"; - service.write(dbSession, Activity.Type.QPROFILE, testValue); - dbSession.commit(); - assertThat(index.findAll().getTotal()).isEqualTo(1); - - Activity activity = Iterables.getFirst(index.findAll().getHits(), null); - assertThat(activity).isNotNull(); - assertThat(activity.message()).isEqualTo(testValue); - } - - @Test - public void insert_find_loggable_log() { - final String testKey = "message"; - final String testValue = "hello world"; - service.write(dbSession, Activity.Type.QPROFILE, new ActivityLog() { - - @Override - public Map<String, String> getDetails() { - return ImmutableMap.of(testKey, testValue); - } - - @Override - public String getAction() { - return "myAction"; - } - }); - dbSession.commit(); - - assertThat(index.findAll().getTotal()).isEqualTo(1); - - Activity activity = Iterables.getFirst(index.findAll().getHits(), null); - assertThat(activity).isNotNull(); - assertThat(activity.details().get(testKey)).isEqualTo(testValue); - } - - @Test - public void current_time_zone() { - service.write(dbSession, Activity.Type.QPROFILE, "now"); - dbSession.commit(); - - Activity activity = service.search(new ActivityQuery(), new QueryContext()).getHits().get(0); - assertThat(System.currentTimeMillis() - activity.time().getTime()).isLessThan(1000L); - } - - @Test - public void massive_insert() { - - // 0 Assert no logs in DB - assertThat(dao.findAll(dbSession)).hasSize(0); - int max = 35; - final String testValue = "hello world"; - for (int i = 0; i < max; i++) { - - service.write(dbSession, Activity.Type.QPROFILE, testValue + "_" + i); - } - dbSession.commit(); - - // 1. assert both backends have all logs - assertThat(dao.findAll(dbSession)).hasSize(max); - assertThat(index.findAll().getHits()).hasSize(max); - - // 2. assert scrollable - int count = 0; - SearchResponse result = index.search(new ActivityQuery(), new QueryContext().setScroll(true)); - Iterator<Activity> logs = new Result<Activity>(index, result).scroll(); - while (logs.hasNext()) { - logs.next(); - count++; - } - assertThat(count).isEqualTo(max); - - // 3 assert synchronize above IndexQueue threshold - tester.clearIndexes(); - tester.get(Platform.class).executeStartupTasks(); - result = index.search(new ActivityQuery(), new QueryContext().setScroll(true)); - logs = new Result<Activity>(index, result).scroll(); - count = 0; - while (logs.hasNext()) { - logs.next(); - count++; - } - assertThat(count).isEqualTo(max); - - } - - @Test - public void massive_log_insert() { - - // 0 Assert no logs in DB - assertThat(dao.findAll(dbSession)).hasSize(0); - int max = 40; - final String testValue = "hello world"; - for (int i = 0; i < max; i++) { - TestActivityLog log = new TestActivityLog(testValue + "_" + i, Activity.Type.QPROFILE.toString()); - service.write(dbSession, Activity.Type.QPROFILE, log); - } - dbSession.commit(); - - // 1. assert both backends have all logs - assertThat(dao.findAll(dbSession)).hasSize(max); - assertThat(index.findAll().getHits()).hasSize(max); - - // 2. assert scrollable - int count = 0; - SearchResponse result = index.search(new ActivityQuery(), new QueryContext().setScroll(true)); - Iterator<Activity> logs = new Result<Activity>(index, result).scroll(); - while (logs.hasNext()) { - logs.next(); - count++; - } - assertThat(count).isEqualTo(max); - - // 3 assert synchronize above IndexQueue threshold - tester.clearIndexes(); - tester.get(Platform.class).executeStartupTasks(); - result = index.search(new ActivityQuery(), new QueryContext().setScroll(true)); - logs = new Result<Activity>(index, result).scroll(); - count = 0; - while (logs.hasNext()) { - logs.next(); - count++; - } - assertThat(count).isEqualTo(max); - - } - - class TestActivityLog implements ActivityLog { - - private final String name; - private final String action; - - TestActivityLog(String name, String action) { - this.name = name; - this.action = action; - } - - @Override - public Map<String, String> getDetails() { - return ImmutableMap.<String, String>of("name", name); - } - - @Override - public String getAction() { - return action; - } - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceMediumTest.java deleted file mode 100644 index 71fec3c117d..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceMediumTest.java +++ /dev/null @@ -1,217 +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.activity; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import org.elasticsearch.action.search.SearchResponse; -import org.joda.time.DateTime; -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.sonar.core.activity.Activity; -import org.sonar.core.activity.ActivityLog; -import org.sonar.core.activity.db.ActivityDto; -import org.sonar.core.persistence.DbSession; -import org.sonar.server.activity.db.ActivityDao; -import org.sonar.server.activity.index.ActivityIndex; -import org.sonar.server.activity.index.ActivityQuery; -import org.sonar.server.db.DbClient; -import org.sonar.server.search.QueryContext; -import org.sonar.server.search.Result; -import org.sonar.server.tester.ServerTester; - -import java.util.Iterator; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ActivityServiceMediumTest { - - @ClassRule - public static ServerTester tester = new ServerTester(); - - ActivityService service = tester.get(ActivityService.class); - ActivityDao dao = tester.get(ActivityDao.class); - ActivityIndex index = tester.get(ActivityIndex.class); - DbSession dbSession; - - @Before - public void before() { - tester.clearDbAndIndexes(); - dbSession = tester.get(DbClient.class).openSession(false); - } - - @After - public void after() { - dbSession.close(); - } - - @Test - public void find_all() throws InterruptedException { - service.write(dbSession, Activity.Type.QPROFILE, testValue); - dbSession.commit(); - assertThat(index.findAll().getTotal()).isEqualTo(1); - - Activity activity = Iterables.getFirst(index.findAll().getHits(), null); - assertThat(activity).isNotNull(); - assertThat(activity.message()).isEqualTo(testValue); - } - - @Test - public void search_message_log() throws InterruptedException { - service.write(dbSession, Activity.Type.QPROFILE, testValue); - dbSession.commit(); - assertThat(index.findAll().getTotal()).isEqualTo(1); - - SearchResponse result = index.search(service.newActivityQuery(), new QueryContext()); - assertThat(result.getHits().getTotalHits()).isEqualTo(1L); - Result<Activity> activityResult = new Result<Activity>(index, result); - assertThat(activityResult.getHits().get(0).message()).isEqualTo(testValue); - } - - @Test - public void search_activity_log() throws InterruptedException { - - service.write(dbSession, Activity.Type.QPROFILE, getActivity()); - dbSession.commit(); - assertThat(index.findAll().getTotal()).isEqualTo(1); - - SearchResponse result = index.search(service.newActivityQuery(), new QueryContext()); - assertThat(result.getHits().getTotalHits()).isEqualTo(1L); - Result<Activity> activityResult = new Result<Activity>(index, result); - assertThat(activityResult.getHits().get(0).details().get(test_key)).isEqualTo(test_value); - } - - @Test - public void filter_by_type() { - service.write(dbSession, Activity.Type.NONE, getActivity()); - service.write(dbSession, Activity.Type.SERVER, getActivity()); - service.write(dbSession, Activity.Type.SERVER, testValue); - service.write(dbSession, Activity.Type.QPROFILE, getActivity()); - dbSession.commit(); - - assertThat(service.search(new ActivityQuery(), - new QueryContext()).getHits()).hasSize(4); - - assertThat(service.search(new ActivityQuery() - .setTypes(ImmutableSet.of(Activity.Type.SERVER)), - new QueryContext()).getHits()).hasSize(2); - - assertThat(service.search(new ActivityQuery() - .setTypes(ImmutableSet.of(Activity.Type.QPROFILE)), - new QueryContext()).getHits()).hasSize(1); - } - - @Test - public void filter_by_date() throws InterruptedException { - - DateTime t0 = new DateTime().minusHours(1); - ActivityDto activity = getActivityDto(); - activity.setCreatedAt(t0.toDate()); - dao.insert(dbSession, activity); - activity = getActivityDto(); - activity.setCreatedAt(t0.toDate()); - dao.insert(dbSession, activity); - dbSession.commit(); - DateTime t1 = new DateTime(); - activity = getActivityDto(); - activity.setCreatedAt(t1.toDate()); - dao.insert(dbSession, activity); - dbSession.commit(); - DateTime t2 = new DateTime().plusHours(1); - - assertThat(service.search(new ActivityQuery(), - new QueryContext()).getHits()).hasSize(3); - - assertThat(service.search(new ActivityQuery() - .setSince(t0.minusSeconds(5).toDate()), - new QueryContext()).getHits()).hasSize(3); - - assertThat(service.search(new ActivityQuery() - .setSince(t1.minusSeconds(5).toDate()), - new QueryContext()).getHits()).hasSize(1); - - assertThat(service.search(new ActivityQuery() - .setSince(t2.minusSeconds(5).toDate()), - new QueryContext()).getHits()).hasSize(0); - - assertThat(service.search(new ActivityQuery() - .setTo(t1.minusSeconds(5).toDate()), - new QueryContext()).getHits()).hasSize(2); - - assertThat(service.search(new ActivityQuery() - .setSince(t1.minusSeconds(5).toDate()) - .setTo(t2.plusSeconds(5).toDate()), - new QueryContext()).getHits()).hasSize(1); - } - - private ActivityDto getActivityDto() { - return ActivityDto.createFor(testValue) - .setType(Activity.Type.NONE).setAuthor("testing"); - } - - @Test - public void iterate_all() throws InterruptedException { - int max = QueryContext.DEFAULT_LIMIT + 3; - final String testValue = "hello world"; - for (int i = 0; i < max; i++) { - service.write(dbSession, Activity.Type.QPROFILE, testValue + "_" + i); - } - dbSession.commit(); - - // 0. assert Base case - assertThat(dao.findAll(dbSession)).hasSize(max); - - SearchResponse result = index.search(service.newActivityQuery(), new QueryContext().setScroll(true)); - assertThat(result.getHits().getTotalHits()).isEqualTo(max); - Result<Activity> activityResult = new Result<Activity>(index, result); - - assertThat(activityResult.getTotal()).isEqualTo(max); - assertThat(activityResult.getHits()).hasSize(0); - int count = 0; - Iterator<Activity> logIterator = activityResult.scroll(); - while (logIterator.hasNext()) { - count++; - logIterator.next(); - } - assertThat(count).isEqualTo(max); - } - - final String test_key = "hello"; - final String test_value = "world"; - final String testValue = "hello world"; - - private ActivityLog getActivity() { - return new ActivityLog() { - @Override - public Map<String, String> getDetails() { - return ImmutableMap.of(test_key, test_value); - } - - @Override - public String getAction() { - return "myAction"; - } - }; - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceTest.java new file mode 100644 index 00000000000..58447946cd5 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceTest.java @@ -0,0 +1,91 @@ +/* + * 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.activity; + +import org.apache.commons.io.IOUtils; +import org.assertj.core.data.MapEntry; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.config.Settings; +import org.sonar.api.utils.System2; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.activity.db.ActivityDao; +import org.sonar.server.activity.index.ActivityDoc; +import org.sonar.server.activity.index.ActivityIndexDefinition; +import org.sonar.server.activity.index.ActivityIndexer; +import org.sonar.server.db.DbClient; +import org.sonar.server.es.EsTester; +import org.sonar.server.issue.db.IssueDao; + +import java.sql.Clob; +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 ActivityServiceTest { + + @ClassRule + public static DbTester db = new DbTester(); + + @ClassRule + public static EsTester es = new EsTester().addDefinitions(new ActivityIndexDefinition(new Settings())); + + System2 system = mock(System2.class); + ActivityService service; + + @Before + public void before() { + ActivityDao activityDao = new ActivityDao(db.myBatis(), system); + IssueDao issueDao = new IssueDao(db.myBatis()); + DbClient dbClient = new DbClient(db.database(), db.myBatis(), issueDao, activityDao); + service = new ActivityService(dbClient, new ActivityIndexer(dbClient, es.client())); + } + + @Test + public void insert_and_index() throws Exception { + when(system.now()).thenReturn(1_500_000_000_000L); + + Activity activity = new Activity(); + activity.setType(Activity.Type.ANALYSIS_REPORT); + activity.setAction("THE_ACTION"); + activity.setMessage("THE_MSG"); + activity.setData("foo", "bar"); + service.save(activity); + + Map<String, Object> dbMap = db.selectFirst("select log_type as \"type\", log_action as \"action\", log_message as \"msg\", data_field as \"data\" from activities"); + assertThat(dbMap).containsEntry("type", "ANALYSIS_REPORT"); + assertThat(dbMap).containsEntry("action", "THE_ACTION"); + assertThat(dbMap).containsEntry("msg", "THE_MSG"); + Clob data = (Clob) dbMap.get("data"); + assertThat(IOUtils.toString(data.getAsciiStream())).isEqualTo("foo=bar"); + + List<ActivityDoc> docs = es.getDocuments("activities", "activity", ActivityDoc.class); + assertThat(docs).hasSize(1); + assertThat(docs.get(0).getKey()).isNotEmpty(); + assertThat(docs.get(0).getAction()).isEqualTo("THE_ACTION"); + assertThat(docs.get(0).getMessage()).isEqualTo("THE_MSG"); + assertThat(docs.get(0).getDetails()).containsOnly(MapEntry.entry("foo", "bar")); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/RubyQProfileActivityServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/RubyQProfileActivityServiceTest.java index 10a2e55b65f..ef446052923 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/activity/RubyQProfileActivityServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/activity/RubyQProfileActivityServiceTest.java @@ -30,11 +30,10 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.utils.DateUtils; -import org.sonar.core.activity.Activity; +import org.sonar.server.es.SearchOptions; import org.sonar.server.qualityprofile.QProfileActivity; import org.sonar.server.qualityprofile.QProfileActivityQuery; import org.sonar.server.qualityprofile.QProfileService; -import org.sonar.server.search.QueryContext; import org.sonar.server.search.Result; import java.util.Date; @@ -53,7 +52,7 @@ public class RubyQProfileActivityServiceTest { ArgumentCaptor<QProfileActivityQuery> activityArgumentCaptor; @Captor - ArgumentCaptor<QueryContext> queryOptionsArgumentCaptor; + ArgumentCaptor<SearchOptions> queryOptionsArgumentCaptor; RubyQProfileActivityService rubyQProfileActivityService; @@ -70,16 +69,16 @@ public class RubyQProfileActivityServiceTest { Result<QProfileActivity> result = mock(Result.class); when(result.getHits()).thenReturn(Lists.<QProfileActivity>newArrayList()); when(result.getTotal()).thenReturn(10L); - when(service.searchActivities(any(QProfileActivityQuery.class), any(QueryContext.class))).thenReturn(result); + when(service.searchActivities(any(QProfileActivityQuery.class), any(SearchOptions.class))).thenReturn(result); - rubyQProfileActivityService.search(ImmutableMap.<String, Object>of("profileKeys", "PROFILE_KEY", "since", since, "to", to)); + rubyQProfileActivityService.search(ImmutableMap.<String, Object>of("profileKey", "PROFILE_KEY", "since", since, "to", to)); verify(service).searchActivities(activityArgumentCaptor.capture(), queryOptionsArgumentCaptor.capture()); assertThat(queryOptionsArgumentCaptor.getValue().getLimit()).isEqualTo(50); - assertThat(activityArgumentCaptor.getValue().getQprofileKeys()).containsOnly("PROFILE_KEY"); - assertThat(activityArgumentCaptor.getValue().getTypes()).containsOnly(Activity.Type.QPROFILE); + assertThat(activityArgumentCaptor.getValue().getQprofileKey()).isEqualTo("PROFILE_KEY"); + assertThat(activityArgumentCaptor.getValue().getTypes()).containsOnly(Activity.Type.QPROFILE.name()); assertThat(activityArgumentCaptor.getValue().getSince()).isEqualTo(since); assertThat(activityArgumentCaptor.getValue().getTo()).isEqualTo(to); } @@ -89,7 +88,7 @@ public class RubyQProfileActivityServiceTest { Result<QProfileActivity> result = mock(Result.class); when(result.getHits()).thenReturn(Lists.<QProfileActivity>newArrayList()); when(result.getTotal()).thenReturn(10L); - when(service.searchActivities(any(QProfileActivityQuery.class), any(QueryContext.class))).thenReturn(result); + when(service.searchActivities(any(QProfileActivityQuery.class), any(SearchOptions.class))).thenReturn(result); rubyQProfileActivityService.search(ImmutableMap.<String, Object>of()); @@ -97,7 +96,7 @@ public class RubyQProfileActivityServiceTest { assertThat(queryOptionsArgumentCaptor.getValue().getLimit()).isEqualTo(50); - assertThat(activityArgumentCaptor.getValue().getQprofileKeys()).isEmpty(); + assertThat(activityArgumentCaptor.getValue().getQprofileKey()).isNull(); assertThat(activityArgumentCaptor.getValue().getSince()).isNull(); assertThat(activityArgumentCaptor.getValue().getTo()).isNull(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/db/ActivityDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/db/ActivityDaoTest.java index 1a2cd967c17..9ebf997f9ac 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/activity/db/ActivityDaoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/activity/db/ActivityDaoTest.java @@ -19,116 +19,50 @@ */ package org.sonar.server.activity.db; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import org.junit.After; +import org.apache.commons.io.IOUtils; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; -import org.sonar.api.utils.KeyValueFormat; +import org.junit.experimental.categories.Category; import org.sonar.api.utils.System2; -import org.sonar.core.activity.Activity; -import org.sonar.core.activity.ActivityLog; import org.sonar.core.activity.db.ActivityDto; -import org.sonar.core.persistence.AbstractDaoTestCase; -import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.test.DbTests; +import java.sql.Clob; +import java.sql.Timestamp; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@Category(DbTests.class) +public class ActivityDaoTest { -public class ActivityDaoTest extends AbstractDaoTestCase { + @Rule + public DbTester dbTester = new DbTester(); - private ActivityDao dao; - private DbSession session; + System2 system = mock(System2.class); + ActivityDao sut; @Before public void before() throws Exception { - this.session = getMyBatis().openSession(false); - this.dao = new ActivityDao(mock(System2.class)); - } - - @After - public void after() { - session.close(); - } - - @Test - public void fail_insert_missing_type() { - String testValue = "hello world"; - ActivityDto log = ActivityDto.createFor(testValue); - try { - dao.insert(session, log); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).isEqualTo("Type must be set"); - } - } - - - @Test - public void fail_get_by_key() { - try { - dao.getByKey(session, "hello world"); - } catch (IllegalStateException e) { - assertThat(e.getMessage()).isEqualTo("Cannot execute getByKey on Activities in DB"); - } - } - - - @Test - public void fail_insert_missing_author() { - String testValue = "hello world"; - ActivityDto log = ActivityDto.createFor(testValue) - .setType(Activity.Type.QPROFILE); - try { - dao.insert(session, log); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage()).isEqualTo("Type must be set"); - } - } - - @Test - public void insert_text_log() { - String testValue = "hello world"; - ActivityDto log = ActivityDto.createFor(testValue) - .setType(Activity.Type.QPROFILE) - .setAuthor("jUnit"); - dao.insert(session, log); - - ActivityDto newDto = Iterables.getFirst(dao.findAll(session), null); - assertThat(newDto).isNotNull(); - assertThat(newDto.getAuthor()).isEqualTo(log.getAuthor()); - assertThat(newDto.getMessage()).isEqualTo(testValue); + sut = new ActivityDao(dbTester.myBatis(), system); } @Test - public void insert_loggable_log() { - final String testKey = "message"; - final String testValue = "hello world"; - ActivityDto log = ActivityDto.createFor(new ActivityLog() { - - @Override - public Map<String, String> getDetails() { - return ImmutableMap.of(testKey, testValue); - } - - @Override - public String getAction() { - return "myAction"; - } - }) - .setAuthor("jUnit") - .setType(Activity.Type.QPROFILE); - - dao.insert(session, log); - - ActivityDto newDto = Iterables.getFirst(dao.findAll(session), null); - assertThat(newDto).isNotNull(); - assertThat(newDto.getAuthor()).isEqualTo(log.getAuthor()); - assertThat(newDto.getData()).isNotNull(); - Map<String, String> details = KeyValueFormat.parse(newDto.getData()); - assertThat(details.get(testKey)).isEqualTo(testValue); + public void insert() throws Exception { + when(system.now()).thenReturn(1_500_000_000_000L); + ActivityDto dto = new ActivityDto() + .setKey("UUID_1").setAction("THE_ACTION").setType("THE_TYPE") + .setAuthor("THE_AUTHOR").setData("THE_DATA"); + sut.insert(dto); + + Map<String, Object> map = dbTester.selectFirst("select created_at as \"createdAt\", log_action as \"action\", data_field as \"data\" from activities where log_key='UUID_1'"); + assertThat(map.get("action")).isEqualTo("THE_ACTION"); + assertThat(((Timestamp)map.get("createdAt")).getTime()).isEqualTo(1_500_000_000_000L); + Clob data = (Clob) map.get("data"); + assertThat(IOUtils.toString(data.getAsciiStream())).isEqualTo("THE_DATA"); } - } diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexDefinitionTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexDefinitionTest.java new file mode 100644 index 00000000000..80ef2f2fba0 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexDefinitionTest.java @@ -0,0 +1,60 @@ +/* + * 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.activity.index; + +import org.junit.Test; +import org.sonar.api.config.Settings; +import org.sonar.process.ProcessConstants; +import org.sonar.server.es.IndexDefinition; +import org.sonar.server.es.NewIndex; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ActivityIndexDefinitionTest { + + IndexDefinition.IndexDefinitionContext context = new IndexDefinition.IndexDefinitionContext(); + + @Test + public void define() throws Exception { + ActivityIndexDefinition def = new ActivityIndexDefinition(new Settings()); + def.define(context); + + assertThat(context.getIndices()).hasSize(1); + NewIndex index = context.getIndices().get("activities"); + assertThat(index).isNotNull(); + assertThat(index.getTypes().keySet()).containsOnly("activity"); + + // no cluster by default + assertThat(index.getSettings().get("index.number_of_shards")).isEqualTo("1"); + assertThat(index.getSettings().get("index.number_of_replicas")).isEqualTo("0"); + } + + @Test + public void enable_cluster() throws Exception { + Settings settings = new Settings(); + settings.setProperty(ProcessConstants.CLUSTER_ACTIVATE, true); + ActivityIndexDefinition def = new ActivityIndexDefinition(settings); + def.define(context); + + NewIndex index = context.getIndices().get("activities"); + assertThat(index.getSettings().get("index.number_of_shards")).isEqualTo("4"); + assertThat(index.getSettings().get("index.number_of_replicas")).isEqualTo("1"); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexTest.java new file mode 100644 index 00000000000..8ace24aa377 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityIndexTest.java @@ -0,0 +1,111 @@ +/* + * 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.activity.index; + +import com.google.common.collect.ImmutableMap; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.config.Settings; +import org.sonar.server.activity.Activity; +import org.sonar.server.es.EsTester; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.es.SearchResult; + +import java.util.Arrays; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ActivityIndexTest { + + @ClassRule + public static EsTester es = new EsTester().addDefinitions(new ActivityIndexDefinition(new Settings())); + + ActivityIndex sut; + + @Before + public void before() { + sut = new ActivityIndex(es.client()); + } + + @Test + public void search_all() throws Exception { + es.putDocuments("activities", "activity", newDoc(1, 1_500_000_000_000L), newDoc(2, 1_600_000_000_000L)); + + SearchResult<ActivityDoc> results = sut.search(new ActivityQuery(), new SearchOptions()); + assertThat(results.getTotal()).isEqualTo(2L); + assertThat(results.getDocs()).hasSize(2); + assertThat(results.getDocs()).extracting("message").containsOnly("THE_MSG 1", "THE_MSG 2"); + } + + @Test + public void search_by_type() throws Exception { + es.putDocuments("activities", "activity", newDoc(1, 1_500_000_000_000L), newDoc(2, 1_600_000_000_000L)); + + ActivityQuery query = new ActivityQuery(); + query.setTypes(Arrays.asList("ANALYSIS_REPORT")); + assertThat(sut.search(query, new SearchOptions()).getTotal()).isEqualTo(2L); + + query = new ActivityQuery(); + query.setTypes(Arrays.asList("OTHER", "TYPES")); + assertThat(sut.search(query, new SearchOptions()).getTotal()).isEqualTo(0L); + } + + @Test + public void search_by_data() throws Exception { + es.putDocuments("activities", "activity", newDoc(1, 1_500_000_000_000L), newDoc(2, 1_600_000_000_000L)); + + ActivityQuery query = new ActivityQuery(); + query.addDataOrFilter("foo", "bar2"); + SearchResult<ActivityDoc> results = sut.search(query, new SearchOptions()); + assertThat(results.getDocs()).hasSize(1); + assertThat(results.getDocs().get(0).getKey()).isEqualTo("UUID2"); + } + + @Test + public void search_by_date() throws Exception { + es.putDocuments("activities", "activity", newDoc(1, 1_500_000_000_000L), newDoc(2, 1_600_000_000_000L)); + + ActivityQuery query = new ActivityQuery(); + query.setSince(new Date(1_550_000_000_000L)); + SearchResult<ActivityDoc> results = sut.search(query, new SearchOptions()); + assertThat(results.getDocs()).hasSize(1); + assertThat(results.getDocs().get(0).getKey()).isEqualTo("UUID2"); + + query = new ActivityQuery(); + query.setTo(new Date(1_550_000_000_000L)); + results = sut.search(query, new SearchOptions()); + assertThat(results.getDocs()).hasSize(1); + assertThat(results.getDocs().get(0).getKey()).isEqualTo("UUID1"); + } + + ActivityDoc newDoc(int id, long date) { + ActivityDoc doc = new ActivityDoc(); + doc.setKey("UUID" + id); + doc.setType(Activity.Type.ANALYSIS_REPORT.name()); + doc.setAction("THE_ACTION " + id); + doc.setMessage("THE_MSG " + id); + doc.setDetails(ImmutableMap.of("foo", "bar" + id)); + doc.setLogin("THE_GUY " + id); + doc.setCreatedAt(new Date(date)); + return doc; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityResultSetIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityResultSetIteratorTest.java new file mode 100644 index 00000000000..6a986068ae5 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/activity/index/ActivityResultSetIteratorTest.java @@ -0,0 +1,91 @@ +/* + * 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.activity.index; + +import org.apache.commons.dbutils.DbUtils; +import org.assertj.core.data.MapEntry; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.sonar.api.utils.DateUtils; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.db.DbClient; +import org.sonar.test.DbTests; + +import java.sql.Connection; + +import static org.assertj.core.api.Assertions.assertThat; + +@Category(DbTests.class) +public class ActivityResultSetIteratorTest { + + @ClassRule + public static DbTester dbTester = new DbTester(); + + DbClient client; + Connection connection; + + @Before + public void setUp() throws Exception { + dbTester.truncateTables(); + client = new DbClient(dbTester.database(), dbTester.myBatis()); + connection = dbTester.openConnection(); + } + + @After + public void tearDown() throws Exception { + DbUtils.closeQuietly(connection); + } + + @Test + public void traverse() throws Exception { + dbTester.prepareDbUnit(getClass(), "traverse.xml"); + ActivityResultSetIterator it = ActivityResultSetIterator.create(client, connection, 0L); + assertThat(it.hasNext()).isTrue(); + ActivityDoc doc = it.next(); + assertThat(doc).isNotNull(); + assertThat(doc.getKey()).isEqualTo("UUID1"); + assertThat(doc.getAction()).isEqualTo("THE_ACTION"); + assertThat(doc.getMessage()).isEqualTo("THE_MSG"); + assertThat(doc.getDetails()).containsOnly(MapEntry.entry("foo", "bar")); + assertThat(doc.getLogin()).isEqualTo("THE_AUTHOR"); + + assertThat(it.hasNext()).isTrue(); + assertThat(it.next()).isNotNull(); + assertThat(it.hasNext()).isFalse(); + it.close(); + } + + @Test + public void traverse_after_date() throws Exception { + dbTester.prepareDbUnit(getClass(), "traverse.xml"); + ActivityResultSetIterator it = ActivityResultSetIterator.create(client, connection, DateUtils.parseDate("2014-12-01").getTime()); + + assertThat(it.hasNext()).isTrue(); + ActivityDoc doc = it.next(); + assertThat(doc).isNotNull(); + assertThat(doc.getKey()).isEqualTo("UUID2"); + + assertThat(it.hasNext()).isFalse(); + it.close(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/activity/ws/ActivitiesWebServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/activity/ws/ActivitiesWebServiceMediumTest.java index 36920b23461..f5462dc69fa 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/activity/ws/ActivitiesWebServiceMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/activity/ws/ActivitiesWebServiceMediumTest.java @@ -19,15 +19,12 @@ */ package org.sonar.server.activity.ws; -import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.sonar.api.server.ws.WebService; -import org.sonar.core.activity.Activity; -import org.sonar.core.persistence.DbSession; +import org.sonar.server.activity.Activity; import org.sonar.server.activity.ActivityService; -import org.sonar.server.db.DbClient; import org.sonar.server.tester.ServerTester; import org.sonar.server.user.MockUserSession; import org.sonar.server.ws.WsTester; @@ -39,22 +36,14 @@ public class ActivitiesWebServiceMediumTest { @ClassRule public static ServerTester tester = new ServerTester(); - private ActivitiesWebService ws; - private ActivityService service; - private DbSession session; - + ActivitiesWebService ws; + ActivityService service; @Before public void setUp() throws Exception { tester.clearDbAndIndexes(); ws = tester.get(ActivitiesWebService.class); service = tester.get(ActivityService.class); - session = tester.get(DbClient.class).openSession(false); - } - - @After - public void after() { - session.close(); } @Test @@ -70,14 +59,20 @@ public class ActivitiesWebServiceMediumTest { } @Test - public void search_logs() throws Exception { - service.write(session, Activity.Type.QPROFILE, "Hello World"); - session.commit(); + public void search() throws Exception { + Activity activity = new Activity(); + activity.setType(Activity.Type.ANALYSIS_REPORT); + activity.setAction("THE_ACTION"); + activity.setMessage("THE_MSG"); + activity.setData("foo", "bar"); + service.save(activity); MockUserSession.set(); - // 1. List single Text log - WsTester.TestRequest request = tester.wsTester().newGetRequest(ActivitiesWebService.API_ENDPOINT, SearchAction.SEARCH_ACTION); + WsTester.TestRequest request = tester.wsTester().newGetRequest("api/activities", "search"); WsTester.Result result = request.execute(); + assertThat(result.outputAsString()).contains("\"total\":1"); + assertThat(result.outputAsString()).contains("\"type\":\"ANALYSIS_REPORT\""); + assertThat(result.outputAsString()).contains("\"details\":{\"foo\":\"bar\"}"); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java index 61d9cc7ab88..26682ddb642 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ComputationServiceTest.java @@ -52,10 +52,9 @@ import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; import org.sonar.batch.protocol.output.BatchOutputWriter; import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.core.activity.Activity; import org.sonar.core.computation.db.AnalysisReportDto; -import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbTester; +import org.sonar.server.activity.Activity; import org.sonar.server.activity.ActivityService; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.computation.step.ComputationStep; @@ -122,7 +121,7 @@ public class ComputationServiceTest { verify(projectStep1).execute(any(ComputationContext.class)); verify(projectStep2).execute(any(ComputationContext.class)); verify(viewStep, never()).execute(any(ComputationContext.class)); - verify(activityService).write(any(DbSession.class), eq(Activity.Type.ANALYSIS_REPORT), any(ReportActivity.class)); + verify(activityService).save(any(Activity.class)); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportActivityTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ReportActivityTest.java deleted file mode 100644 index 82ca71f828a..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ReportActivityTest.java +++ /dev/null @@ -1,60 +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.computation; - -import org.junit.Test; -import org.sonar.api.utils.DateUtils; -import org.sonar.core.component.ComponentDto; -import org.sonar.core.computation.db.AnalysisReportDto; -import org.sonar.server.component.ComponentTesting; - -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.core.computation.db.AnalysisReportDto.Status.FAILED; - -public class ReportActivityTest { - - @Test - public void insert_find_analysis_report_log() { - AnalysisReportDto report = AnalysisReportDto.newForTests(1L) - .setProjectKey("projectKey") - .setStatus(FAILED) - .setCreatedAt(DateUtils.parseDate("2014-10-15").getTime()) - .setUpdatedAt(DateUtils.parseDate("2014-10-16").getTime()) - .setStartedAt(DateUtils.parseDate("2014-10-17").getTime()) - .setFinishedAt(DateUtils.parseDate("2014-10-18").getTime()); - ComponentDto project = ComponentTesting.newProjectDto(); - - ReportActivity activity = new ReportActivity(report, project); - - Map<String, String> details = activity.getDetails(); - assertThat(details.get("key")).isEqualTo(String.valueOf(report.getId())); - assertThat(details.get("projectKey")).isEqualTo(project.key()); - assertThat(details.get("projectName")).isEqualTo(project.name()); - assertThat(details.get("projectUuid")).isEqualTo(project.uuid()); - assertThat(details.get("status")).isEqualTo("FAILED"); - assertThat(details.get("submittedAt")).isEqualTo("2014-10-15T00:00:00+0200"); - assertThat(details.get("startedAt")).isEqualTo("2014-10-17T00:00:00+0200"); - assertThat(details.get("finishedAt")).isEqualTo("2014-10-18T00:00:00+0200"); - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/HistoryWsActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/HistoryWsActionMediumTest.java index 6852087e43f..26fe86462b3 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/ws/HistoryWsActionMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/ws/HistoryWsActionMediumTest.java @@ -20,126 +20,71 @@ package org.sonar.server.computation.ws; -import org.apache.commons.io.IOUtils; -import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; -import org.sonar.api.security.DefaultGroups; -import org.sonar.api.web.UserRole; -import org.sonar.core.activity.Activity; -import org.sonar.core.component.ComponentDto; import org.sonar.core.computation.db.AnalysisReportDto; import org.sonar.core.permission.GlobalPermissions; -import org.sonar.core.permission.PermissionFacade; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.MyBatis; -import org.sonar.core.user.UserDto; +import org.sonar.server.activity.Activity; import org.sonar.server.activity.ActivityService; -import org.sonar.server.component.ComponentTesting; -import org.sonar.server.computation.ReportActivity; -import org.sonar.server.computation.ReportQueue; -import org.sonar.server.db.DbClient; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.tester.ServerTester; import org.sonar.server.user.MockUserSession; import org.sonar.server.ws.WsTester; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; +import java.util.Date; /** -* TODO replace this medium test by a small test -*/ + * TODO replace this medium test by a small test + */ public class HistoryWsActionMediumTest { - private static final String DEFAULT_PROJECT_KEY = "DefaultProjectKey"; - private static final String DEFAULT_PROJECT_NAME = "DefaultProjectName"; - private static final String DEFAULT_REPORT_DATA = "default-project"; @ClassRule public static ServerTester tester = new ServerTester(); - private DbClient dbClient; - private DbSession session; - private WsTester wsTester; - private ReportQueue queue; - private MockUserSession userSession; - private ActivityService activityService; + HistoryWsAction sut; + ActivityService activityService; @Before - public void before() { + public void setUp() throws Exception { tester.clearDbAndIndexes(); - dbClient = tester.get(DbClient.class); - wsTester = tester.get(WsTester.class); - session = dbClient.openSession(false); - queue = tester.get(ReportQueue.class); + sut = tester.get(HistoryWsAction.class); activityService = tester.get(ActivityService.class); - - UserDto connectedUser = new UserDto().setLogin("gandalf").setName("Gandalf"); - dbClient.userDao().insert(session, connectedUser); - - userSession = MockUserSession.set() - .setLogin(connectedUser.getLogin()) - .setUserId(connectedUser.getId().intValue()) - .setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); - } - - @After - public void after() { - MyBatis.closeQuietly(session); } @Test - public void add_and_try_to_retrieve_activities() throws Exception { - insertPermissionsForProject(DEFAULT_PROJECT_KEY); - queue.add(DEFAULT_PROJECT_KEY, IOUtils.toInputStream(DEFAULT_REPORT_DATA)); - queue.add(DEFAULT_PROJECT_KEY, IOUtils.toInputStream(DEFAULT_REPORT_DATA)); - queue.add(DEFAULT_PROJECT_KEY, IOUtils.toInputStream(DEFAULT_REPORT_DATA)); - - List<AnalysisReportDto> reports = queue.all(); - ComponentDto project = ComponentTesting.newProjectDto() - .setName(DEFAULT_PROJECT_NAME) - .setKey(DEFAULT_PROJECT_KEY); - for (AnalysisReportDto report : reports) { - report.succeed(); - activityService.write(session, Activity.Type.ANALYSIS_REPORT, new ReportActivity(report, project)); - } - - session.commit(); - userSession.setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); - - WsTester.TestRequest request = wsTester.newGetRequest(ComputationWebService.API_ENDPOINT, "history"); - WsTester.Result result = request.execute(); - - assertThat(result).isNotNull(); - result.assertJson(getClass(), "list_history_reports.json", false); - } - - private ComponentDto insertPermissionsForProject(String projectKey) { - ComponentDto project = new ComponentDto().setKey(projectKey).setId(1L); - dbClient.componentDao().insert(session, project); - - tester.get(PermissionFacade.class).insertGroupPermission(project.getId(), DefaultGroups.ANYONE, UserRole.USER, session); - userSession.addProjectPermissions(UserRole.ADMIN, project.key()); - userSession.addProjectPermissions(UserRole.USER, project.key()); - - session.commit(); - - return project; + public void search() throws Exception { + Activity activity1 = new Activity(); + activity1.setType(Activity.Type.ANALYSIS_REPORT); + activity1.setAction("LOG_ANALYSIS_REPORT"); + activity1.setData("projectKey", "P1"); + activity1.setData("projectName", "POne"); + activity1.setData("projectUuid", "U1"); + activity1.setData("status", AnalysisReportDto.Status.SUCCESS); + activity1.setData("submittedAt", new Date()); + activityService.save(activity1); + + Activity activity2 = new Activity(); + activity2.setType(Activity.Type.ANALYSIS_REPORT); + activity2.setAction("LOG_ANALYSIS_REPORT"); + activity2.setData("projectKey", "P2"); + activity2.setData("projectName", "PTwo"); + activity2.setData("projectUuid", "U2"); + activity2.setData("status", AnalysisReportDto.Status.FAILED); + activity2.setData("submittedAt", new Date()); + activityService.save(activity2); + + MockUserSession.set().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); + + WsTester.TestRequest request = tester.wsTester().newGetRequest("api/computation", "history"); + request.execute().assertJson(getClass(), "list_history_reports.json", false); } @Test(expected = ForbiddenException.class) - public void user_rights_is_not_enough_throw_ForbiddenException() throws Exception { - insertPermissionsForProject(DEFAULT_PROJECT_KEY); - queue.add(DEFAULT_PROJECT_KEY, IOUtils.toInputStream(DEFAULT_REPORT_DATA)); - - AnalysisReportDto report = queue.all().get(0); - report.succeed(); - // queue.remove(report); - userSession.setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); + public void requires_admin_right() throws Exception { + MockUserSession.set(); - WsTester.TestRequest sut = wsTester.newGetRequest(ComputationWebService.API_ENDPOINT, "history"); - sut.execute(); + WsTester.TestRequest request = tester.wsTester().newGetRequest("api/computation", "history"); + request.execute(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/ChangeLogMigrationTest.java b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/ChangeLogMigrationTest.java index 699581a22b6..ceb22da3b6a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/ChangeLogMigrationTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/db/migrations/v44/ChangeLogMigrationTest.java @@ -23,31 +23,23 @@ import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.System2; -import org.sonar.core.activity.db.ActivityDto; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbTester; import org.sonar.server.activity.db.ActivityDao; import org.sonar.server.db.DbClient; -import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) public class ChangeLogMigrationTest { @ClassRule public static DbTester db = new DbTester().schema(ChangeLogMigrationTest.class, "schema.sql"); - @Mock - System2 system2; - + System2 system2 = mock(System2.class); DbClient dbClient; ActivityDao dao; ChangeLogMigration migration; @@ -56,7 +48,7 @@ public class ChangeLogMigrationTest { @Before public void setUp() throws Exception { when(system2.now()).thenReturn(DateUtils.parseDate("2014-03-13").getTime()); - dao = new ActivityDao(system2); + dao = new ActivityDao(db.myBatis(), system2); dbClient = new DbClient(db.database(), db.myBatis(), dao); migration = new ChangeLogMigration(dao, dbClient); session = dbClient.openSession(false); @@ -71,10 +63,10 @@ public class ChangeLogMigrationTest { public void migrate() throws Exception { db.prepareDbUnit(getClass(), "active_rules_changes.xml"); migration.execute(); - assertThat(dao.findAll(session)).hasSize(5); + assertThat(db.countRowsOfTable("activities")).isEqualTo(5); - List<ActivityDto> changes = dao.findAll(session); - assertThat(changes.get(1).getData()).contains("param_PARAM1=TODO"); + int count = db.countSql("select count(*) from activities where data_field like '%param_PARAM1=TODO%'"); + assertThat(count).isGreaterThan(0); } @Test @@ -82,6 +74,6 @@ public class ChangeLogMigrationTest { db.prepareDbUnit(getClass(), "migrate_when_no_changelog.xml"); migration.execute(); - assertThat(dao.findAll(session)).isEmpty(); + assertThat(db.countRowsOfTable("activities")).isEqualTo(0); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ActiveRuleChangeMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ActiveRuleChangeMediumTest.java deleted file mode 100644 index c57d29fb178..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ActiveRuleChangeMediumTest.java +++ /dev/null @@ -1,77 +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.qualityprofile; - -import com.google.common.collect.Iterables; -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.sonar.api.rule.RuleKey; -import org.sonar.core.activity.Activity; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.qualityprofile.db.ActiveRuleKey; -import org.sonar.server.activity.ActivityService; -import org.sonar.server.activity.index.ActivityIndex; -import org.sonar.server.db.DbClient; -import org.sonar.server.tester.ServerTester; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ActiveRuleChangeMediumTest { - - @ClassRule - public static ServerTester tester = new ServerTester(); - - ActivityService service = tester.get(ActivityService.class); - ActivityIndex index = tester.get(ActivityIndex.class); - DbSession dbSession; - - @Before - public void before() { - tester.clearDbAndIndexes(); - dbSession = tester.get(DbClient.class).openSession(false); - } - - @After - public void after() { - dbSession.close(); - } - - @Test - public void insert_find_active_rule_change() { - ActiveRuleKey key = ActiveRuleKey.of("XOO_P1", RuleKey.of("xoo", "X1")); - ActiveRuleChange change = ActiveRuleChange - .createFor(ActiveRuleChange.Type.ACTIVATED, key) - .setInheritance(ActiveRule.Inheritance.INHERITED) - .setSeverity("BLOCKER") - .setParameter("param1", "value1"); - - service.write(dbSession, Activity.Type.QPROFILE, change); - dbSession.commit(); - - // 0. AssertBase case - assertThat(index.findAll().getHits()).hasSize(1); - - Activity activity = Iterables.getFirst(index.findAll().getHits(), null); - assertThat(activity).isNotNull(); - assertThat(activity.details().get("key")).isEqualTo(key.toString()); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java index 2015af9ab51..034bb052ef9 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileServiceMediumTest.java @@ -20,7 +20,6 @@ package org.sonar.server.qualityprofile; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import org.junit.After; @@ -36,19 +35,19 @@ import org.sonar.api.rule.Severity; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RulePriority; import org.sonar.api.utils.ValidationMessages; -import org.sonar.core.activity.Activity; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; import org.sonar.core.qualityprofile.db.ActiveRuleKey; import org.sonar.core.qualityprofile.db.QualityProfileDto; import org.sonar.core.rule.RuleDto; import org.sonar.core.user.UserDto; +import org.sonar.server.activity.Activity; import org.sonar.server.activity.ActivityService; import org.sonar.server.db.DbClient; +import org.sonar.server.es.SearchOptions; import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer; import org.sonar.server.rule.RuleTesting; 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; @@ -187,24 +186,23 @@ public class QProfileServiceMediumTest { // We need an actual rule in DB to test RuleName in Activity RuleDto rule = db.ruleDao().getByKey(dbSession, RuleTesting.XOO_X1); - tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE, - ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1)) - .setSeverity(Severity.MAJOR) - .setParameter("max", "10") - ); dbSession.commit(); - Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new QueryContext()); + tester.get(ActivityService.class).save(ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1)) + .setSeverity(Severity.MAJOR) + .setParameter("max", "10").toActivity()); + + Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new SearchOptions()); assertThat(activities.getHits()).hasSize(1); QProfileActivity activity = activities.getHits().get(0); - assertThat(activity.type()).isEqualTo(Activity.Type.QPROFILE); - assertThat(activity.action()).isEqualTo(ActiveRuleChange.Type.ACTIVATED.name()); + assertThat(activity.getType()).isEqualTo(Activity.Type.QPROFILE.name()); + assertThat(activity.getAction()).isEqualTo(ActiveRuleChange.Type.ACTIVATED.name()); assertThat(activity.ruleKey()).isEqualTo(RuleTesting.XOO_X1); assertThat(activity.profileKey()).isEqualTo(XOO_P1_KEY); assertThat(activity.severity()).isEqualTo(Severity.MAJOR); assertThat(activity.ruleName()).isEqualTo(rule.getName()); - assertThat(activity.login()).isEqualTo("david"); + assertThat(activity.getLogin()).isEqualTo("david"); assertThat(activity.authorName()).isEqualTo("David"); assertThat(activity.parameters()).hasSize(1); @@ -217,13 +215,11 @@ public class QProfileServiceMediumTest { RuleKey ruleKey = RuleKey.of("xoo", "deleted_rule"); - tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE, - ActiveRuleChange.createFor(ActiveRuleChange.Type.UPDATED, ActiveRuleKey.of(XOO_P1_KEY, ruleKey)) - .setParameter("max", "10") + tester.get(ActivityService.class).save(ActiveRuleChange.createFor(ActiveRuleChange.Type.UPDATED, ActiveRuleKey.of(XOO_P1_KEY, ruleKey)) + .setParameter("max", "10").toActivity() ); - dbSession.commit(); - Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new QueryContext()); + Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new SearchOptions()); assertThat(activities.getHits()).hasSize(1); QProfileActivity activity = activities.getHits().get(0); @@ -238,19 +234,20 @@ public class QProfileServiceMediumTest { // We need an actual rule in DB to test RuleName in Activity db.ruleDao().getByKey(dbSession, RuleTesting.XOO_X1); + dbSession.commit(); - tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE, + tester.get(ActivityService.class).save( ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1)) .setSeverity(Severity.MAJOR) .setParameter("max", "10") + .toActivity() ); - dbSession.commit(); - Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new QueryContext()); + Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new SearchOptions()); assertThat(activities.getHits()).hasSize(1); QProfileActivity activity = activities.getHits().get(0); - assertThat(activity.login()).isEqualTo("david"); + assertThat(activity.getLogin()).isEqualTo("david"); assertThat(activity.authorName()).isNull(); } @@ -260,14 +257,14 @@ public class QProfileServiceMediumTest { RuleKey ruleKey = RuleKey.of("xoo", "deleted_rule"); - tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE, - ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, ruleKey)) - .setSeverity(Severity.MAJOR) - .setParameter("max", "10") + tester.get(ActivityService.class).save(ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, ruleKey)) + .setSeverity(Severity.MAJOR) + .setParameter("max", "10") + .toActivity() ); dbSession.commit(); - Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new QueryContext()); + Result<QProfileActivity> activities = service.searchActivities(new QProfileActivityQuery(), new SearchOptions()); assertThat(activities.getHits()).hasSize(1); QProfileActivity activity = activities.getHits().get(0); @@ -278,48 +275,36 @@ public class QProfileServiceMediumTest { @Test public void search_activity_by_qprofile() throws InterruptedException { - tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE, - ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1))); - tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE, - ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P2_KEY, RuleTesting.XOO_X1))); - dbSession.commit(); + tester.get(ActivityService.class).save( + ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P1_KEY, RuleTesting.XOO_X1)).toActivity()); + tester.get(ActivityService.class).save( + ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of(XOO_P2_KEY, RuleTesting.XOO_X1)).toActivity()); // 0. Base case verify 2 activities in index - assertThat(service.searchActivities(new QProfileActivityQuery(), new QueryContext()).getHits()) + assertThat(service.searchActivities(new QProfileActivityQuery(), new SearchOptions()).getHits()) .hasSize(2); // 1. filter by QProfile List<QProfileActivity> result = service.searchActivities(new QProfileActivityQuery() - .setQprofileKeys(ImmutableSet.of(XOO_P1_KEY)), new QueryContext()).getHits(); + .setQprofileKey(XOO_P1_KEY), new SearchOptions()).getHits(); assertThat(result).hasSize(1); - - // 1. filter by QProfiles - assertThat(service.searchActivities(new QProfileActivityQuery() - .setQprofileKeys(ImmutableSet.of(XOO_P1_KEY, XOO_P2_KEY)) - , new QueryContext()).getHits()).hasSize(2); } @Test public void search_activity_by_qprofile_having_dashes_in_keys() throws InterruptedException { - tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE, - ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of("java-default", RuleTesting.XOO_X1))); - tester.get(ActivityService.class).write(dbSession, Activity.Type.QPROFILE, - ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of("java-toto", RuleTesting.XOO_X1))); - dbSession.commit(); + tester.get(ActivityService.class).save( + ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of("java-default", RuleTesting.XOO_X1)).toActivity()); + tester.get(ActivityService.class).save( + ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, ActiveRuleKey.of("java-toto", RuleTesting.XOO_X1)).toActivity()); // 0. Base case verify 2 activities in index - assertThat(service.searchActivities(new QProfileActivityQuery(), new QueryContext()).getHits()) + assertThat(service.searchActivities(new QProfileActivityQuery(), new SearchOptions()).getHits()) .hasSize(2); // 1. filter by QProfile List<QProfileActivity> result = service.searchActivities(new QProfileActivityQuery() - .setQprofileKeys(ImmutableSet.of("java-default")), new QueryContext()).getHits(); + .setQprofileKey("java-default"), new SearchOptions()).getHits(); assertThat(result).hasSize(1); - - // 1. filter by QProfiles - assertThat(service.searchActivities(new QProfileActivityQuery() - .setQprofileKeys(ImmutableSet.of("java-default", "java-toto")) - , new QueryContext()).getHits()).hasSize(2); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/db/RuleDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/db/RuleDaoTest.java index 15ce8518841..2b82eb2aaa9 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/db/RuleDaoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/db/RuleDaoTest.java @@ -34,9 +34,7 @@ import org.sonar.core.persistence.DbSession; import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleDto.Format; import org.sonar.core.rule.RuleParamDto; -import org.sonar.server.rule.RuleTesting; -import java.util.Date; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -368,31 +366,4 @@ public class RuleDaoTest extends AbstractDaoTestCase { checkTables("update_parameter", "rules_parameters"); } - - @Test - public void findAfterDate() throws Exception { - long t0 = DateUtils.parseDate("2014-01-01").getTime(); - when(system2.now()).thenReturn(t0); - dao.insert(session, RuleTesting.newXooX1()); - session.commit(); - assertThat(dao.getByKey(session, RuleTesting.XOO_X1).getCreatedAt().after(new Date(t0))); - - - long t1 = DateUtils.parseDate("2014-02-01").getTime(); - when(system2.now()).thenReturn(t1); - dao.insert(session, RuleTesting.newXooX2()); - session.commit(); - assertThat(dao.getByKey(session, RuleTesting.XOO_X2).getCreatedAt().after(new Date(t1))); - - long t2 = DateUtils.parseDate("2014-03-01").getTime(); - when(system2.now()).thenReturn(t2); - session.flushStatements(); - - - assertThat(dao.findAll(session)).hasSize(2); - assertThat(dao.findAfterDate(session, new Date(0))).hasSize(2); - assertThat(dao.findAfterDate(session, new Date(t0))).hasSize(2); - assertThat(dao.findAfterDate(session, new Date(t1))).hasSize(1); - assertThat(dao.findAfterDate(session, new Date(t2))).hasSize(0); - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java index 9eeecef377d..006e153ef06 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/search/BaseIndexTest.java @@ -20,7 +20,6 @@ package org.sonar.server.search; import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse; -import org.elasticsearch.common.settings.ImmutableSettings; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; @@ -91,11 +90,6 @@ public class BaseIndexTest { null, searchClient) { @Override - protected ImmutableSettings.Builder addCustomIndexSettings(ImmutableSettings.Builder baseIndexSettings) { - return baseIndexSettings.put("index.number_of_replicas", 22); - } - - @Override protected String getKeyValue(Serializable key) { return null; } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/activity/index/ActivityResultSetIteratorTest/traverse.xml b/server/sonar-server/src/test/resources/org/sonar/server/activity/index/ActivityResultSetIteratorTest/traverse.xml new file mode 100644 index 00000000000..f92f7678379 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/activity/index/ActivityResultSetIteratorTest/traverse.xml @@ -0,0 +1,7 @@ +<dataset> + <activities id="1" log_key="UUID1" log_type="ANALYSIS_REPORT" log_action="THE_ACTION" log_message="THE_MSG" + created_at="2014-01-01" data_field="foo=bar" user_login="THE_AUTHOR"/> + + <activities id="2" log_key="UUID2" log_type="ANALYSIS_REPORT" log_action="THE_ACTION" log_message="THE_MSG" + created_at="2015-01-01" data_field="foo=bar" user_login="THE_AUTHOR"/> +</dataset> diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/ws/HistoryWsActionMediumTest/list_history_reports.json b/server/sonar-server/src/test/resources/org/sonar/server/computation/ws/HistoryWsActionMediumTest/list_history_reports.json index b0ed60a0876..caa180819a4 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/computation/ws/HistoryWsActionMediumTest/list_history_reports.json +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/ws/HistoryWsActionMediumTest/list_history_reports.json @@ -2,18 +2,15 @@ "reports": [ { "status": "SUCCESS", - "projectName": "DefaultProjectName", - "projectKey": "DefaultProjectKey" + "projectName": "POne", + "projectKey": "P1", + "projectUuid": "U1" }, { - "status": "SUCCESS", - "projectName": "DefaultProjectName", - "projectKey": "DefaultProjectKey" - }, - { - "status": "SUCCESS", - "projectName": "DefaultProjectName", - "projectKey": "DefaultProjectKey" + "status": "FAILED", + "projectName": "PTwo", + "projectKey": "P2", + "projectUuid": "U2" } ] -}
\ No newline at end of file +} diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb index 3ac6eba4b8d..2b107edfefb 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/profiles_controller.rb @@ -268,7 +268,7 @@ class ProfilesController < ApplicationController require_parameters 'key' @profile = Internal.qprofile_loader.getByKey(params[:key]) - search = {'profileKeys' => @profile.key().to_s, 'since' => params[:since], 'to' => params[:to], 'p' => params[:p]} + search = {'profileKey' => @profile.key().to_s, 'since' => params[:since], 'to' => params[:to], 'p' => params[:p]} result = Internal.component(Java::OrgSonarServerActivity::RubyQProfileActivityService.java_class).search(search) @changes = result.activities @paging = result.paging |