From e3891df243a85dc894fb3e7958051a92718e2f79 Mon Sep 17 00:00:00 2001
From: Andrey Antukh <niwi@niwi.nz>
Date: Fri, 29 Jan 2021 17:18:53 +0100
Subject: [PATCH] :sparkles: Minor improvements on profile initial data.

---
 backend/src/app/config.clj                  |  8 +--
 backend/src/app/db/profile_initial_data.clj | 80 +++++++++------------
 backend/src/app/rpc/mutations/demo.clj      | 10 ++-
 backend/src/app/rpc/mutations/profile.clj   | 23 +++---
 backend/src/app/srepl/main.clj              | 18 ++---
 5 files changed, 67 insertions(+), 72 deletions(-)

diff --git a/backend/src/app/config.clj b/backend/src/app/config.clj
index 87357a8bc..1f0f7912b 100644
--- a/backend/src/app/config.clj
+++ b/backend/src/app/config.clj
@@ -70,8 +70,8 @@
    :ldap-auth-fullname-attribute "displayName"
    :ldap-auth-avatar-attribute "jpegPhoto"
 
-   ;; :initial-data-project-name "Penpot Onboarding"
-   ;; :initial-data-file "/internal/initial-data.json"
+   ;; :initial-data-file "resources/initial-data.json"
+   ;; :initial-data-project-name "Penpot Oboarding"
    })
 
 (s/def ::http-server-port ::us/integer)
@@ -140,7 +140,7 @@
 (s/def ::telemetry-server-enabled ::us/boolean)
 (s/def ::telemetry-server-port ::us/integer)
 
-(s/def ::initial-data-project-id ::us/uuid)
+(s/def ::initial-data-file ::us/string)
 (s/def ::initial-data-project-name ::us/string)
 
 (s/def ::config
@@ -197,7 +197,7 @@
                    ::telemetry-server-enabled
                    ::telemetry-server-port
                    ::telemetry-uri
-                   ::initial-data-project-id
+                   ::initial-data-file
                    ::initial-data-project-name]))
 
 (defn- env->config
diff --git a/backend/src/app/db/profile_initial_data.clj b/backend/src/app/db/profile_initial_data.clj
index 9366b9dd5..c10778077 100644
--- a/backend/src/app/db/profile_initial_data.clj
+++ b/backend/src/app/db/profile_initial_data.clj
@@ -13,12 +13,10 @@
    [app.config :as cfg]
    [app.db :as db]
    [app.rpc.mutations.projects :as projects]
-   [app.storage :as storage]
    [app.util.transit :as tr]
-   [clojure.tools.logging :as log]
-   [cuerdas.core :as str])
-  (:import java.io.FileOutputStream
-           java.io.FileInputStream))
+   [clojure.java.io :as io]
+   [cuerdas.core :as str]
+   [datoteka.core :as fs]))
 
 (def sql:file
   "select * from file where project_id = ?")
@@ -35,18 +33,10 @@
      from file_media_object
      where file_id in (select id from file_ids)")
 
-(def sql:file-media-thumbnail
-  "with file_ids as (select id from file where project_id = ?),
-        media_ids as (select id from file_media_object where file_id in (select id from file_ids))
-   select *
-     from file_media_thumbnail
-     where media_object_id in (select id from media_ids)")
-
 (defn change-ids
   "Given a collection and a map from ID to ID. Changes all the `keys` properties
   so they point to the new ID existing in `map-ids`"
   [map-ids coll keys]
-
   (let [generate-id
         (fn [map-ids {:keys [id]}]
           (assoc map-ids id (uuid/next)))
@@ -66,61 +56,57 @@
     [new-map-ids (map (partial change-id new-map-ids) coll)]))
 
 (defn create-initial-data-dump
-  [conn project-id output-file]
+  [conn project-id output-path]
   (let [ ;; Retrieve data from templates
+        opath                (fs/path output-path)
         file                 (db/exec! conn [sql:file, project-id])
         file-library-rel     (db/exec! conn [sql:file-library-rel, project-id])
         file-media-object    (db/exec! conn [sql:file-media-object, project-id])
-        file-media-thumbnail (db/exec! conn [sql:file-media-thumbnail, project-id])
 
         data {:file file
               :file-library-rel file-library-rel
-              :file-media-object file-media-object
-              :file-media-thumbnail file-media-thumbnail}]
+              :file-media-object file-media-object}]
+    (with-open [output (io/output-stream opath)]
+      (tr/encode-stream data output)
+      nil)))
 
-    (with-open [output (FileOutputStream. output-file)]
-      (tr/encode-stream data output))))
+(defn read-initial-data
+  [path]
+  (when (fs/exists? path)
+    (with-open [input (io/input-stream (fs/path path))]
+      (tr/decode-stream input))))
 
 (defn create-profile-initial-data
-  [conn storage profile]
-
-  (let [initial-data-file (get cfg/config :initial-data-file)
-        initial-data (when initial-data-file
-                       (with-open [input (FileInputStream. initial-data-file)]
-                         (tr/decode-stream input)))]
-    (when initial-data
-      (let [{:keys [file file-library-rel file-media-object file-media-thumbnail]} initial-data
-
-            sample-project-name (get cfg/config :initial-data-project-name "Penpot Onboarding")
-
-
+  [conn profile]
+  (when-let [initial-data-path (:initial-data-file cfg/config)]
+    (when-let [{:keys [file file-library-rel file-media-object file-media-thumbnail]} (read-initial-data initial-data-path)]
+      (let [sample-project-name (:initial-data-project-name cfg/config "Penpot Onboarding")
             proj (projects/create-project conn {:profile-id (:id profile)
                                                 :team-id (:default-team-id profile)
                                                 :name sample-project-name})
 
-            _ (projects/create-project-profile conn {:project-id (:id proj)
-                                                     :profile-id (:id profile)})
-
-            _ (projects/create-team-project-profile conn {:team-id (:default-team-id profile)
-                                                          :project-id (:id proj)
-                                                          :profile-id (:id profile)})
-
             map-ids {}
 
             ;; Create new ID's and change the references
-            [map-ids file]           (change-ids map-ids file #{:id})
-
+            [map-ids file]                 (change-ids map-ids file #{:id})
             [map-ids file-library-rel]     (change-ids map-ids file-library-rel #{:file-id :library-file-id})
             [map-ids file-media-object]    (change-ids map-ids file-media-object #{:id :file-id :media-id :thumbnail-id})
             [map-ids file-media-thumbnail] (change-ids map-ids file-media-thumbnail #{:id :media-object-id})
 
-            file (->> file (map (fn [data] (assoc data :project-id (:id proj)))))
-            file-profile-rel (->> file (map (fn [data]
-                                              (hash-map :file-id (:id data)
-                                                        :profile-id (:id profile)
-                                                        :is-owner true
-                                                        :is-admin true
-                                                        :can-edit true))))]
+            file             (map #(assoc % :project-id (:id proj)) file)
+            file-profile-rel (map #(array-map :file-id (:id %)
+                                              :profile-id (:id profile)
+                                              :is-owner true
+                                              :is-admin true
+                                              :can-edit true)
+                                  file)]
+
+        (projects/create-project-profile conn {:project-id (:id proj)
+                                               :profile-id (:id profile)})
+
+        (projects/create-team-project-profile conn {:team-id (:default-team-id profile)
+                                                    :project-id (:id proj)
+                                                    :profile-id (:id profile)})
 
         ;; Re-insert into the database
         (db/insert-multi! conn :file file)
diff --git a/backend/src/app/rpc/mutations/demo.clj b/backend/src/app/rpc/mutations/demo.clj
index 5ebe0b5cf..b7a5759f5 100644
--- a/backend/src/app/rpc/mutations/demo.clj
+++ b/backend/src/app/rpc/mutations/demo.clj
@@ -14,6 +14,7 @@
    [app.common.exceptions :as ex]
    [app.config :as cfg]
    [app.db :as db]
+   [app.db.profile-initial-data :refer [create-profile-initial-data]]
    [app.rpc.mutations.profile :as profile]
    [app.tasks :as tasks]
    [app.util.services :as sv]
@@ -27,7 +28,7 @@
   [{:keys [pool] :as cfg} _]
   (let [id       (uuid/next)
         sem      (System/currentTimeMillis)
-        email    (str "demo-" sem ".demo@nodomain.com")
+        email    (str "demo-" sem ".demo@example.com")
         fullname (str "Demo User " sem)
         password (-> (bn/random-bytes 16)
                      (bc/bytes->b64u)
@@ -36,7 +37,8 @@
                   :email email
                   :fullname fullname
                   :demo? true
-                  :password password}]
+                  :password password
+                  :props {:onboarding-viewed true}}]
 
     (when-not (:allow-demo-users cfg/config)
       (ex/raise :type :validation
@@ -45,11 +47,13 @@
 
     (db/with-atomic [conn pool]
       (->> (#'profile/create-profile conn params)
-           (#'profile/create-profile-relations conn))
+           (#'profile/create-profile-relations conn)
+           (create-profile-initial-data conn))
 
       ;; Schedule deletion of the demo profile
       (tasks/submit! conn {:name "delete-profile"
                            :delay cfg/default-deletion-delay
                            :props {:profile-id id}})
+
       {:email email
        :password password})))
diff --git a/backend/src/app/rpc/mutations/profile.clj b/backend/src/app/rpc/mutations/profile.clj
index 3576a89c9..2ffad5f30 100644
--- a/backend/src/app/rpc/mutations/profile.clj
+++ b/backend/src/app/rpc/mutations/profile.clj
@@ -55,7 +55,7 @@
           :opt-un [::token]))
 
 (sv/defmethod ::register-profile {:auth false :rlimit :password}
-  [{:keys [pool tokens session storage] :as cfg} {:keys [token] :as params}]
+  [{:keys [pool tokens session] :as cfg} {:keys [token] :as params}]
   (when-not (:registration-enabled cfg/config)
     (ex/raise :type :restriction
               :code :registration-disabled))
@@ -69,7 +69,7 @@
     (check-profile-existence! conn params)
     (let [profile (->> (create-profile conn params)
                        (create-profile-relations conn))]
-      (create-profile-initial-data conn storage profile)
+      (create-profile-initial-data conn profile)
 
       (if token
         ;; If token comes in params, this is because the user comes
@@ -160,18 +160,21 @@
 (defn- create-profile
   "Create the profile entry on the database with limited input
   filling all the other fields with defaults."
-  [conn {:keys [id fullname email password demo?] :as params}]
+  [conn {:keys [id fullname email password demo? props] :as params}]
   (let [id       (or id (uuid/next))
         demo?    (if (boolean? demo?) demo? false)
         active?  (if demo? true false)
+        props    (db/tjson (or props {}))
         password (derive-password password)]
-    (db/insert! conn :profile
-                {:id id
-                 :fullname fullname
-                 :email (str/lower email)
-                 :password password
-                 :is-active active?
-                 :is-demo demo?})))
+    (-> (db/insert! conn :profile
+                    {:id id
+                     :fullname fullname
+                     :email (str/lower email)
+                     :password password
+                     :props props
+                     :is-active active?
+                     :is-demo demo?})
+        (update :props db/decode-transit-pgobject))))
 
 (defn- create-profile-relations
   [conn profile]
diff --git a/backend/src/app/srepl/main.clj b/backend/src/app/srepl/main.clj
index f62dfa91f..99a69b586 100644
--- a/backend/src/app/srepl/main.clj
+++ b/backend/src/app/srepl/main.clj
@@ -44,15 +44,17 @@
           (update :data pmg/migrate-data)))))
 
 
-;; Examples
+;; Examples:
 ;; (def backup (update-file  #uuid "1586e1f0-3e02-11eb-b1d2-556a2f641513" identity))
-;; (def x (update-file #uuid "1586e1f0-3e02-11eb-b1d2-556a2f641513" (fn [{:keys [data] :as file}] (update-in data [:pages-index #uuid "878278c0-3ef0-11eb-9d67-8551e7624f43" :objects] dissoc nil))))
+;; (def x (update-file
+;;         #uuid "1586e1f0-3e02-11eb-b1d2-556a2f641513"
+;;         (fn [{:keys [data] :as file}]
+;;           (update-in data [:pages-index #uuid "878278c0-3ef0-11eb-9d67-8551e7624f43" :objects] dissoc nil))))
+
+(def default-project-id #uuid "5761a890-3b81-11eb-9e7d-556a2f641513")
 
 (defn initial-data-dump
-  ([system file]
-   (let [default-project-id #uuid "5761a890-3b81-11eb-9e7d-556a2f641513"]
-     (initial-data-dump system default-project-id file)))
-
-  ([system project-id file]
+  ([system file] (initial-data-dump system default-project-id file))
+  ([system project-id path]
    (db/with-atomic [conn (:app.db/pool system)]
-     (pid/create-initial-data-dump conn  file))))
+     (pid/create-initial-data-dump conn project-id path))))