]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5329 - Rename Log package to Activity
authorStephane Gamard <stephane.gamard@searchbox.com>
Mon, 16 Jun 2014 11:21:25 +0000 (13:21 +0200)
committerStephane Gamard <stephane.gamard@searchbox.com>
Mon, 16 Jun 2014 11:22:02 +0000 (13:22 +0200)
14 files changed:
sonar-core/src/main/resources/org/sonar/core/activity/db/ActivityMapper.xml [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/activity/ActivityService.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/activity/db/ActivityDao.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/activity/index/ActivityDoc.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/activity/index/ActivityIndex.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/activity/index/ActivityNormalizer.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/activity/index/ActivityQuery.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/activity/ws/ActivitiesWebService.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/activity/ws/ActivityMapping.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/activity/ws/SearchAction.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/activity/ActivityBackendMediumTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/activity/ActivityServiceMediumTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/activity/db/ActivityDaoTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/activity/ws/ActivitiesWebServiceMediumTest.java [new file with mode: 0644]

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 (file)
index 0000000..14e061d
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.core.activity.db.LogMapper">
+
+  <insert id="insert" parameterType="Log" useGeneratedKeys="false" lang="raw">
+    insert into logs
+    (created_at, log_type,execution_time_field,user_login,data_field, log_message)
+    values (#{createdAt}, #{type}, #{executionTime}, #{author}, #{data}, #{message})
+  </insert>
+
+  <select id="selectByKey" parameterType="map" resultType="Log" lang="raw">
+    SELECT
+    l.created_at as "createdAt",
+    l.log_type as "type",
+    l.execution_time_field as "executionTime",
+    l.user_login as "author",
+    l.data_field as "data",
+    l.log_message as "message"
+    FROM logs l
+    WHERE l.created_at=#{key.createdAt}
+    AND l.user_login=#{key.author}
+    AND l.log_type=#{key.type}
+  </select>
+
+  <select id="selectAll" parameterType="map" resultType="Log" lang="raw">
+    SELECT
+    l.created_at as "createdAt",
+    l.log_type as "type",
+    l.execution_time_field as "executionTime",
+    l.user_login as "author",
+    l.data_field as "data",
+    l.log_message as "message"
+    FROM logs l
+  </select>
+</mapper>
+
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 (file)
index 0000000..7f7149d
--- /dev/null
@@ -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 <L extends ActivityLog> void write(DbSession session, Activity.Type type, List<L> logs) {
+    for (ActivityLog log : logs) {
+      this.write(session, type, log);
+    }
+  }
+
+  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();
+  }
+
+  public Result<Activity> 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 (file)
index 0000000..d009113
--- /dev/null
@@ -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<ActivityMapper, ActivityDto, ActivityKey> {
+
+  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<ActivityDto> 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 (file)
index 0000000..e224864
--- /dev/null
@@ -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<String, Object> 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<String, String> 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 (file)
index 0000000..6d5f36b
--- /dev/null
@@ -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<Activity, ActivityDto, ActivityKey> {
+
+  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<String, Object> mapping = new HashMap<String, Object>();
+    // return mapping;
+  }
+
+  @Override
+  protected Settings getIndexSettings() throws IOException {
+    return ImmutableSettings.builder().build();
+  }
+
+  @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));
+    }
+    return mapping;
+  }
+
+  @Override
+  protected Activity toDoc(final Map<String, Object> fields) {
+    return new ActivityDoc(fields);
+  }
+
+  public Result<Activity> findAll() {
+    return new Result<Activity>(this, getClient().prepareSearch(this.getIndexName())
+      .setQuery(QueryBuilders.matchAllQuery())
+      .setTypes(this.getIndexType())
+      .setSize(Integer.MAX_VALUE)
+      .get());
+  }
+
+  public Result<Activity> 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<Activity>(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 (file)
index 0000000..ebfb710
--- /dev/null
@@ -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<ActivityDto, ActivityKey> {
+
+
+  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<IndexField> ALL_FIELDS = getAllFields();
+
+    private static Set<IndexField> getAllFields() {
+      Set<IndexField> fields = new HashSet<IndexField>();
+      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<UpdateRequest> normalize(ActivityKey activityKey) {
+    DbSession dbSession = db.openSession(false);
+    List<UpdateRequest> requests = new ArrayList<UpdateRequest>();
+    try {
+      requests.addAll(normalize(db.activityDao().getNullableByKey(dbSession, activityKey)));
+    } finally {
+      dbSession.close();
+    }
+    return requests;
+  }
+
+  @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.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 (file)
index 0000000..76e88f5
--- /dev/null
@@ -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<Activity.Type> 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<Activity.Type> getTypes() {
+    return types;
+  }
+
+  public void setTypes(Collection<Activity.Type> 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 (file)
index 0000000..e6aa30c
--- /dev/null
@@ -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 (file)
index 0000000..f416028
--- /dev/null
@@ -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<Activity> {
+    DetailField() {
+      super(ActivityNormalizer.LogFields.DETAILS.field());
+    }
+
+    @Override
+    public void write(JsonWriter json, Activity activity) {
+      json.name("details").beginObject();
+      for (Map.Entry<String, String> 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 (file)
index 0000000..6178865
--- /dev/null
@@ -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<Activity> 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<Activity> 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 (file)
index 0000000..aeb7e9f
--- /dev/null
@@ -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<String, String> 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<Activity> 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 (file)
index 0000000..2b52fdd
--- /dev/null
@@ -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<Activity> 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<Activity> result = index.search(service.newActivityQuery(), new QueryOptions().setScroll(true));
+    assertThat(result.getTotal()).isEqualTo(max);
+    assertThat(result.getHits()).hasSize(0);
+    int count = 0;
+    Iterator<Activity> 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 (file)
index 0000000..9e4b67f
--- /dev/null
@@ -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<String, String> 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<String, String> 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 (file)
index 0000000..20c255c
--- /dev/null
@@ -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());
+  }
+
+
+}