Skip to content
Snippets Groups Projects
Commit f7922397 authored by Kyle Doherty's avatar Kyle Doherty
Browse files

Merge branch 'activity_feed' of github.com:metabase/metabase-init into activity_feed

parents e42d43cb 90da9413
No related branches found
No related tags found
No related merge requests found
......@@ -152,7 +152,7 @@ export default class Activity extends Component {
}
render() {
let { activity } = this.props;
let { activity, user } = this.props;
let { error } = this.state;
return (
......@@ -172,7 +172,7 @@ export default class Activity extends Component {
<li key={item.id} className="mt3">
<ActivityItem
item={item}
description={this.activityDescription(item, item.user)}
description={this.activityDescription(item, user)}
userColors={this.initialsCssClasses(item.user)}
/>
<ActivityStory story={this.activityDescription(item, item.user)} />
......
......@@ -24,6 +24,17 @@ export default class Cards extends Component {
}
}
tableName(table_id) {
const { databaseMetadata } = this.props;
for (var tableIdx in databaseMetadata.tables) {
if (databaseMetadata.tables[tableIdx].id === table_id) {
return databaseMetadata.tables[tableIdx].display_name;
}
}
return "";
}
renderCards(cards) {
let items = cards.slice().sort((a, b) => b.created_at - a.created_at);
......@@ -59,7 +70,7 @@ export default class Cards extends Component {
}
render() {
let { cards } = this.props;
let { cards, cardsFilter, databaseMetadata } = this.props;
let { error } = this.state;
return (
......@@ -69,8 +80,19 @@ export default class Cards extends Component {
{ cards.length === 0 ?
<div className="flex flex-column layout-centered pt4" style={{fontSize: '1.08rem', marginTop: '100px'}}>
<span className="QuestionCircle">?</span>
<div className="text-normal mt3 mb1 h2 text-bold">Hmmm, looks like you don't have any saved questions yet.</div>
<div className="text-normal text-grey-2">Save a question and get this baby going!</div>
<div className="text-normal mt3 mb1 h2 text-bold">
{ cardsFilter.database && cardsFilter.table ?
"No questions have been saved against "+this.tableName(cardsFilter.table)+" yet."
: null}
{ cardsFilter.database && !cardsFilter.table ?
"No questions have been saved against "+databaseMetadata.name+" yet."
: null}
{ !cardsFilter.database && !cardsFilter.table ?
"You don't have any saved questions yet."
: null}
</div>
</div>
:
this.renderCards(cards)
......
......@@ -13,7 +13,7 @@
(defendpoint GET "/"
"Get recent activity."
[]
(-> (db/sel :many Activity (k/order :timestamp :DESC))
(-> (db/sel :many Activity (k/order :timestamp :DESC) (k/limit 40))
(hydrate :user :table :database :model_exists)))
(defendpoint GET "/recent_views"
......@@ -30,7 +30,7 @@
(k/where (= :user_id *current-user-id*))
(k/group :user_id :model :model_id)
(k/order :max_ts :desc)
(k/limit 15))
(k/limit 10))
(map #(assoc % :model_object (delay (case (:model %)
"card" (-> (Card (:model_id %))
(select-keys [:id :name :description :display]))
......
......@@ -49,7 +49,7 @@
[object]
(or (:actor_id object) (:user_id object) (:creator_id object)))
(defn- process-view-count-event
(defn process-view-count-event
"Handle processing for a single event notification received on the view-counts-channel"
[event]
;; try/catch here to prevent individual topic processing exceptions from bubbling up. better to handle them here.
......
......@@ -47,7 +47,10 @@
(pre-cascade-delete [_ {:keys [id]}]
(cascade-delete 'Session :user_id id)
(cascade-delete 'Activity :user_id id)))
(cascade-delete 'Dashboard :creator_id id)
(cascade-delete 'Card :creator_id id)
(cascade-delete 'Activity :user_id id)
(cascade-delete 'ViewLog :user_id id)))
(def ^:const current-user-fields
......
(ns metabase.api.activity-test
"Tests for /api/activity endpoints."
(:require [expectations :refer :all]
[metabase.db :as db]
[metabase.http-client :refer :all]
(metabase.models [activity :refer [Activity]]
[card :refer [Card]]
[dashboard :refer [Dashboard]]
[view-log :refer [ViewLog]])
[metabase.test.data :refer :all]
[metabase.test.data.users :refer :all]
[metabase.test.util :refer [match-$ expect-eval-actual-first random-name with-temp]]
[metabase.util :as u]))
;; GET /
; Things we are testing for:
; 1. ordered by timestamp DESC
; 2. :user and :model_exists are hydrated
; NOTE: timestamp matching was being a real PITA so I cheated a bit. ideally we'd fix that
(expect-let [_ (korma.core/delete Activity)
activity1 (db/ins Activity
:topic "install"
:details {}
:timestamp (u/parse-iso8601 "2015-09-09T12:13:14.888Z"))
activity2 (db/ins Activity
:topic "dashboard-create"
:user_id (user->id :crowberto)
:model "dashboard"
:model_id 1234
:details {:description "Because I can!"
:name "Bwahahaha"
:public_perms 2}
:timestamp (u/parse-iso8601 "2015-09-10T18:53:01.632Z"))
activity3 (db/ins Activity
:topic "user-joined"
:user_id (user->id :rasta)
:model "user"
:details {}
:timestamp (u/parse-iso8601 "2015-09-10T05:33:43.641Z"))]
[(match-$ (db/sel :one Activity :id (:id activity2))
{:id $
:topic "dashboard-create"
:user_id $
:user (match-$ (fetch-user :crowberto)
{:id (user->id :crowberto)
:email $
:date_joined $
:first_name $
:last_name $
:last_login $
:is_superuser $
:common_name $})
:model $
:model_id $
:model_exists false
:database_id nil
:database nil
:table_id nil
:table nil
:custom_id nil
:details $})
(match-$ (db/sel :one Activity :id (:id activity3))
{:id $
:topic "user-joined"
:user_id $
:user (match-$ (fetch-user :rasta)
{:id (user->id :rasta)
:email $
:date_joined $
:first_name $
:last_name $
:last_login $
:is_superuser $
:common_name $})
:model $
:model_id $
:model_exists nil
:database_id nil
:database nil
:table_id nil
:table nil
:custom_id nil
:details $})
(match-$ (db/sel :one Activity :id (:id activity1))
{:id $
:topic "install"
:user_id nil
:user nil
:model $
:model_id $
:model_exists nil
:database_id nil
:database nil
:table_id nil
:table nil
:custom_id nil
:details $})]
(->> ((user->client :crowberto) :get 200 "activity")
(map #(dissoc % :timestamp))))
;; GET /recent_views
; Things we are testing for:
; 1. ordering is sorted by most recent
; 2. results are filtered to current user
; 3. `:model_object` is hydrated in each result
; 4. we filter out entries where `:model_object` is nil (object doesn't exist)
(defn- create-card []
(db/ins Card
:name "rand-name"
:creator_id (user->id :crowberto)
:public_perms 2
:display "table"
:dataset_query {}
:visualization_settings {}))
(defn- create-dash []
(db/ins Dashboard
:name "rand-name"
:description "rand-name"
:creator_id (user->id :crowberto)
:public_perms 2))
(expect-let [card1 (create-card)
dash1 (create-dash)
card2 (create-card)]
[{:cnt 1
:user_id (user->id :crowberto)
:model "card"
:model_id (:id card1)
:model_object {:id (:id card1)
:name (:name card1)
:description (:description card1)
:display (name (:display card1))}}
{:cnt 1
:user_id (user->id :crowberto)
:model "dashboard"
:model_id (:id dash1)
:model_object {:id (:id dash1)
:name (:name dash1)
:description (:description dash1)}}
{:cnt 1
:user_id (user->id :crowberto)
:model "card"
:model_id (:id card2)
:model_object {:id (:id card2)
:name (:name card2)
:description (:description card2)
:display (name (:display card2))}}]
(let [create-view (fn [user model model-id]
(db/ins ViewLog
:user_id user
:model model
:model_id model-id
:timestamp (u/new-sql-timestamp)))]
(do
(create-view (user->id :crowberto) "card" (:id card2))
(create-view (user->id :crowberto) "dashboard" (:id dash1))
(create-view (user->id :crowberto) "card" (:id card1))
(create-view (user->id :crowberto) "card" 36478)
(create-view (user->id :rasta) "card" (:id card1))
(->> ((user->client :crowberto) :get 200 "activity/recent_views")
(map #(dissoc % :max_ts))))))
(ns metabase.events.activity-feed-test
(:require [expectations :refer :all]
[korma.core :as k]
[metabase.db :as db]
[metabase.events.activity-feed :refer :all]
(metabase.models [activity :refer [Activity]]
[card :refer [Card]]
[dashboard :refer [Dashboard]]
[dashboard-card :refer [DashboardCard]]
[database :refer [Database]]
[session :refer [Session]]
[user :refer [User]])
[metabase.test.data :refer :all]
[metabase.test.util :refer [expect-eval-actual-first with-temp random-name]]
[metabase.test-setup :refer :all]))
;; TODO - we can simplify the cleanup work we do by using the :in-context :expectations-options
;; the only downside is that it then runs the annotated function on ALL tests :/
(defn- create-test-objects
"Simple helper function which creates a series of test objects for use in the tests"
[]
(let [rand-name (random-name)
user (db/ins User
:email (str rand-name "@metabase.com")
:first_name rand-name
:last_name rand-name
:password rand-name)
;; i don't know why, but the below `ins` doesn't return an object :(
session (db/ins Session
:id rand-name
:user_id (:id user))
dashboard (db/ins Dashboard
:name rand-name
:description rand-name
:creator_id (:id user)
:public_perms 2)
card (db/ins Card
:name rand-name
:creator_id (:id user)
:public_perms 2
:display "table"
:dataset_query {:database (db-id)
:type :query
:query {:source_table (id :categories)}}
:visualization_settings {})
dashcard (db/ins DashboardCard
:card_id (:id card)
:dashboard_id (:id dashboard))]
{:card card
:dashboard dashboard
:dashcard dashcard
:session {:id rand-name}
:user user}))
;; `:card-create` event
(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
{:topic :card-create
:user_id (:id user)
:model "card"
:model_id (:id card)
:database_id (db-id)
:table_id (id :categories)
:details {:description (:description card)
:name (:name card)
:public_perms (:public_perms card)}}
(do
(k/delete Activity)
(process-activity-event {:topic :card-create
:item card})
(-> (db/sel :one Activity :topic "card-create")
(select-keys [:topic :user_id :model :model_id :database_id :table_id :details]))))
;; `:card-update` event
(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
{:topic :card-update
:user_id (:id user)
:model "card"
:model_id (:id card)
:database_id (db-id)
:table_id (id :categories)
:details {:description (:description card)
:name (:name card)
:public_perms (:public_perms card)}}
(do
(k/delete Activity)
(process-activity-event {:topic :card-update
:item card})
(-> (db/sel :one Activity :topic "card-update")
(select-keys [:topic :user_id :model :model_id :database_id :table_id :details]))))
;; `:card-delete` event
(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
{:topic :card-delete
:user_id (:id user)
:model "card"
:model_id (:id card)
:database_id (db-id)
:table_id (id :categories)
:details {:description (:description card)
:name (:name card)
:public_perms (:public_perms card)}}
(do
(k/delete Activity)
(process-activity-event {:topic :card-delete
:item card})
(-> (db/sel :one Activity :topic "card-delete")
(select-keys [:topic :user_id :model :model_id :database_id :table_id :details]))))
;; `:dashboard-create` event
(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
{:topic :dashboard-create
:user_id (:id user)
:model "dashboard"
:model_id (:id dashboard)
:details {:description (:description dashboard)
:name (:name dashboard)
:public_perms (:public_perms dashboard)}}
(do
(k/delete Activity)
(process-activity-event {:topic :dashboard-create
:item dashboard})
(-> (db/sel :one Activity :topic "dashboard-create")
(select-keys [:topic :user_id :model :model_id :details]))))
;; `:dashboard-delete` event
(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
{:topic :dashboard-delete
:user_id (:id user)
:model "dashboard"
:model_id (:id dashboard)
:details {:description (:description dashboard)
:name (:name dashboard)
:public_perms (:public_perms dashboard)}}
(do
(k/delete Activity)
(process-activity-event {:topic :dashboard-delete
:item dashboard})
(-> (db/sel :one Activity :topic "dashboard-delete")
(select-keys [:topic :user_id :model :model_id :details]))))
;; `:dashboard-add-cards` event
(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
{:topic :dashboard-add-cards
:user_id (:id user)
:model "dashboard"
:model_id (:id dashboard)
:details {:description (:description dashboard)
:name (:name dashboard)
:public_perms (:public_perms dashboard)
:dashcards [{:description (:description card)
:name (:name card)
:public_perms (:public_perms card)
:id (:id dashcard)
:card_id (:id card)}]}}
(do
(k/delete Activity)
(process-activity-event {:topic :dashboard-add-cards
:item {:id (:id dashboard) :actor_id (:id user) :dashcards [dashcard]}})
(-> (db/sel :one Activity :topic "dashboard-add-cards")
(select-keys [:topic :user_id :model :model_id :details]))))
;; `:dashboard-remove-cards` event
(expect-let [{:keys [card dashboard dashcard user]} (create-test-objects)]
{:topic :dashboard-remove-cards
:user_id (:id user)
:model "dashboard"
:model_id (:id dashboard)
:details {:description (:description dashboard)
:name (:name dashboard)
:public_perms (:public_perms dashboard)
:dashcards [{:description (:description card)
:name (:name card)
:public_perms (:public_perms card)
:id (:id dashcard)
:card_id (:id card)}]}}
(do
(k/delete Activity)
(process-activity-event {:topic :dashboard-remove-cards
:item {:id (:id dashboard) :actor_id (:id user) :dashcards [dashcard]}})
(-> (db/sel :one Activity :topic "dashboard-remove-cards")
(select-keys [:topic :user_id :model :model_id :details]))))
;; `:database-sync-*` events
(expect
[1
{:topic :database-sync
:user_id nil
:model "database"
:model_id (db-id)
:database_id (db-id)
:custom_id "abc"
:details {:status "started"}}
{:topic :database-sync
:user_id nil
:model "database"
:model_id (db-id)
:database_id (db-id)
:custom_id "abc"
:details {:status "completed" :running_time 0}}]
(do
(k/delete Activity)
(let [_ (process-activity-event {:topic :database-sync-begin
:item {:database_id (db-id) :custom_id "abc"}})
activity1 (-> (db/sel :one Activity :topic "database-sync")
(select-keys [:topic :user_id :model :model_id :database_id :custom_id :details]))
_ (process-activity-event {:topic :database-sync-end
:item {:database_id (db-id) :custom_id "abc"}})
activity2 (-> (db/sel :one Activity :topic "database-sync")
(select-keys [:topic :user_id :model :model_id :database_id :custom_id :details])
(assoc-in [:details :running_time] 0))
activity-cnt (:cnt (first (k/select Activity (k/aggregate (count :*) :cnt) (k/where {:topic "database-sync"}))))]
[activity-cnt
activity1
activity2])))
;; `:install` event
(expect
{:topic :install
:user_id nil
:model "install"
:model_id nil
:details {}}
(do
(k/delete Activity)
(process-activity-event {:topic :install
:item {}})
(-> (db/sel :one Activity :topic "install")
(select-keys [:topic :user_id :model :model_id :details]))))
;; `:user-login` event
(expect-let [{{user-id :id} :user {session-id :id :as session} :session} (create-test-objects)]
{:topic :user-joined
:user_id user-id
:model "user"
:model_id user-id
:details {}}
(do
(k/delete Activity)
(process-activity-event {:topic :user-login
:item {:user_id user-id
:session_id session-id}})
(-> (db/sel :one Activity :topic "user-joined")
(select-keys [:topic :user_id :model :model_id :details]))))
(ns metabase.events.view-log-test
(:require [expectations :refer :all]
[korma.core :as k]
[metabase.db :as db]
[metabase.events.view-log :refer :all]
(metabase.models [user :refer [User]]
[view-log :refer [ViewLog]])
[metabase.test.data :refer :all]
[metabase.test.util :refer [expect-eval-actual-first with-temp random-name]]
[metabase.test-setup :refer :all]))
(defn- create-test-user []
(let [rand-name (random-name)]
(db/ins User
:email (str rand-name "@metabase.com")
:first_name rand-name
:last_name rand-name
:password rand-name)))
;; `:card-create` event
(expect-let [{user-id :id} (create-test-user)
card {:id 1234
:creator_id user-id}]
{:user_id user-id
:model "card"
:model_id (:id card)}
(do
(process-view-count-event {:topic :card-create
:item card})
(-> (db/sel :one ViewLog :user_id user-id)
(select-keys [:user_id :model :model_id]))))
;; `:card-read` event
(expect-let [{user-id :id} (create-test-user)
card {:id 1234
:actor_id user-id}]
{:user_id user-id
:model "card"
:model_id (:id card)}
(do
(process-view-count-event {:topic :card-read
:item card})
(-> (db/sel :one ViewLog :user_id user-id)
(select-keys [:user_id :model :model_id]))))
;; `:dashboard-read` event
(expect-let [{user-id :id} (create-test-user)
dashboard {:id 1234
:actor_id user-id}]
{:user_id user-id
:model "dashboard"
:model_id (:id dashboard)}
(do
(process-view-count-event {:topic :dashboard-read
:item dashboard})
(-> (db/sel :one ViewLog :user_id user-id)
(select-keys [:user_id :model :model_id]))))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment