/api/timeline-event endpoints. | (ns metabase.timeline.api.timeline-event (:require [metabase.analytics.core :as analytics] [metabase.api.common :as api] [metabase.api.macros :as api.macros] [metabase.models.collection :as collection] [metabase.timeline.models.timeline-event :as timeline-event] [metabase.util :as u] [metabase.util.date-2 :as u.date] [metabase.util.i18n :refer [tru]] [metabase.util.malli.schema :as ms] [toucan2.core :as t2])) |
(api.macros/defendpoint :post "/" "Create a new [[TimelineEvent]]." [_route-params _query-params {:keys [timestamp time_matters icon timeline_id source question_id] :as body} :- [:map [:name ms/NonBlankString] [:description {:optional true} [:maybe :string]] [:timestamp ms/TemporalString] [:time_matters {:optional true} [:maybe :boolean]] [:timezone :string] [:icon {:optional true} [:maybe timeline-event/Icon]] [:timeline_id ms/PositiveInt] [:source {:optional true} [:maybe timeline-event/Source]] [:question_id {:optional true} [:maybe ms/PositiveInt]] [:archived {:optional true} [:maybe :boolean]]]] ;; deliberately not using api/check-404 so we can have a useful error message. (let [timeline (t2/select-one :model/Timeline :id timeline_id)] (when-not timeline (throw (ex-info (tru "Timeline with id {0} not found" timeline_id) {:status-code 404}))) (collection/check-write-perms-for-collection (:collection_id timeline)) ;; todo: revision system (let [parsed (if (nil? timestamp) (throw (ex-info (tru "Timestamp cannot be null") {:status-code 400})) (u.date/parse timestamp)) tl-event (merge (dissoc body :source :question_id) {:creator_id api/*current-user-id* :timestamp parsed} (when-not icon {:icon (t2/select-one-fn :icon :model/Timeline :id timeline_id)}))] (analytics/track-event! :snowplow/timeline (cond-> {:event :new-event-created :time_matters time_matters :collection_id (:collection_id timeline)} (boolean source) (assoc :source source) (boolean question_id) (assoc :question_id question_id))) (first (t2/insert-returning-instances! :model/TimelineEvent tl-event))))) | |
(api.macros/defendpoint :get "/:id" "Fetch the [[TimelineEvent]] with `id`." [{:keys [id]} :- [:map [:id ms/PositiveInt]]] (api/read-check :model/TimelineEvent id)) | |
(api.macros/defendpoint :put "/:id" "Update a [[TimelineEvent]]." [{:keys [id]} :- [:map [:id ms/PositiveInt]] _query-params {:keys [timestamp] :as timeline-event-updates} :- [:map [:name {:optional true} [:maybe ms/NonBlankString]] [:description {:optional true} [:maybe :string]] [:timestamp {:optional true} [:maybe ms/TemporalString]] [:time_matters {:optional true} [:maybe :boolean]] [:timezone {:optional true} [:maybe :string]] [:icon {:optional true} [:maybe timeline-event/Icon]] [:timeline_id {:optional true} [:maybe ms/PositiveInt]] [:archived {:optional true} [:maybe :boolean]]]] (let [existing (api/write-check :model/TimelineEvent id) timeline-event-updates (cond-> timeline-event-updates (boolean timestamp) (update :timestamp u.date/parse))] (collection/check-allowed-to-change-collection existing timeline-event-updates) ;; todo: if we accept a new timestamp, must we require a timezone? gut says yes? (t2/update! :model/TimelineEvent id (u/select-keys-when timeline-event-updates :present #{:description :timestamp :time_matters :timezone :icon :timeline_id :archived} :non-nil #{:name})) (t2/select-one :model/TimelineEvent :id id))) | |
(api.macros/defendpoint :delete "/:id" "Delete a [[TimelineEvent]]." [{:keys [id]} :- [:map [:id ms/PositiveInt]]] (api/write-check :model/TimelineEvent id) (t2/delete! :model/TimelineEvent :id id) api/generic-204-no-content) | |