Repository: CSCfi/rems
Pull request #1491 (2019-08-14 13:33:52)

Add rems.api.services.workflow, refactor


Patch:
diff --git a/src/clj/rems/api/applications.clj b/src/clj/rems/api/applications.clj
index 99b5bbe22..f7091e50f 100644
--- a/src/clj/rems/api/applications.clj
+++ b/src/clj/rems/api/applications.clj
@@ -67,42 +67,6 @@
 
 ;; Api implementation
 
-(defn invalid-user? [u]
-  (or (str/blank? (:eppn u))
-      (str/blank? (:commonName u))
-      (str/blank? (:mail u))))
-
-;; XXX: Adding these attributes is not done consistently when retrieving
-;;   the data for a user.
-(defn format-user [u]
-  {:userid (:eppn u)
-   :name (:commonName u)
-   :email (:mail u)})
-
-;; TODO Filter applicant, requesting user
-;;
-;; XXX: Removing invalid users is not done consistently. It seems that
-;;   only the following API calls are affected:
-;;
-;;     /applications/commenters
-;;     /applications/members
-;;     /applications/deciders
-;;     /workflows/actors
-;;
-;;   For example, a user without commonName is able to log in and send an
-;;   application, and the application is visible to the handler and can
-;;   be approved.
-(defn get-users []
-  (->> (users/get-all-users)
-       (remove invalid-user?)
-       (map format-user)))
-
-(def get-applicants get-users)
-
-(def get-commenters get-users)
-
-(def get-deciders get-users)
-
 (def ^:private todo-roles
   #{:handler :commenter :decider :past-commenter :past-decider})
 
@@ -213,19 +177,19 @@
       :summary "Available third party commenters"
       :roles #{:handler}
       :return Commenters
-      (ok (get-commenters)))
+      (ok (users/get-commenters)))
 
     (GET "/members" []
       :summary "Existing REMS users available for application membership"
       :roles #{:handler}
       :return [Applicant]
-      (ok (get-applicants)))
+      (ok (users/get-applicants)))
 
     (GET "/deciders" []
       :summary "Available deciders"
       :roles #{:handler}
       :return Deciders
-      (ok (get-deciders)))
+      (ok (users/get-deciders)))
 
     (GET "/attachment/:attachment-id" []
       :summary "Get an attachment"
diff --git a/src/clj/rems/api/services/workflow.clj b/src/clj/rems/api/services/workflow.clj
new file mode 100644
index 000000000..dd5b1be66
--- /dev/null
+++ b/src/clj/rems/api/services/workflow.clj
@@ -0,0 +1,51 @@
+(ns rems.api.services.workflow
+  (:require [rems.db.catalogue :as catalogue]
+            [rems.db.core :as db]
+            [rems.db.workflow :as workflow]
+            [rems.db.users :as users]
+            [rems.json :as json])
+  (:import [org.apache.commons.lang3 NotImplementedException]))
+
+(defn- create-auto-approve-workflow! [{:keys [user-id organization title]}]
+  (assert user-id)
+  ;; TODO: create a new auto-approve workflow in the style of dynamic workflows
+  (throw (NotImplementedException. "auto-approve workflows are not yet implemented")))
+
+(defn- create-dynamic-workflow! [{:keys [user-id organization title handlers]}]
+  (assert user-id)
+  (assert organization)
+  (assert title)
+  (assert (every? string? handlers) {:handlers handlers})
+  (let [wfid (:id (db/create-workflow! {:organization organization,
+                                        :owneruserid user-id,
+                                        :modifieruserid user-id,
+                                        :title title,
+                                        :workflow (json/generate-string {:type :workflow/dynamic
+                                                                         :handlers handlers})}))]
+    {:id wfid}))
+
+(defn create-workflow! [command]
+  (let [result (case (:type command)
+                 :auto-approve (create-auto-approve-workflow! command)
+                 :dynamic (create-dynamic-workflow! command))]
+    (merge
+     result
+     {:success (not (nil? (:id result)))})))
+
+(defn update-workflow! [command]
+  (let [catalogue-items
+        (->> (catalogue/get-localized-catalogue-items {:workflow (:id command) :archived false})
+             (map #(select-keys % [:id :title :localizations])))]
+    (if (and (:archived command) (seq catalogue-items))
+      {:success false
+       :errors [{:type :t.administration.errors/workflow-in-use :catalogue-items catalogue-items}]}
+      (do
+        (db/update-workflow! (merge (select-keys command [:id :enabled :archived :title])
+                                    (when-let [handlers (:handlers command)]
+                                      {:workflow (json/generate-string {:type :workflow/dynamic
+                                                                        :handlers handlers})})))
+        {:success true}))))
+
+(defn get-workflow [id] (workflow/get-workflow id))
+(defn get-workflows [filters] (workflow/get-workflows filters))
+(defn get-available-actors [] (users/get-users))
diff --git a/src/clj/rems/api/workflows.clj b/src/clj/rems/api/workflows.clj
index 6ce90b00e..8197b430f 100644
--- a/src/clj/rems/api/workflows.clj
+++ b/src/clj/rems/api/workflows.clj
@@ -1,48 +1,13 @@
 (ns rems.api.workflows
-  (:require [clj-time.core :as time-core]
-            [clojure.test :refer [deftest is]]
-            [compojure.api.sweet :refer :all]
-            [rems.api.applications :refer [User get-users format-user]]
+  (:require [compojure.api.sweet :refer :all]
+            [rems.api.applications :refer [User]]
             [rems.api.schema :refer [SuccessResponse UserId Workflow]]
+            [rems.api.services.workflow :as workflow]
             [rems.api.util] ; required for route :roles
             [rems.db.core :as db]
-            [rems.db.users :as users]
-            [rems.db.workflow :as workflow]
-            [rems.util :refer [getx-user-id update-present]]
+            [rems.util :refer [getx-user-id]]
             [ring.util.http-response :refer :all]
-            [schema.core :as s])
-  (:import [org.joda.time DateTime DateTimeZone]))
-
-(defn parse-db-time [s]
-  (when s
-    (-> (DateTime/parse s)
-        (.withZone DateTimeZone/UTC))))
-
-(deftest test-parse-db-time
-  (is (= nil (parse-db-time nil)))
-  (is (= (time-core/date-time 2019 1 30 7 56 38 627) (parse-db-time "2019-01-30T09:56:38.627616+02:00")))
-  (is (= (time-core/date-time 2015 2 13 12 47 26) (parse-db-time "2015-02-13T14:47:26+02:00"))))
-
-(defn format-license [license]
-  (-> license
-      (select-keys [:type :textcontent :localizations])
-      (assoc :start (parse-db-time (:start license)))
-      (assoc :end (parse-db-time (:end license)))))
-
-(defn- format-workflow
-  [{:keys [id organization owneruserid modifieruserid title workflow start end expired enabled archived licenses]}]
-  {:id id
-   :organization organization
-   :owneruserid owneruserid
-   :modifieruserid modifieruserid
-   :title title
-   :workflow workflow
-   :start start
-   :end end
-   :expired expired
-   :enabled enabled
-   :archived archived
-   :licenses (mapv format-license licenses)})
+            [schema.core :as s]))
 
 (s/defschema CreateWorkflowCommand
   {:organization s/Str
@@ -65,22 +30,6 @@
 ; TODO: deduplicate or decouple with /api/applications/reviewers API?
 (s/defschema AvailableActor User)
 (s/defschema AvailableActors [AvailableActor])
-(def get-available-actors get-users) ;; TODO move get-users to rems.db.users
-
-(def ^:private get-user (comp format-user users/get-user-attributes))
-
-(defn- enrich-and-format-workflow [wf]
-  (-> wf
-      (update-present :workflow update :handlers #(mapv get-user %))
-      format-workflow))
-
-(defn- get-workflows [filters]
-  (mapv enrich-and-format-workflow (workflow/get-workflows filters)))
-
-(defn- get-workflow [workflow-id]
-  (-> workflow-id
-      workflow/get-workflow
-      enrich-and-format-workflow))
 
 (def workflows-api
   (context "/workflows" []
@@ -93,9 +42,9 @@
                      {expired :- (describe s/Bool "whether to include expired workflows") false}
                      {archived :- (describe s/Bool "whether to include archived workflows") false}]
       :return [Workflow]
-      (ok (get-workflows (merge (when-not expired {:expired false})
-                                (when-not disabled {:enabled true})
-                                (when-not archived {:archived false})))))
+      (ok (workflow/get-workflows (merge (when-not expired {:expired false})
+                                         (when-not disabled {:enabled true})
+                                         (when-not archived {:archived false})))))
 
     (POST "/create" []
       :summary "Create workflow"
@@ -115,11 +64,11 @@
       :summary "List of available actors"
       :roles #{:owner}
       :return AvailableActors
-      (ok (get-available-actors)))
+      (ok (workflow/get-available-actors)))
 
     (GET "/:workflow-id" []
       :summary "Get workflow by id"
       :roles #{:owner}
       :path-params [workflow-id :- (describe s/Int "workflow-id")]
       :return Workflow
-      (ok (get-workflow workflow-id)))))
+      (ok (workflow/get-workflow workflow-id)))))
diff --git a/src/clj/rems/application/model.clj b/src/clj/rems/application/model.clj
index 42117c078..ae914d5a6 100644
--- a/src/clj/rems/application/model.clj
+++ b/src/clj/rems/application/model.clj
@@ -492,7 +492,7 @@
 (defn enrich-workflow-handlers [application get-workflow]
   (if (= :workflow/dynamic (get-in application [:application/workflow :workflow/type]))
     (let [workflow (get-workflow (get-in application [:application/workflow :workflow/id]))
-          handlers (set (get-in workflow [:workflow :handlers]))]
+          handlers (set (mapv :userid (get-in workflow [:workflow :handlers])))]
       (-> application
           (assoc-in [:application/workflow :workflow.dynamic/handlers] handlers)
           (permissions/give-role-to-users :handler handlers)))
diff --git a/src/clj/rems/db/users.clj b/src/clj/rems/db/users.clj
index c637d3fe0..563ff26f9 100644
--- a/src/clj/rems/db/users.clj
+++ b/src/clj/rems/db/users.clj
@@ -1,7 +1,20 @@
 (ns rems.db.users
-  (:require [rems.db.core :as db]
+  (:require [clojure.string :as str]
+            [rems.db.core :as db]
             [rems.json :as json]))
 
+;; XXX: Adding these attributes is not done consistently when retrieving
+;;   the data for a user.
+(defn format-user [u]
+  {:userid (:eppn u)
+   :name (:commonName u)
+   :email (:mail u)})
+
+(defn- invalid-user? [u]
+  (or (str/blank? (:eppn u))
+      (str/blank? (:commonName u))
+      (str/blank? (:mail u))))
+
 (defn add-user! [user userattrs]
   (assert (and userattrs user) "User or user attributes missing!")
   (db/add-user! {:user user :userattrs (json/generate-string userattrs)}))
@@ -27,7 +40,36 @@
        (map get-user-attributes)
        (doall)))
 
+;; TODO Filter applicant, requesting user
+;;
+;; XXX: Removing invalid users is not done consistently. It seems that
+;;   only the following API calls are affected:
+;;
+;;     /applications/commenters
+;;     /applications/members
+;;     /applications/deciders
+;;     /workflows/actors
+;;
+;;   For example, a user without commonName is able to log in and send an
+;;   application, and the application is visible to the handler and can
+;;   be approved.
+(defn get-users []
+  (->> (get-all-users)
+       (remove invalid-user?)
+       (map format-user)))
+
+(def get-applicants get-users)
+
+(def get-commenters get-users)
+
+(def get-deciders get-users)
+
 (defn get-users-with-role [role]
   (->> (db/get-users-with-role {:role (name role)})
        (map :userid)
        (doall)))
+
+(defn get-user [userid]
+  (->> userid
+       get-user-attributes
+       format-user))
diff --git a/src/clj/rems/db/workflow.clj b/src/clj/rems/db/workflow.clj
index 447114896..2033d1302 100644
--- a/src/clj/rems/db/workflow.clj
+++ b/src/clj/rems/db/workflow.clj
@@ -1,8 +1,49 @@
 (ns rems.db.workflow
-  (:require [rems.db.catalogue :as catalogue]
+  (:require [clj-time.core :as time-core]
+            [clojure.test :refer [deftest is]]
+            [rems.db.catalogue :as catalogue]
             [rems.db.core :as db]
-            [rems.json :as json])
-  (:import [org.apache.commons.lang3 NotImplementedException]))
+            [rems.db.users :as users]
+            [rems.json :as json]
+            [rems.util :refer [update-present]])
+  (:import [org.joda.time DateTime DateTimeZone]))
+
+(defn- parse-db-time [s]
+  (when s
+    (-> (DateTime/parse s)
+        (.withZone DateTimeZone/UTC))))
+
+(deftest test-parse-db-time
+  (is (= nil (parse-db-time nil)))
+  (is (= (time-core/date-time 2019 1 30 7 56 38 627) (parse-db-time "2019-01-30T09:56:38.627616+02:00")))
+  (is (= (time-core/date-time 2015 2 13 12 47 26) (parse-db-time "2015-02-13T14:47:26+02:00"))))
+
+;; TODO: This should probably be in rems.db.licenses.
+(defn- format-license [license]
+  (-> license
+      (select-keys [:type :textcontent :localizations])
+      (assoc :start (parse-db-time (:start license)))
+      (assoc :end (parse-db-time (:end license)))))
+
+(defn- format-workflow
+  [{:keys [id organization owneruserid modifieruserid title workflow start end expired enabled archived licenses]}]
+  {:id id
+   :organization organization
+   :owneruserid owneruserid
+   :modifieruserid modifieruserid
+   :title title
+   :workflow workflow
+   :start start
+   :end end
+   :expired expired
+   :enabled enabled
+   :archived archived
+   :licenses (mapv format-license licenses)})
+
+(defn- enrich-and-format-workflow [wf]
+  (-> wf
+      (update-present :workflow update :handlers #(mapv users/get-user %))
+      format-workflow))
 
 (defn- parse-workflow-body [json]
   (json/parse-string json))
@@ -15,50 +56,12 @@
       db/get-workflow
       (update :workflow parse-workflow-body)
       (update :licenses parse-licenses)
-      db/assoc-expired))
+      db/assoc-expired
+      enrich-and-format-workflow))
 
 (defn get-workflows [filters]
   (->> (db/get-workflows)
        (map #(update % :workflow parse-workflow-body))
        (map db/assoc-expired)
-       (db/apply-filters filters)))
-
-(defn- create-auto-approve-workflow! [{:keys [user-id organization title]}]
-  (assert user-id)
-  ;; TODO: create a new auto-approve workflow in the style of dynamic workflows
-  (throw (NotImplementedException. "auto-approve workflows are not yet implemented")))
-
-(defn- create-dynamic-workflow! [{:keys [user-id organization title handlers]}]
-  (assert user-id)
-  (assert organization)
-  (assert title)
-  (assert (every? string? handlers) {:handlers handlers})
-  (let [wfid (:id (db/create-workflow! {:organization organization,
-                                        :owneruserid user-id,
-                                        :modifieruserid user-id,
-                                        :title title,
-                                        :workflow (json/generate-string {:type :workflow/dynamic
-                                                                         :handlers handlers})}))]
-    {:id wfid}))
-
-(defn create-workflow! [command]
-  (let [result (case (:type command)
-                 :auto-approve (create-auto-approve-workflow! command)
-                 :dynamic (create-dynamic-workflow! command))]
-    (merge
-     result
-     {:success (not (nil? (:id result)))})))
-
-(defn update-workflow! [command]
-  (let [catalogue-items
-        (->> (catalogue/get-localized-catalogue-items {:workflow (:id command) :archived false})
-             (map #(select-keys % [:id :title :localizations])))]
-    (if (and (:archived command) (seq catalogue-items))
-      {:success false
-       :errors [{:type :t.administration.errors/workflow-in-use :catalogue-items catalogue-items}]}
-      (do
-        (db/update-workflow! (merge (select-keys command [:id :enabled :archived :title])
-                                    (when-let [handlers (:handlers command)]
-                                      {:workflow (json/generate-string {:type :workflow/dynamic
-                                                                        :handlers handlers})})))
-        {:success true}))))
+       (db/apply-filters filters)
+       (mapv enrich-and-format-workflow)))


Test patch:
diff --git a/src/clj/rems/db/test_data.clj b/src/clj/rems/db/test_data.clj
index 8976e511e..730d23f86 100644
--- a/src/clj/rems/db/test_data.clj
+++ b/src/clj/rems/db/test_data.clj
@@ -3,17 +3,59 @@
   (:require [clj-time.core :as time]
             [rems.api.services.licenses :as licenses]
             [rems.api.services.resource :as resource]
+            [rems.api.services.workflow :as workflow]
             [rems.db.applications :as applications]
             [rems.db.catalogue :as catalogue]
             [rems.db.core :as db]
             [rems.db.form :as form]
             [rems.db.roles :as roles]
             [rems.db.users :as users]
-            [rems.db.workflow :as workflow]
             [rems.poller.entitlements :as entitlements-poller]
             [ring.util.http-response :refer [bad-request!]])
   (:import [java.util UUID]))
 
+;;; test data definitions
+
+(def +fake-users+
+  {:applicant1 "alice"
+   :applicant2 "malice"
+   :approver1 "developer"
+   :approver2 "bob"
+   :owner "owner"
+   :reporter "reporter"
+   :reviewer "carl"
+   :roleless1 "elsa"
+   :roleless2 "frank"})
+
+(def +fake-user-data+
+  {"developer" {:eppn "developer" :mail "developer@example.com" :commonName "Developer"}
+   "alice" {:eppn "alice" :mail "alice@example.com" :commonName "Alice Applicant"}
+   "malice" {:eppn "malice" :mail "malice@example.com" :commonName "Malice Applicant" :twinOf "alice" :other "Attribute Value"}
+   "bob" {:eppn "bob" :mail "bob@example.com" :commonName "Bob Approver"}
+   "carl" {:eppn "carl" :mail "carl@example.com" :commonName "Carl Reviewer"}
+   "elsa" {:eppn "elsa" :mail "elsa@example.com" :commonName "Elsa Roleless"}
+   "frank" {:eppn "frank" :mail "frank@example.com" :commonName "Frank Roleless"}
+   "owner" {:eppn "owner" :mail "owner@example.com" :commonName "Owner"}
+   "reporter" {:eppn "reporter" :mail "reporter@example.com" :commonName "Reporter"}})
+
+(def +demo-users+
+  {:applicant1 "RDapplicant1@funet.fi"
+   :applicant2 "RDapplicant2@funet.fi"
+   :approver1 "RDapprover1@funet.fi"
+   :approver2 "RDapprover2@funet.fi"
+   :reviewer "RDreview@funet.fi"
+   :owner "RDowner@funet.fi"
+   :reporter "RDdomainreporter@funet.fi"})
+
+(def +demo-user-data+
+  {"RDapplicant1@funet.fi" {:eppn "RDapplicant1@funet.fi" :mail "RDapplicant1.test@test_example.org" :commonName "RDapplicant1 REMSDEMO1"}
+   "RDapplicant2@funet.fi" {:eppn "RDapplicant2@funet.fi" :mail "RDapplicant2.test@test_example.org" :commonName "RDapplicant2 REMSDEMO"}
+   "RDapprover1@funet.fi" {:eppn "RDapprover1@funet.fi" :mail "RDapprover1.test@rems_example.org" :commonName "RDapprover1 REMSDEMO"}
+   "RDapprover2@funet.fi" {:eppn "RDapprover2@funet.fi" :mail "RDapprover2.test@rems_example.org" :commonName "RDapprover2 REMSDEMO"}
+   "RDreview@funet.fi" {:eppn "RDreview@funet.fi" :mail "RDreview.test@rems_example.org" :commonName "RDreview REMSDEMO"}
+   "RDowner@funet.fi" {:eppn "RDowner@funet.fi" :mail "RDowner.test@test_example.org" :commonName "RDowner REMSDEMO"}
+   "RDdomainreporter@funet.fi" {:eppn "RDdomainreporter@funet.fi" :mail "RDdomainreporter.test@test_example.org" :commonName "RDdomainreporter REMSDEMO"}})
+
 ;;; helpers for generating test data
 
 (defn command! [command]
@@ -32,6 +74,12 @@
                  (assoc-in m [k2 k1] v))
                {})))
 
+(defn create-user! [user-attributes & roles]
+  (let [user (:eppn user-attributes)]
+    (users/add-user! user user-attributes)
+    (doseq [role roles]
+      (roles/add-role! user role))))
+
 (defn create-license! [{:keys [actor]
                         :license/keys [type title link text]
                         :as command}]
@@ -66,11 +114,15 @@
 
 (defn create-dynamic-workflow! [{:keys [actor organization title handlers]
                                  :as command}]
-  (let [result (workflow/create-workflow! {:user-id (or actor "owner")
-                                           :organization (or organization "abc")
-                                           :title (or title "")
-                                           :type :dynamic
-                                           :handlers (or handlers ["developer"])})]
+  (let [result (workflow/create-workflow!
+                {:user-id (or actor "owner")
+                 :organization (or organization "abc")
+                 :title (or title "")
+                 :type :dynamic
+                 :handlers
+                 (or handlers
+                     (do (create-user! (get +fake-user-data+ "developer"))
+                         ["developer"]))})]
     (assert (:success result) {:command command :result result})
     (:id result)))
 
@@ -130,53 +182,7 @@
                        :actor actor})
     app-id))
 
-;;; test data
-
-(def +fake-users+
-  {:applicant1 "alice"
-   :applicant2 "malice"
-   :approver1 "developer"
-   :approver2 "bob"
-   :owner "owner"
-   :reporter "reporter"
-   :reviewer "carl"
-   :roleless1 "elsa"
-   :roleless2 "frank"})
-
-(def +fake-user-data+
-  {"developer" {:eppn "developer" :mail "developer@example.com" :commonName "Developer"}
-   "alice" {:eppn "alice" :mail "alice@example.com" :commonName "Alice Applicant"}
-   "malice" {:eppn "malice" :mail "malice@example.com" :commonName "Malice Applicant" :twinOf "alice" :other "Attribute Value"}
-   "bob" {:eppn "bob" :mail "bob@example.com" :commonName "Bob Approver"}
-   "carl" {:eppn "carl" :mail "carl@example.com" :commonName "Carl Reviewer"}
-   "elsa" {:eppn "elsa" :mail "elsa@example.com" :commonName "Elsa Roleless"}
-   "frank" {:eppn "frank" :mail "frank@example.com" :commonName "Frank Roleless"}
-   "owner" {:eppn "owner" :mail "owner@example.com" :commonName "Owner"}
-   "reporter" {:eppn "reporter" :mail "reporter@example.com" :commonName "Reporter"}})
-
-(def +demo-users+
-  {:applicant1 "RDapplicant1@funet.fi"
-   :applicant2 "RDapplicant2@funet.fi"
-   :approver1 "RDapprover1@funet.fi"
-   :approver2 "RDapprover2@funet.fi"
-   :reviewer "RDreview@funet.fi"
-   :owner "RDowner@funet.fi"
-   :reporter "RDdomainreporter@funet.fi"})
-
-(def +demo-user-data+
-  {"RDapplicant1@funet.fi" {:eppn "RDapplicant1@funet.fi" :mail "RDapplicant1.test@test_example.org" :commonName "RDapplicant1 REMSDEMO1"}
-   "RDapplicant2@funet.fi" {:eppn "RDapplicant2@funet.fi" :mail "RDapplicant2.test@test_example.org" :commonName "RDapplicant2 REMSDEMO"}
-   "RDapprover1@funet.fi" {:eppn "RDapprover1@funet.fi" :mail "RDapprover1.test@rems_example.org" :commonName "RDapprover1 REMSDEMO"}
-   "RDapprover2@funet.fi" {:eppn "RDapprover2@funet.fi" :mail "RDapprover2.test@rems_example.org" :commonName "RDapprover2 REMSDEMO"}
-   "RDreview@funet.fi" {:eppn "RDreview@funet.fi" :mail "RDreview.test@rems_example.org" :commonName "RDreview REMSDEMO"}
-   "RDowner@funet.fi" {:eppn "RDowner@funet.fi" :mail "RDowner.test@test_example.org" :commonName "RDowner REMSDEMO"}
-   "RDdomainreporter@funet.fi" {:eppn "RDdomainreporter@funet.fi" :mail "RDdomainreporter.test@test_example.org" :commonName "RDdomainreporter REMSDEMO"}})
-
-(defn- create-user! [user-attributes & roles]
-  (let [user (:eppn user-attributes)]
-    (users/add-user! user user-attributes)
-    (doseq [role roles]
-      (roles/add-role! user role))))
+;;; generate test data
 
 (defn create-users-and-roles! []
   ;; users provided by the fake login
diff --git a/test/clj/rems/api/test_workflows.clj b/test/clj/rems/api/test_workflows.clj
index 96a5c1ae3..0b33b81cb 100644
--- a/test/clj/rems/api/test_workflows.clj
+++ b/test/clj/rems/api/test_workflows.clj
@@ -1,10 +1,10 @@
 (ns ^:integration rems.api.test-workflows
   (:require [clojure.test :refer :all]
+            [rems.api.services.workflow :as workflow]
             [rems.api.testing :refer :all]
             [rems.common-util :refer [index-by]]
             [rems.db.test-data :as test-data]
             [rems.db.testing :refer [sync-with-database-time]]
-            [rems.db.workflow :as workflow]
             [rems.handler :refer [handler]]
             [ring.mock.request :refer :all]))
 
diff --git a/test/clj/rems/application/test_commands.clj b/test/clj/rems/application/test_commands.clj
index f429a4dad..55fd0a6dd 100644
--- a/test/clj/rems/application/test_commands.clj
+++ b/test/clj/rems/application/test_commands.clj
@@ -24,7 +24,9 @@
                                     :form/id 1
                                     :workflow/id 1
                                     :workflow/type :workflow/dynamic})
-(def ^:private dummy-workflows {1 {:workflow {:handlers [handler-user-id]}}})
+(def ^:private dummy-workflows {1 {:workflow {:handlers [{:userid handler-user-id
+                                                          :name "user"
+                                                          :email "user@example.com"}]}}})
 (def ^:private dummy-forms {1 {}})
 
 (def ^:private injections
diff --git a/test/clj/rems/application/test_model.clj b/test/clj/rems/application/test_model.clj
index 5148dacbb..8716edb5c 100644
--- a/test/clj/rems/application/test_model.clj
+++ b/test/clj/rems/application/test_model.clj
@@ -176,7 +176,9 @@
        :organization "org"
        :title "workflow title"
        :workflow {:type "workflow/dynamic"
-                  :handlers ["handler"]}
+                  :handlers [{:userid "handler"
+                              :name "Handler"
+                              :email "handler@example.com"}]}
        :licenses nil
        :owneruserid "owner"
        :modifieruserid "owner"
diff --git a/test/clj/rems/db/test_entitlements.clj b/test/clj/rems/db/test_entitlements.clj
index f3b918888..22411fdc5 100644
--- a/test/clj/rems/db/test_entitlements.clj
+++ b/test/clj/rems/db/test_entitlements.clj
@@ -100,9 +100,9 @@
                                                           :license-ids [lic-id1]})
                 :form-id form-id
                 :workflow-id wfid})]
-    (db/add-user! {:user applicant :userattrs (cheshire/generate-string {"mail" "b@o.b"})})
-    (db/add-user! {:user member :userattrs (cheshire/generate-string {"mail" "e.l@s.a"})})
-    (db/add-user! {:user admin :userattrs nil})
+    (test-data/create-user! {:eppn applicant :mail "b@o.b" :commonName "Bob"})
+    (test-data/create-user! {:eppn member :mail "e.l@s.a" :commonName "Elsa"})
+    (test-data/create-user! {:eppn admin :mail "o.w@n.er" :commonName "Owner"})
 
     (let [app-id (test-data/create-application! {:actor applicant :catalogue-item-ids [item1 item2]})]
       (testing "submitted application should not yet cause entitlements"
diff --git a/test/clj/rems/poller/test_email.clj b/test/clj/rems/poller/test_email.clj
index dcb22a07a..3f5f5719c 100644
--- a/test/clj/rems/poller/test_email.clj
+++ b/test/clj/rems/poller/test_email.clj
@@ -53,7 +53,12 @@
                             :title "fi title 21"}}}})
 
 (def ^:private get-workflow
-  {5 {:workflow {:handlers ["handler" "assistant"]}}})
+  {5 {:workflow {:handlers [{:userid "handler"
+                             :name "Handler"
+                             :email "handler@example.com"}
+                            {:userid "assistant"
+                             :name "Assistant"
+                             :email "assistant@example.com"}]}}})
 
 (defn ^:private get-nothing [& _]
   nil)
diff --git a/test/clj/rems/test_db.clj b/test/clj/rems/test_db.clj
index 57f8b65ec..374e383ea 100644
--- a/test/clj/rems/test_db.clj
+++ b/test/clj/rems/test_db.clj
@@ -37,8 +37,8 @@
           "should find same catalogue item by id"))))
 
 (deftest test-multi-applications
-  (db/add-user! {:user "test-user" :userattrs nil})
-  (db/add-user! {:user "handler" :userattrs nil})
+  (test-data/create-user! {:eppn "test-user" :mail "test-user@test.com" :commonName "Test-user"})
+  (test-data/create-user! {:eppn "handler" :mail "handler@test.com" :commonName "Handler"})
   (let [applicant "test-user"
         wfid (test-data/create-dynamic-workflow! {:handlers ["handler"]})
         res1 (test-data/create-resource! {:resource-ext-id "resid111"})
@@ -81,9 +81,9 @@
   (is (thrown? RuntimeException (roles/add-role! "pekka" :unknown-role))))
 
 (deftest test-get-entitlements-for-export
-  (db/add-user! {:user "handler" :userattrs nil})
-  (db/add-user! {:user "jack" :userattrs nil})
-  (db/add-user! {:user "jill" :userattrs nil})
+  (test-data/create-user! {:eppn "handler" :mail "handler@test.com" :commonName "Handler"})
+  (test-data/create-user! {:eppn "jack" :mail "jack@test.com" :commonName "Jack"})
+  (test-data/create-user! {:eppn "jill" :mail "jill@test.com" :commonName "Jill"})
   (let [wf (test-data/create-dynamic-workflow! {:handlers ["handler"]})
         form-id (test-data/create-form! {})
         res1 (test-data/create-resource! {:resource-ext-id "resource1"})