From 65dd651ea8e3532954e4d73ae82c3b8207f5c571 Mon Sep 17 00:00:00 2001 From: Stephane Gamard Date: Mon, 16 Jun 2014 13:21:25 +0200 Subject: [PATCH] SONAR-5329 - Rename Log package to Activity --- .../sonar/core/activity/db/ActivityMapper.xml | 37 +++++ .../server/activity/ActivityService.java | 89 ++++++++++++ .../sonar/server/activity/db/ActivityDao.java | 76 ++++++++++ .../server/activity/index/ActivityDoc.java | 67 +++++++++ .../server/activity/index/ActivityIndex.java | 112 +++++++++++++++ .../activity/index/ActivityNormalizer.java | 113 +++++++++++++++ .../server/activity/index/ActivityQuery.java | 62 +++++++++ .../activity/ws/ActivitiesWebService.java | 43 ++++++ .../server/activity/ws/ActivityMapping.java | 62 +++++++++ .../server/activity/ws/SearchAction.java | 98 +++++++++++++ .../activity/ActivityBackendMediumTest.java | 131 ++++++++++++++++++ .../activity/ActivityServiceMediumTest.java | 112 +++++++++++++++ .../server/activity/db/ActivityDaoTest.java | 126 +++++++++++++++++ .../ws/ActivitiesWebServiceMediumTest.java | 86 ++++++++++++ 14 files changed, 1214 insertions(+) create mode 100644 sonar-core/src/main/resources/org/sonar/core/activity/db/ActivityMapper.xml create mode 100644 sonar-server/src/main/java/org/sonar/server/activity/ActivityService.java create mode 100644 sonar-server/src/main/java/org/sonar/server/activity/db/ActivityDao.java create mode 100644 sonar-server/src/main/java/org/sonar/server/activity/index/ActivityDoc.java create mode 100644 sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndex.java create mode 100644 sonar-server/src/main/java/org/sonar/server/activity/index/ActivityNormalizer.java create mode 100644 sonar-server/src/main/java/org/sonar/server/activity/index/ActivityQuery.java create mode 100644 sonar-server/src/main/java/org/sonar/server/activity/ws/ActivitiesWebService.java create mode 100644 sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java create mode 100644 sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java create mode 100644 sonar-server/src/test/java/org/sonar/server/activity/ActivityBackendMediumTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceMediumTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/activity/db/ActivityDaoTest.java create mode 100644 sonar-server/src/test/java/org/sonar/server/activity/ws/ActivitiesWebServiceMediumTest.java diff --git a/sonar-core/src/main/resources/org/sonar/core/activity/db/ActivityMapper.xml b/sonar-core/src/main/resources/org/sonar/core/activity/db/ActivityMapper.xml new file mode 100644 index 00000000000..14e061dbaae --- /dev/null +++ b/sonar-core/src/main/resources/org/sonar/core/activity/db/ActivityMapper.xml @@ -0,0 +1,37 @@ + + + + + + + insert into logs + (created_at, log_type,execution_time_field,user_login,data_field, log_message) + values (#{createdAt}, #{type}, #{executionTime}, #{author}, #{data}, #{message}) + + + + + + + diff --git a/sonar-server/src/main/java/org/sonar/server/activity/ActivityService.java b/sonar-server/src/main/java/org/sonar/server/activity/ActivityService.java new file mode 100644 index 00000000000..7f7149d3acc --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/activity/ActivityService.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; + +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.index.ActivityIndex; +import org.sonar.server.activity.index.ActivityQuery; +import org.sonar.server.db.DbClient; +import org.sonar.server.search.IndexClient; +import org.sonar.server.search.QueryOptions; +import org.sonar.server.search.Result; +import org.sonar.server.user.UserSession; + +import java.util.List; + +/** + * Log service is used to log Activity classes which represents an event to DB and Index. + * + * @see org.sonar.core.activity.ActivityLog + * @since 4.4 + */ +public class ActivityService { + + private final DbClient dbClient; + private final IndexClient indexClient; + + public ActivityService(DbClient dbClient, IndexClient indexClient) { + this.dbClient = dbClient; + this.indexClient = indexClient; + } + + private String getAuthor() { + return (UserSession.get().login() != null) ? UserSession.get().login() : "UNKNOWN"; + } + + private void save(DbSession session, ActivityDto log) { + dbClient.activityDao().insert(session, + log.setAuthor(getAuthor())); + } + + public void write(DbSession session, Activity.Type type, String message) { + this.write(session, type, message, null); + } + + public void write(DbSession session, Activity.Type type, String message, Integer time) { + this.save(session, ActivityDto.createFor(message) + .setType(type) + .setExecutionTime(time)); + } + + public void write(DbSession session, Activity.Type type, List logs) { + for (ActivityLog log : logs) { + this.write(session, type, log); + } + } + + public void write(DbSession session, Activity.Type type, L log) { + this.save(session, ActivityDto.createFor(log) + .setType(type)); + } + + public ActivityQuery newActivityQuery() { + return new ActivityQuery(); + } + + public Result search(ActivityQuery query, QueryOptions options) { + return indexClient.get(ActivityIndex.class).search(query, options); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/activity/db/ActivityDao.java b/sonar-server/src/main/java/org/sonar/server/activity/db/ActivityDao.java new file mode 100644 index 00000000000..d009113dcf9 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/activity/db/ActivityDao.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.db; + +import com.google.common.annotations.VisibleForTesting; +import org.sonar.api.utils.System2; +import org.sonar.core.activity.db.ActivityDto; +import org.sonar.core.activity.db.ActivityKey; +import org.sonar.core.activity.db.ActivityMapper; +import org.sonar.core.persistence.DbSession; +import org.sonar.server.db.BaseDao; +import org.sonar.server.search.IndexDefinition; + +import java.util.List; + +/** + * @since 4.4 + */ +public class ActivityDao extends BaseDao { + + public ActivityDao() { + this(System2.INSTANCE); + } + + @VisibleForTesting + public ActivityDao(System2 system) { + super(IndexDefinition.LOG, ActivityMapper.class, system); + } + + @Override + protected ActivityDto doGetNullableByKey(DbSession session, ActivityKey key) { + return mapper(session).selectByKey(key); + } + + @Override + protected ActivityDto doInsert(DbSession session, ActivityDto item) { + mapper(session).insert(item); + return item; + } + + @Override + protected ActivityDto doUpdate(DbSession session, ActivityDto item) { + throw new IllegalStateException("Cannot update Log!"); + } + + @Override + protected void doDeleteByKey(DbSession session, ActivityKey key) { + throw new IllegalStateException("Cannot delete Log!"); + } + + public List findAll(DbSession session) { + return mapper(session).selectAll(); + } + + @Override + public void synchronizeAfter(DbSession session, long timestamp) { + throw new IllegalStateException("Log Index does not synchronize!"); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityDoc.java b/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityDoc.java new file mode 100644 index 00000000000..e224864ce60 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityDoc.java @@ -0,0 +1,67 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.activity.index; + +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.sonar.core.activity.Activity; +import org.sonar.server.search.BaseDoc; + +import java.util.Date; +import java.util.Map; + +/** + * @since 4.4 + */ +public class ActivityDoc extends BaseDoc implements Activity { + + protected ActivityDoc(Map fields) { + super(fields); + } + + @Override + public Date time() { + return this.getField(ActivityNormalizer.LogFields.DATE.field()); + } + + @Override + public String author() { + return this.getField(ActivityNormalizer.LogFields.AUTHOR.field()); + } + + @Override + public Integer executionTime() { + return this.getField(ActivityNormalizer.LogFields.EXECUTION.field()); + } + + @Override + public Map details() { + return this.getField(ActivityNormalizer.LogFields.DETAILS.field()); + } + + @Override + public String message() { + return this.getField(ActivityNormalizer.LogFields.MESSAGE.field()); + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndex.java b/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndex.java new file mode 100644 index 00000000000..6d5f36bf2fc --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndex.java @@ -0,0 +1,112 @@ +/* + * 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.search.SearchRequestBuilder; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.search.SearchType; +import org.elasticsearch.common.settings.ImmutableSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.index.query.QueryBuilders; +import org.sonar.core.activity.Activity; +import org.sonar.core.activity.db.ActivityDto; +import org.sonar.core.activity.db.ActivityKey; +import org.sonar.core.cluster.WorkQueue; +import org.sonar.core.profiling.Profiling; +import org.sonar.server.search.BaseIndex; +import org.sonar.server.search.ESNode; +import org.sonar.server.search.IndexDefinition; +import org.sonar.server.search.IndexField; +import org.sonar.server.search.QueryOptions; +import org.sonar.server.search.Result; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * @since 4.4 + */ +public class ActivityIndex extends BaseIndex { + + public ActivityIndex(Profiling profiling, ActivityNormalizer normalizer, WorkQueue workQueue, ESNode node) { + super(IndexDefinition.LOG, normalizer, workQueue, node, profiling); + } + + @Override + protected String getKeyValue(ActivityKey key) { + // FIXME too many collision with key.toString() due to lack of time precision + return null;// return key.toString(); + } + + @Override + protected Map mapKey() { + return null; + // Map mapping = new HashMap(); + // return mapping; + } + + @Override + protected Settings getIndexSettings() throws IOException { + return ImmutableSettings.builder().build(); + } + + @Override + protected Map mapProperties() { + Map mapping = new HashMap(); + for (IndexField field : ActivityNormalizer.LogFields.ALL_FIELDS) { + mapping.put(field.field(), mapField(field)); + } + return mapping; + } + + @Override + protected Activity toDoc(final Map fields) { + return new ActivityDoc(fields); + } + + public Result findAll() { + return new Result(this, getClient().prepareSearch(this.getIndexName()) + .setQuery(QueryBuilders.matchAllQuery()) + .setTypes(this.getIndexType()) + .setSize(Integer.MAX_VALUE) + .get()); + } + + public Result search(ActivityQuery query, QueryOptions options) { + SearchRequestBuilder esSearch = getClient() + .prepareSearch(this.getIndexName()) + .setTypes(this.getIndexType()) + .setIndices(this.getIndexName()); + + // TODO implement query and filters based on LogQuery + esSearch.setQuery(QueryBuilders.matchAllQuery()); + + if (options.isScroll()) { + esSearch.setSearchType(SearchType.SCAN); + esSearch.setScroll(TimeValue.timeValueMinutes(3)); + } + + SearchResponse esResult = esSearch.get(); + + return new Result(this, esResult); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityNormalizer.java b/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityNormalizer.java new file mode 100644 index 00000000000..ebfb71007f2 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityNormalizer.java @@ -0,0 +1,113 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.activity.index; + +import com.google.common.collect.ImmutableList; +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.core.activity.db.ActivityKey; +import org.sonar.core.persistence.DbSession; +import org.sonar.server.db.DbClient; +import org.sonar.server.search.BaseNormalizer; +import org.sonar.server.search.IndexDefinition; +import org.sonar.server.search.IndexField; +import org.sonar.server.search.Indexable; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @since 4.4 + */ +public class ActivityNormalizer extends BaseNormalizer { + + + public static final class LogFields extends Indexable { + + public final static IndexField KEY = addSortableAndSearchable(IndexField.Type.STRING, "key"); + public final static IndexField TYPE = addSortable(IndexField.Type.STRING, "type"); + public final static IndexField DATE = addSortable(IndexField.Type.DATE, "date"); + public final static IndexField EXECUTION = add(IndexField.Type.NUMERIC, "executionTime"); + public final static IndexField AUTHOR = addSearchable(IndexField.Type.STRING, "author"); + public final static IndexField DETAILS = addSearchable(IndexField.Type.OBJECT, "details"); + public final static IndexField MESSAGE = addSearchable(IndexField.Type.STRING, "message"); + + public static Set ALL_FIELDS = getAllFields(); + + private static Set getAllFields() { + Set fields = new HashSet(); + for (Field classField : LogFields.class.getDeclaredFields()) { + if (Modifier.isFinal(classField.getModifiers()) && Modifier.isStatic(classField.getModifiers())) { + try { + fields.add(IndexField.class.cast(classField.get(null))); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + } + return fields; + } + } + + public ActivityNormalizer(DbClient db) { + super(IndexDefinition.LOG, db); + } + + @Override + public List normalize(ActivityKey activityKey) { + DbSession dbSession = db.openSession(false); + List requests = new ArrayList(); + try { + requests.addAll(normalize(db.activityDao().getNullableByKey(dbSession, activityKey))); + } finally { + dbSession.close(); + } + return requests; + } + + @Override + public List normalize(ActivityDto dto) { + + Map logDoc = new HashMap(); + logDoc.put(LogFields.KEY.field(), dto.getKey()); + logDoc.put(LogFields.TYPE.field(), dto.getType()); + logDoc.put(LogFields.AUTHOR.field(), dto.getAuthor()); + logDoc.put(LogFields.MESSAGE.field(), dto.getMessage()); + logDoc.put(LogFields.EXECUTION.field(), dto.getExecutionTime()); + logDoc.put(LogFields.DATE.field(), dto.getCreatedAt()); + + logDoc.put(LogFields.DETAILS.field(), KeyValueFormat.parse(dto.getData())); + + /* Creating updateRequest */ + return ImmutableList.of(new UpdateRequest() + //Need to make a UUID because Key does not insure unicity + .replicationType(ReplicationType.ASYNC) + .doc(logDoc) + .upsert(logDoc)); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityQuery.java b/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityQuery.java new file mode 100644 index 00000000000..76e88f5c464 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/activity/index/ActivityQuery.java @@ -0,0 +1,62 @@ +/* + * 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.sonar.core.activity.Activity; + +import java.util.Collection; +import java.util.Date; + +/** + * @since 4.4 + */ +public class ActivityQuery { + + private Date since; + private Date to; + private Collection types; + + public ActivityQuery() { + } + + public Date getSince() { + return since; + } + + public void setSince(Date since) { + this.since = since; + } + + public Date getTo() { + return to; + } + + public void setTo(Date to) { + this.to = to; + } + + public Collection getTypes() { + return types; + } + + public void setTypes(Collection types) { + this.types = types; + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivitiesWebService.java b/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivitiesWebService.java new file mode 100644 index 00000000000..e6aa30ce395 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivitiesWebService.java @@ -0,0 +1,43 @@ +/* + * 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.ws; + +import org.sonar.api.server.ws.WebService; + +public class ActivitiesWebService implements WebService { + + public static final String API_ENDPOINT = "api/activities"; + + private final SearchAction search; + + public ActivitiesWebService(SearchAction search) { + this.search = search; + } + + @Override + public void define(Context context) { + NewController controller = context + .createController(API_ENDPOINT) + .setDescription("Logs search and views"); + + search.define(controller); + controller.done(); + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java b/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java new file mode 100644 index 00000000000..f4160282775 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java @@ -0,0 +1,62 @@ +/* + * 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.ws; + +import org.sonar.api.resources.Languages; +import org.sonar.api.utils.text.JsonWriter; +import org.sonar.core.activity.Activity; +import org.sonar.server.activity.index.ActivityNormalizer; +import org.sonar.server.search.ws.BaseMapping; +import org.sonar.server.text.MacroInterpreter; + +import java.util.Map; + +/** + * Conversion between Log and WS JSON response + */ +public class ActivityMapping extends BaseMapping { + + + public ActivityMapping(Languages languages, MacroInterpreter macroInterpreter) { + super(); + addIndexStringField("key", ActivityNormalizer.LogFields.KEY.field()); + addIndexStringField("type", ActivityNormalizer.LogFields.TYPE.field()); + addIndexDatetimeField("createdAt", ActivityNormalizer.LogFields.DATE.field()); + addIndexStringField("userLogin", ActivityNormalizer.LogFields.AUTHOR.field()); + addIndexStringField("message", ActivityNormalizer.LogFields.MESSAGE.field()); + addIndexStringField("executionTime", ActivityNormalizer.LogFields.EXECUTION.field()); + addField("details", new DetailField()); + } + + private static class DetailField extends IndexField { + DetailField() { + super(ActivityNormalizer.LogFields.DETAILS.field()); + } + + @Override + public void write(JsonWriter json, Activity activity) { + json.name("details").beginObject(); + for (Map.Entry detail : activity.details().entrySet()) { + json.prop(detail.getKey(), detail.getValue()); + } + json.endObject(); + } + } +} diff --git a/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java b/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java new file mode 100644 index 00000000000..61788652235 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java @@ -0,0 +1,98 @@ +/* + * 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.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.index.ActivityDoc; +import org.sonar.server.activity.index.ActivityQuery; +import org.sonar.server.search.QueryOptions; +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; + + public SearchAction(ActivityService logService, ActivityMapping mapping) { + this.logService = logService; + this.mapping = mapping; + } + + void define(WebService.NewController controller) { + WebService.NewAction action = controller + .createAction(SEARCH_ACTION) + .setDescription("Search for a logs") + .setSince("4.4") + .setHandler(this); + + // Other parameters + action.createParam(PARAM_TYPE) + .setDescription("Select types of log to search") + .setPossibleValues(Activity.Type.values()) + .setDefaultValue(StringUtils.join(Activity.Type.values(), ",")); + + // Generic search parameters + SearchOptions.defineFieldsParam(action, mapping.supportedFields()); + + SearchOptions.definePageParams(action); + } + + @Override + public void handle(Request request, Response response) { + ActivityQuery query = createLogQuery(logService.newActivityQuery(), request); + SearchOptions searchOptions = SearchOptions.create(request); + QueryOptions queryOptions = mapping.newQueryOptions(searchOptions); + + Result results = logService.search(query, queryOptions); + + JsonWriter json = response.newJsonWriter().beginObject(); + searchOptions.writeStatistics(json, results); + writeLogs(results, json, searchOptions); + json.endObject().close(); + } + + public static ActivityQuery createLogQuery(ActivityQuery query, Request request) { + // query.setTypes(request.param(SearchOptions.PARAM_TEXT_QUERY)); + return query; + } + + private void writeLogs(Result result, JsonWriter json, SearchOptions options) { + json.name("logs").beginArray(); + for (Activity log : result.getHits()) { + mapping.write((ActivityDoc) log, json, options); + } + json.endArray(); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/activity/ActivityBackendMediumTest.java b/sonar-server/src/test/java/org/sonar/server/activity/ActivityBackendMediumTest.java new file mode 100644 index 00000000000..aeb7e9ff1a1 --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/activity/ActivityBackendMediumTest.java @@ -0,0 +1,131 @@ +/* + * 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 org.elasticsearch.common.collect.Iterables; +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.search.QueryOptions; +import org.sonar.server.tester.ServerTester; + +import java.util.Iterator; +import java.util.Map; + +import static org.fest.assertions.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); + DbClient db; + DbSession dbSession; + + @Before + public void before() { + tester.clearDbAndIndexes(); + db = tester.get(DbClient.class); + 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.ACTIVE_RULE, 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.ACTIVE_RULE, new ActivityLog() { + + @Override + public Map getDetails() { + return ImmutableMap.of(testKey, testValue); + } + + @Override + public int getExecutionTime() { + return 12; + } + }); + 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); + assertThat(activity.executionTime()).isEqualTo(12); + } + + @Test + public void massive_insert() { + + // 0 Assert no logs in DB + assertThat(dao.findAll(dbSession)).hasSize(0); + int max = 200; + final String testValue = "hello world"; + for (int i = 0; i < max; i++) { + service.write(dbSession, Activity.Type.ACTIVE_RULE, 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; + Iterator logs = index.search(new ActivityQuery(), new QueryOptions().setScroll(true)).scroll(); + while (logs.hasNext()) { + logs.next(); + count++; + } + assertThat(count).isEqualTo(max); + + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceMediumTest.java b/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceMediumTest.java new file mode 100644 index 00000000000..2b52fddc4ce --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceMediumTest.java @@ -0,0 +1,112 @@ +/* + * 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.elasticsearch.common.collect.Iterables; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Test; +import org.sonar.core.activity.Activity; +import org.sonar.core.persistence.DbSession; +import org.sonar.server.activity.db.ActivityDao; +import org.sonar.server.activity.index.ActivityIndex; +import org.sonar.server.db.DbClient; +import org.sonar.server.search.QueryOptions; +import org.sonar.server.search.Result; +import org.sonar.server.tester.ServerTester; + +import java.util.Iterator; + +import static org.fest.assertions.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); + DbClient db; + DbSession dbSession; + + @Before + public void before() { + tester.clearDbAndIndexes(); + db = tester.get(DbClient.class); + dbSession = tester.get(DbClient.class).openSession(false); + } + + @After + public void after() { + dbSession.close(); + } + + @Test + public void find_all() throws InterruptedException { + final String testValue = "hello world"; + service.write(dbSession, Activity.Type.ACTIVE_RULE, 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_all() throws InterruptedException { + final String testValue = "hello world"; + service.write(dbSession, Activity.Type.ACTIVE_RULE, testValue); + dbSession.commit(); + assertThat(index.findAll().getTotal()).isEqualTo(1); + + Result result = index.search(service.newActivityQuery(), new QueryOptions()); + assertThat(result.getTotal()).isEqualTo(1L); + } + + @Test + @Ignore + // TODO fix missing logs in ES. + public void iterate_all() throws InterruptedException { + int max = QueryOptions.DEFAULT_LIMIT + 3; + final String testValue = "hello world"; + for (int i = 0; i < max; i++) { + service.write(dbSession, Activity.Type.ACTIVE_RULE, testValue + "_" + i); + } + dbSession.commit(); + + // 0. assert Base case + assertThat(dao.findAll(dbSession)).hasSize(max); + + Result result = index.search(service.newActivityQuery(), new QueryOptions().setScroll(true)); + assertThat(result.getTotal()).isEqualTo(max); + assertThat(result.getHits()).hasSize(0); + int count = 0; + Iterator logIterator = result.scroll(); + while (logIterator.hasNext()) { + count++; + logIterator.next(); + } + assertThat(count).isEqualTo(max); + } +} diff --git a/sonar-server/src/test/java/org/sonar/server/activity/db/ActivityDaoTest.java b/sonar-server/src/test/java/org/sonar/server/activity/db/ActivityDaoTest.java new file mode 100644 index 00000000000..9e4b67f98af --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/activity/db/ActivityDaoTest.java @@ -0,0 +1,126 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.activity.db; + + +import com.google.common.collect.ImmutableMap; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.sonar.api.utils.KeyValueFormat; +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 java.util.Map; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +public class ActivityDaoTest extends AbstractDaoTestCase { + + + private ActivityDao dao; + private DbSession session; + private System2 system2; + + @Before + public void before() throws Exception { + this.session = getMyBatis().openSession(false); + this.system2 = mock(System2.class); + this.dao = new ActivityDao(system2); + } + + @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_insert_missing_author() { + String testValue = "hello world"; + ActivityDto log = ActivityDto.createFor(testValue) + .setType(Activity.Type.ACTIVE_RULE); + 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.ACTIVE_RULE) + .setAuthor("jUnit"); + dao.insert(session, log); + + assertThat(dao.findAll(session)).hasSize(1); + ActivityDto newDto = dao.getByKey(session, log.getKey()); + assertThat(newDto.getAuthor()).isEqualTo(log.getAuthor()); + assertThat(newDto.getMessage()).isEqualTo(testValue); + } + + @Test + public void insert_loggable_log() { + final String testKey = "message"; + final String testValue = "hello world"; + ActivityDto log = ActivityDto.createFor(new ActivityLog() { + + @Override + public Map getDetails() { + return ImmutableMap.of(testKey, testValue); + } + + @Override + public int getExecutionTime() { + return 12; + } + }) + .setAuthor("jUnit") + .setType(Activity.Type.ACTIVE_RULE); + + dao.insert(session, log); + + assertThat(dao.findAll(session)).hasSize(1); + ActivityDto newDto = dao.getByKey(session, log.getKey()); + assertThat(newDto.getAuthor()).isEqualTo(log.getAuthor()); + assertThat(newDto.getExecutionTime()).isEqualTo(12); + assertThat(newDto.getData()).isNotNull(); + Map details = KeyValueFormat.parse(newDto.getData()); + assertThat(details.get(testKey)).isEqualTo(testValue); + } + +} diff --git a/sonar-server/src/test/java/org/sonar/server/activity/ws/ActivitiesWebServiceMediumTest.java b/sonar-server/src/test/java/org/sonar/server/activity/ws/ActivitiesWebServiceMediumTest.java new file mode 100644 index 00000000000..20c255c416c --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/activity/ws/ActivitiesWebServiceMediumTest.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.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.db.DbClient; +import org.sonar.server.activity.ActivityService; +import org.sonar.server.tester.ServerTester; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.ws.WsTester; + +import static org.fest.assertions.Assertions.assertThat; + +public class ActivitiesWebServiceMediumTest { + + @ClassRule + public static ServerTester tester = new ServerTester(); + + private ActivitiesWebService ws; + private ActivityService service; + private DbSession session; + + + @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 + public void define() throws Exception { + WebService.Context context = new WebService.Context(); + ws.define(context); + + WebService.Controller controller = context.controller(ActivitiesWebService.API_ENDPOINT); + + assertThat(controller).isNotNull(); + assertThat(controller.actions()).hasSize(1); + assertThat(controller.action(SearchAction.SEARCH_ACTION)).isNotNull(); + } + + @Test + public void search_logs() throws Exception { + service.write(session, Activity.Type.ACTIVE_RULE, "Hello World"); + session.commit(); + + MockUserSession.set(); + + // 1. List single Text log + WsTester.TestRequest request = tester.wsTester().newGetRequest(ActivitiesWebService.API_ENDPOINT, SearchAction.SEARCH_ACTION); + WsTester.Result result = request.execute(); + System.out.println("result = " + result.outputAsString()); + } + + +} -- 2.39.5