duck1123/jiksnu

View on GitHub
src/jiksnu/transforms/activity_transforms.clj

Summary

Maintainability
Test Coverage
(ns jiksnu.transforms.activity-transforms
  (:require [ciste.config :refer [config]]
            [clj-time.core :as time]
            [clojure.string :as string]
            [jiksnu.actions.user-actions :as actions.user]
            [jiksnu.model.activity :as model.activity]
            [jiksnu.model.feed-source :as model.feed-source]
            [jiksnu.ops :as ops]
            [jiksnu.session :as session]
            [jiksnu.util :as util]
            [slingshot.slingshot :refer [throw+]])
  (:import java.net.URI
           (org.bson.types ObjectId)))

(defn set-local
  [activity]
  (assoc activity :local true))

(defn set-published-time
  [item]
  (if (:published item)
    item
    (assoc item :published (time/now))))

(defn set-object-updated
  [activity]
  (if (:updated (:object activity))
    activity
    (assoc-in activity [:object :updated] (time/now))))

(defn set-object-created
  [activity]
  (if (:created (:object activity))
    activity
    (assoc-in activity [:object :created] (time/now))))

(defn set-object-type
  [activity]
  (if (seq (get-in activity [:object :type]))
    activity
    (let [type (if-let [object-type (:type (:object activity))]
                 (-> object-type
                     ;; strip namespaces
                     (string/replace #"http://onesocialweb.org/spec/1.0/object/" "")
                     (string/replace #"http://activitystrea.ms/schema/1.0/" ""))
                 "note")]
      (assoc-in
       activity [:object :type] type))))

(defn set-parent
  [params]
  (if (empty? (:parent params))
    (let [params (dissoc params :parent)]
      (if-let [uri (:parent-uri params)]
        (if-let [parent (model.activity/fetch-by-remote-id uri)]
          (assoc params :parent (:_id parent))
          (do (ops/update-resource uri)
              params))
        params))
    params))

(defn set-source
  [activity]
  (if (:update-source activity)
    activity
    (or (when-let [author (model.activity/get-author activity)]
          (when-let [source (model.feed-source/find-by-user author)]
            (assoc activity :update-source (:_id source))))
        activity)))

(defn set-tags
  [activity]
  (let [tags (:tags activity)]
    (if (string? tags)
      (if (and tags (not= tags ""))
        (if-let [tag-seq (filter #(not= % "") (string/split tags #",\s*"))]
          (assoc activity :tags tag-seq)
          (dissoc activity :tags))
        (dissoc activity :tags))
      (if (coll? tags)
        activity
        (dissoc activity :tags)))))

(defn set-public
  [activity]
  (if (false? (:public activity))
    activity
    (assoc activity :public true)))

(defn set-geo
  [activity]
  (let [latitude (get-in activity [:geo :latitude])
        longitude (get-in activity [:geo :longitude])]
    (-> activity
        (dissoc :geo)
        (assoc :geo {:latitude latitude
                     :longitude longitude}))))

(defn set-remote
  [activity]
  (if (:local activity)
    activity
    (assoc activity :local false)))

(defn set-object-id
  [activity]
  (if (:id (:object activity))
    activity
    (assoc-in activity [:object :id] (util/new-id))))

(defn set-title
  [item]
  (if (:title item)
    item
    (assoc item :title "")))

(defn set-actor
  [activity]
  ;; TODO: Should we be allowing an author to be passed in?
  (if-let [author (or (:author activity) (session/current-user-id))]
    (assoc activity :author author)))

;; TODO: This operation should be performed on local posts. Remote
;; posts without an id should be rejected
(defn set-id
  [activity]
  (if (empty? (:id activity))
    (if-let [user (model.activity/get-author activity)]
      (if (:local activity)
        (let [id (str
                  ;; FIXME: refer to the security flag
                  "https://"
                  (config :domain) "/main/activities/" (:_id activity))]
          (assoc activity :id id))
        (throw+ "is not local and does not have an id"))
      (throw+ "Could not determine author"))
    activity))

(defn set-verb
  [activity]
  (if (:verb activity)
    activity
    (assoc activity :verb "post")))

(defn set-recipients*
  [uri]
  (let [user (actions.user/find-or-create {:_id uri})]
    (:_id user)))

(defn- set-mentioned*
  [url]
  (let [uri-obj (URI. url)
        scheme (.getScheme uri-obj)]
    (if (#{"http" "https"} scheme)
      (let [actor (or (try
                        (actions.user/find-or-create {:_id url})
                        (catch RuntimeException ex
                          ;; FIXME: Handle error
                          nil))
                      #_(try
                          (actions.group/find-or-create {:url url})
                          (catch RuntimeException ex
                            ;; FIXME: Handle error
                            nil)))]
        (:_id actor))
      (:_id (actions.user/find-or-create {:_id url})))))

;; TODO: this type of job should be done via triggers
(defn set-recipients
  "attempt to resolve the recipients"
  [activity]
  (let [uris (filter identity (:recipient-uris activity))]
    (if (empty? uris)
      (dissoc activity :recipient-uris)
      (let [users (keep set-recipients* uris)]
        (assoc activity :recipients users)))))

(defn set-conversation
  [item]
  (if (:conversation item)
    item
    (if-let [user (model.activity/get-author item)]
      (if (:local user)
        (assoc item :conversation (:_id @(ops/create-new-conversation)))
        (if-let [uri (first (:conversation-uris item))]
          (let [conversation (ops/get-conversation uri)]
            (-> item
                (assoc :conversation (:_id @conversation))
                (dissoc :conversation-uris)))
          item))
      (throw+ "could not determine author"))))

(defn set-mentioned
  [activity]
  (if-let [ids (->> activity
                    :mentioned-uris
                    (map set-mentioned*)
                    (filter identity)
                    seq)]
    (-> activity
        (assoc :mentioned ids)
        (dissoc :mentioned-uris))
    activity))

(defn set-resources
  [activity]
  (if-let [ids (->> activity
                    :enclosures
                    (map (comp :_id ops/get-resource :href)))]
    (-> activity
        (assoc :resources ids)
        (dissoc :enclosures))
    activity))

(defn set-content
  [activity]
  (if (:content activity)
    activity
    (let [target-id (:id (:object activity))
          content (condp = (:verb activity)
                    "follow" (str "Follow user: " target-id)
                    "unfollow" (str "Unfollow User: " target-id)
                    "like" "Liked an activity"
                    "join" (str "Joined a group: " target-id)
                    "unknown verb")]
      (assoc activity :content content))))

(defn set-streams
  [activity]
  (let [streams (->> activity
                     :streams
                     (mapv (fn [stream]
                             (if (seq stream)
                               (if (string? stream)
                                 (ObjectId. stream)
                                 stream)
                               (throw+ {:message "Invalid stream"
                                        :stream stream})))))]
    (assoc activity :streams streams)))