Repository: CSCfi/rems
Pull request #1487 (2019-08-14 08:43:48)

Add rems.api.services.licenses, providing API with licenses


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index 507b24fe1..000000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,131 +0,0 @@
-# Changelog
-
-All notable changes to this project will be documented in this file.
-Pull requests should add lines under the Unreleased heading if they
-have notable changes.
-
-## Unreleased
-
-Changes since v2.4
-
-### Breaking changes
-- Removed support for old round-based workflows
-- Removed support for transferring REMS 1 data
-- Replace applications API with new one (for dynamic applications)
-
-### Additions
-- Dynamic workflows
-- New field types: description, option, multiselect
-- Setting maximum length for a form field
-- Showing changes between two versions of an application
-- Show last modified time for applications
-- Many improvements in admininistration pages
-  - Archiving forms, workflows, licenses and catalogue items
-  - Preview for forms
-  - Editing workflows
-  - "Copy as new" button for forms
-- Upload an attachment file for a license (#808)
-- Adding and removing members to/from an application (#609, #870)
-- More configuration options for themes (e.g. alert colour)
-- Track license acceptance per member (#653)
-- Optional external id for applications (format "2019/123") (#862)
-- Reporter role
-- Accessibility improvements: screen reader support etc. (#1172)
-- Store user language preference, use chosen language for emails
-- Upgraded swagger-ui from 2 to 3
-- Extra pages (#472)
-
-### Enhancements
-- Improved version information in footer
-- More systematic use of db transactions
-- Improved table widget
-- Hide language switcher when only one language configured
-- PDFs are now downloaded as \<application-id\>.pdf instead of pdf.pdf
-
-### Fixes
-- Entitlement API
-
-## v2.4 "Tietotie" 2018-10-24
-
-Starting from this milestone releases will also include pre-built war and jar packages.
-
-### Additions
-- support for attachment fields on forms
-- API endpoint for adding users
-### Enhancements
-- entitlements API can now be used to fetch applicant's own entitlements
-- support for linking to catalogue items by their external resource ids (https://my-rems-instance.org/apply-for?resource=my-resource)
-### Changes
-- resource api now also returns owner attribute
-- configuration and localization has now been externalized from the deployed application (read more at https://rems2docs.rahtiapp.fi/configuration/)
-### Breaking Changes
-- prefix attribute renamed to organization in API
-- backwards compatibility with legacy applications related data has been dropped. Legacy workflows, forms etc. can still be migrated from REMS1 to REMS2
-### Fixes
-- issue where review buttons weren't rendered correctly
-- content created by editor wasn't sometimes shown immediately
-
-## v2.3 "Tekniikantie" 2018-08-29
-
-### Additions
-- support for date fields on forms
-- filtering functionality for tables
-- workflow editor to administration page
-- resource editor to administration page
-- license editor to administration page
-- pdf view for applications
-### Enhancements
-- support for extra script files with hooks
-- enable multiple login saml2 endpoints by providing a link to eds
-- Changes
-- minor cosmetic changes to status field on application page
-- workflows and applications forms now also have an organization prefix field
-### Fixes
-- current page highlighting
-
-## v2.2 "Vuorimiehentie" 2018-07-13
-
-### Enhancements
-- page transitions now have loading animations
-- configurable default language
-- configurable csv import separation symbol
-- header title added to localization
-- catalogue item creation from existing form, workflow and resource
-### Changes
-- catalogue now has a separate "more info" button instead of having items as links
-- handled applications are only loaded when "show more" has been clicked
-- updates to documentation
-### Fixes
-- nondeterministic redirect problems for authenticated users
-
-## v2.1 "Otaniementie" 2018-06-25
-
-### Changes
-- dependencies updated, the project is now using stable bootstrap 4 and font awesome 5
-- changed format of urns and made prefix configurable
-- updated demo-data target to be more aligned with test-data
-- review request list now ignores users with missing attributes
-- namespace cleanup according to coding conventions
-
-### Fixes
-- unauthenticated users are redirected to requested url after login
-- comments for review request are shown to 3rd party reviewers
-- fixed erroneous localization in status change notification
-- date to show user time
-
-## v2.0 "Keilaranta" 2018-05-29
-
-First production release with the following major features:
-
-- catalogue with shopping cart
-- application sending
-- approval process
-- 3rd party review
-- 3rd party review request
-- email notifications
-- application bundling
-- Saml2 & LDAP login
-- API with Swagger
-- configurable themes
-- configurable localizations
-- documentation server
diff --git a/dev-config.edn b/dev-config.edn
index 147319116..8635ee346 100644
--- a/dev-config.edn
+++ b/dev-config.edn
@@ -2,7 +2,6 @@
  :nrepl-port 7000
  :database-url "postgresql://localhost/rems?user=rems"
  :test-database-url "postgresql://localhost/rems_test?user=rems_test"
- :search-index-path "target/search-index-dev"
  :theme-path "example-theme/theme.edn"
  :authentication :fake-shibboleth
  :public-url "http://localhost:3000/"
diff --git a/docs/dictionary.md b/docs/dictionary.md
new file mode 100644
index 000000000..8207697bc
--- /dev/null
+++ b/docs/dictionary.md
@@ -0,0 +1,25 @@
+# Dictionary
+
+The glossary below provides definitions for words used in REMS:
+
+**Application**: An instance of an applicant applying for entitlement to some resources. Includes a form for the applicant to fill and licenses to accept.
+
+**Catalogue**: A catalogue lists the resources to which access can be applied for. Catalogue consists of catalogue items.
+
+**Catalogue Item**: A catalogue item is a resource whose owner has attached a workflow and a form to it. To make it appear in the catalogue, the resource owner must set the catalogue item to an enabled state.
+
+**Entitlement**: Access right to a resource.
+
+**Field**: A textarea, checkbox, attachment upload or some other place for user input.
+
+**Form**: A set of fields for the applicant to fill as part of an application.
+
+**License**: The applicant (and application members) must accept the license term of the application before being entitled to the resources.
+
+**REMS**: Resource Entitlement Management System.
+
+**Resource**: The asset (e.g. scientific dataset) whose entitlements (access rights) are managed by REMS.
+
+**Resource Owner**: A resource owner is the person who owns one or more resources in REMS. Resource owners are eligible to create and manage catalogue items (including workflows, licenses and forms).
+
+**Workflow**: The process for handing an application submitted by the applicant. In a dynamic workflow, the person in control of the application is called a handler. The handler can request for reviews from other people before approving or rejecting the application.
diff --git a/docs/glossary.md b/docs/glossary.md
index 55b738c32..6b15353b7 100644
--- a/docs/glossary.md
+++ b/docs/glossary.md
@@ -19,12 +19,9 @@
   - can be text, a link, or an attached file
   - can currently be attached to workflows, forms or catalogue items
   - upcoming: dynamic licenses added to application by handler
-- catalogue item: something an applicant can pick from the catalogue and apply for
-  - catalogue item = resource + workflow + form
+- catalogue item: resource+workflow+form
   - internal to rems
-- catalogue: the list of all (enabled) catalogue items
-- application: an instance of an applicant applying for entitlement to some resources
-  - application = answers + catalogue item(s) + events
+- application: answers + catalogue item(s) + events
   - answers mean values for the form fields + license acceptance
   - can also contain additional members
   - an application can have multiple catalogue items _if_ they have the same form & workflow
@@ -41,8 +38,6 @@
   - they will receive entitlements once the application is approved and they have accepted the licenses
 - user: somebody who is logged in to REMS. They can either be an applicant, a handler or somebody who
   responds to requests from handlers
-- reporter: a role that can view all applications
-- owner: a role that can create and edit resources, workflows, catalogue items, etc.
 
 ## Events
 
diff --git a/project.clj b/project.clj
index b4345268d..5c731567f 100644
--- a/project.clj
+++ b/project.clj
@@ -38,8 +38,6 @@
                  [metosin/ring-swagger "0.26.2"]
                  [metosin/ring-swagger-ui "3.20.1"]
                  [mount "0.1.15"]
-                 [org.apache.lucene/lucene-core "8.2.0"]
-                 [org.apache.lucene/lucene-queryparser "8.2.0"]
                  [org.clojars.luontola/garden "1.3.6-patch1"] ;; TODO: waiting for a new release with https://github.com/noprompt/garden/pull/172
                  [org.clojars.luontola/ns-tracker "0.3.1-patch3"] ;; TODO: waiting for a new release with https://github.com/weavejester/ns-tracker/pull/24
                  [org.clojars.pntblnk/clj-ldap "0.0.16"]
diff --git a/resources/config-defaults.edn b/resources/config-defaults.edn
index 6a19ee834..9ab658bd0 100644
--- a/resources/config-defaults.edn
+++ b/resources/config-defaults.edn
@@ -11,9 +11,6 @@
  :database-url nil
  :database-jndi-name "java:comp/env/jdbc/rems"
 
- ;; Path to a writable directory where to store the Lucene search index files.
- :search-index-path "search-index"
-
  ;; Supported authentication methods: :shibboleth, :fake-shibboleth, :ldap
  :authentication :shibboleth
  :ldap {:connection {:host "localhost:2636"
diff --git a/src/clj/rems/api/applications.clj b/src/clj/rems/api/applications.clj
index 99b5bbe22..91922638b 100644
--- a/src/clj/rems/api/applications.clj
+++ b/src/clj/rems/api/applications.clj
@@ -6,7 +6,6 @@
             [rems.api.util :as api-util] ; required for route :roles
             [rems.application-util :as application-util]
             [rems.application.commands :as commands]
-            [rems.application.search :as search]
             [rems.auth.util :refer [throw-forbidden]]
             [rems.context :as context]
             [rems.db.applications :as applications]
@@ -129,14 +128,6 @@
   (->> (get-potential-todos user-id)
        (remove todo?)))
 
-(defn- filter-with-search [query apps]
-  (if (str/blank? query)
-    apps
-    (let [app-ids (search/find-applications query)]
-      (filter (fn [app]
-                (contains? app-ids (:application/id app)))
-              apps))))
-
 (defn- coerce-command-from-api [cmd]
   ;; TODO: schema could do these coercions for us
   (update-present cmd :decision keyword))
@@ -170,9 +161,7 @@
       :summary "Get the current user's own applications"
       :roles #{:logged-in}
       :return [ApplicationOverview]
-      :query-params [{query :- (describe s/Str "search query") nil}]
-      (ok (->> (applications/get-my-applications (getx-user-id))
-               (filter-with-search query))))))
+      (ok (applications/get-my-applications (getx-user-id))))))
 
 (def applications-api
   (context "/applications" []
@@ -182,25 +171,19 @@
       :summary "Get all applications which the current user can see"
       :roles #{:logged-in}
       :return [ApplicationOverview]
-      :query-params [{query :- (describe s/Str "search query") nil}]
-      (ok (->> (applications/get-all-applications (getx-user-id))
-               (filter-with-search query))))
+      (ok (applications/get-all-applications (getx-user-id))))
 
     (GET "/todo" []
       :summary "Get all applications that the current user needs to act on."
       :roles #{:logged-in}
       :return [ApplicationOverview]
-      :query-params [{query :- (describe s/Str "search query") nil}]
-      (ok (->> (get-todos (getx-user-id))
-               (filter-with-search query))))
+      (ok (get-todos (getx-user-id))))
 
     (GET "/handled" []
       :summary "Get all applications that the current user no more needs to act on."
       :roles #{:logged-in}
       :return [ApplicationOverview]
-      :query-params [{query :- (describe s/Str "search query") nil}]
-      (ok (->> (get-handled-todos (getx-user-id))
-               (filter-with-search query))))
+      (ok (get-handled-todos (getx-user-id))))
 
     (POST "/create" []
       :summary "Create a new application"
@@ -304,7 +287,5 @@
                (pdf/application-to-pdf-bytes)
                (ByteArrayInputStream.)
                (ok)
-               ;; could also set "attachment" here to force download:
-               (header "Content-Disposition" (str "filename=\"" application-id ".pdf\""))
                (content-type "application/pdf")))
         (api-util/not-found-json-response)))))
diff --git a/src/clj/rems/api/licenses.clj b/src/clj/rems/api/licenses.clj
index 1fde96f60..c8152fa43 100644
--- a/src/clj/rems/api/licenses.clj
+++ b/src/clj/rems/api/licenses.clj
@@ -4,7 +4,7 @@
             [rems.api.util :as api-util] ; required for route :roles
             [rems.db.attachments :as attachments]
             [rems.db.core :as db]
-            [rems.db.licenses :as licenses]
+            [rems.api.services.licenses :as licenses]
             [rems.util :refer [getx-user-id]]
             [ring.middleware.multipart-params :as multipart]
             [ring.swagger.upload :as upload]
diff --git a/src/clj/rems/api/services/licenses.clj b/src/clj/rems/api/services/licenses.clj
new file mode 100644
index 000000000..d75dc7b8f
--- /dev/null
+++ b/src/clj/rems/api/services/licenses.clj
@@ -0,0 +1,79 @@
+(ns rems.api.services.licenses
+  "Serving licenses for API."
+  (:require [rems.common-util :refer [distinct-by]]
+            [rems.db.core :as db]
+            [rems.db.licenses :as licenses]
+            [rems.db.resource :as resource]
+            [rems.db.workflow :as workflow])
+  (:import (java.io FileInputStream ByteArrayOutputStream)))
+
+(defn create-license! [{:keys [title licensetype textcontent localizations attachment-id]} user-id]
+  (let [license (db/create-license! {:owneruserid user-id
+                                     :modifieruserid user-id
+                                     :type licensetype
+                                     :title title
+                                     :textcontent textcontent
+                                     :attachmentId attachment-id})
+        licid (:id license)]
+    (doseq [[langcode localization] localizations]
+      (db/create-license-localization! {:licid licid
+                                        :langcode (name langcode)
+                                        :title (:title localization)
+                                        :textcontent (:textcontent localization)
+                                        :attachmentId (:attachment-id localization)}))
+    {:success (not (nil? licid))
+     :id licid}))
+
+(defn create-license-attachment! [{:keys [tempfile filename content-type]} user-id]
+  (let [byte-array (with-open [input (FileInputStream. tempfile)
+                               buffer (ByteArrayOutputStream.)]
+                     (clojure.java.io/copy input buffer)
+                     (.toByteArray buffer))]
+    (select-keys
+     (db/create-license-attachment! {:user user-id
+                                     :filename filename
+                                     :type content-type
+                                     :data byte-array})
+     [:id])))
+
+(defn remove-license-attachment!
+  [attachment-id]
+  (db/remove-license-attachment! {:id attachment-id}))
+
+(defn- get-license-usage [id]
+  ;; these could be db joins
+  (let [resources (->> (db/get-resources-for-license {:id id})
+                       (map :resid)
+                       (map resource/get-resource)
+                       (remove :archived)
+                       (map #(select-keys % [:id :resid])))
+        workflows (->> (db/get-workflows-for-license {:id id})
+                       (map :wfid)
+                       (map workflow/get-workflow)
+                       (remove :archived)
+                       (map #(select-keys % [:id :title])))]
+    (when (or (seq resources) (seq workflows))
+      {:resources resources
+       :workflows workflows})))
+
+(defn update-license! [command]
+  (let [usage (get-license-usage (:id command))]
+    (if (and (:archived command) usage)
+      {:success false
+       :errors [(merge {:type :t.administration.errors/license-in-use}
+                       usage)]}
+      (do
+        (db/set-license-state! command)
+        {:success true}))))
+
+(defn get-license
+  "Get a single license by id"
+  [id]
+  (licenses/get-license id))
+
+(defn get-all-licenses
+  "Get all licenses.
+
+   filters is a map of key-value pairs that must be present in the licenses"
+  [filters]
+  (licenses/get-all-licenses filters))
diff --git a/src/clj/rems/application/search.clj b/src/clj/rems/application/search.clj
deleted file mode 100644
index 6500e2618..000000000
--- a/src/clj/rems/application/search.clj
+++ /dev/null
@@ -1,120 +0,0 @@
-(ns rems.application.search
-  (:require [clojure.java.io :as io]
-            [clojure.string :as str]
-            [clojure.tools.logging :as log]
-            [mount.core :as mount]
-            [rems.application-util :as application-util]
-            [rems.config :refer [env]]
-            [rems.db.applications :as applications]
-            [rems.db.events :as events]
-            [rems.text :as text])
-  (:import [org.apache.lucene.analysis.standard StandardAnalyzer]
-           [org.apache.lucene.document Document StringField Field$Store TextField]
-           [org.apache.lucene.index IndexWriter IndexWriterConfig IndexWriterConfig$OpenMode]
-           [org.apache.lucene.queryparser.flexible.core QueryNodeException]
-           [org.apache.lucene.queryparser.flexible.standard StandardQueryParser]
-           [org.apache.lucene.search IndexSearcher ScoreDoc TopDocs SearcherManager SearcherFactory Query]
-           [org.apache.lucene.store Directory NIOFSDirectory]))
-
-(def ^:private analyzer (StandardAnalyzer.))
-
-(mount/defstate ^Directory search-index
-  :start (let [directory (NIOFSDirectory. (.toPath (io/file (:search-index-path env))))]
-           (with-open [writer (IndexWriter. directory (-> (IndexWriterConfig. analyzer)
-                                                          (.setOpenMode IndexWriterConfig$OpenMode/CREATE)))]
-             (.deleteAll writer))
-           (atom {::directory directory
-                  ::searcher-manager (SearcherManager. directory (SearcherFactory.))
-                  ::last-processed-event-id 0}))
-  :stop (do
-          (.close ^SearcherManager (::searcher-manager @search-index))
-          (.close ^Directory (::directory @search-index))))
-
-(defn- index-terms-for-application [app]
-  {:id (->> [(:application/id app)
-             (:application/external-id app)]
-            (str/join " "))
-   :applicant (->> [(:application/applicant app)
-                    (application-util/get-applicant-name app)
-                    (:mail (:application/applicant-attributes app))]
-                   (str/join " "))
-   :member (->> (:application/members app)
-                (mapcat (fn [member]
-                          [(:userid member)
-                           (application-util/get-member-name member)
-                           (:mail member)]))
-                (str/join " "))
-   :title (:application/description app)
-   :resource (->> (:application/resources app)
-                  (mapcat (fn [resource]
-                            (vals (:catalogue-item/title resource))))
-                  (str/join " "))
-   :state (->> (:languages env)
-               (map (fn [lang]
-                      (text/with-language lang
-                        #(text/localize-state (:application/state app)))))
-               (str/join " "))
-   :form (->> (:form/fields (:application/form app))
-              (map (fn [field]
-                     ;; TODO: filter out checkboxes, attachments etc?
-                     (:field/value field)))
-              (str/join " "))})
-
-(defn- index-application! [^IndexWriter writer app]
-  (log/info "Indexing application" (:application/id app))
-  (let [doc (Document.)
-        terms (index-terms-for-application app)]
-    ;; metadata
-    (.add doc (StringField. "app-id" (str (:application/id app)) Field$Store/YES))
-    ;; searchable fields
-    (doseq [[k v] terms]
-      (.add doc (TextField. (name k) v Field$Store/NO)))
-    (.add doc (TextField. "all" (str/join " " (vals terms)) Field$Store/NO))
-    (.addDocument writer doc)))
-
-(def ^:private refresh-lock (Object.))
-
-(defn refresh! []
-  ;; Only one IndexWriter may use the directory at a time.
-  ;; Otherwise it'll throw a LockObtainFailedException.
-  (locking refresh-lock
-    (let [{::keys [directory ^SearcherManager searcher-manager last-processed-event-id]} @search-index
-          events (events/get-all-events-since last-processed-event-id)]
-      (when-not (empty? events)
-        (with-open [writer (IndexWriter. directory (-> (IndexWriterConfig. analyzer)
-                                                       (.setOpenMode IndexWriterConfig$OpenMode/APPEND)))]
-          (doseq [app-id (distinct (map :application/id events))]
-            (index-application! writer (applications/get-unrestricted-application app-id))))
-        (.maybeRefresh searcher-manager)
-        (swap! search-index assoc ::last-processed-event-id (:event/id (last events)))))))
-
-(defn- with-searcher [f]
-  (let [searcher-manager ^SearcherManager (::searcher-manager @search-index)
-        searcher ^IndexSearcher (.acquire searcher-manager)]
-    (try
-      (f searcher)
-      (finally
-        (.release searcher-manager searcher)))))
-
-(defn- get-application-ids [^IndexSearcher searcher ^TopDocs results]
-  (doall (for [^ScoreDoc hit (.-scoreDocs results)]
-           (let [doc (.doc searcher (.-doc hit))
-                 app-id (.get doc "app-id")]
-             (Long/parseLong app-id)))))
-
-(defn- ^Query parse-query [^String query]
-  (try
-    (-> (StandardQueryParser. analyzer)
-        (.parse query "all"))
-    (catch QueryNodeException e
-      (log/info (str "Failed to parse query '" query "', " e))
-      nil)))
-
-(defn find-applications [^String query]
-  (when-let [query (parse-query query)]
-    (refresh!) ; TODO: call from a background thread asynchronously?
-    (with-searcher
-     (fn [^IndexSearcher searcher]
-       (->> (.search searcher query Integer/MAX_VALUE)
-            (get-application-ids searcher)
-            set)))))
diff --git a/src/clj/rems/css/styles.clj b/src/clj/rems/css/styles.clj
index 956e6eb4f..f202e3fd7 100644
--- a/src/clj/rems/css/styles.clj
+++ b/src/clj/rems/css/styles.clj
@@ -669,11 +669,6 @@
 
      :.application-description {:max-width "30em"}
      :.application-applicant {:max-width "10em"}]]
-   [:.search-field {:display :flex
-                    :flex-wrap :nowrap
-                    :align-items :center}
-    [:label {:margin-bottom 0}] ; override the default from Bootstrap
-    [:div.input-group {:width "17em"}]]
 
    ;; !important is needed here, otherwise these attributes are overridden
    ;; by more specific styles by react-select.
diff --git a/src/clj/rems/db/licenses.clj b/src/clj/rems/db/licenses.clj
index 13d917f88..45bb245eb 100644
--- a/src/clj/rems/db/licenses.clj
+++ b/src/clj/rems/db/licenses.clj
@@ -1,9 +1,7 @@
 (ns rems.db.licenses
   "querying localized licenses"
   (:require [rems.common-util :refer [distinct-by]]
-            [rems.db.core :as db]
-            [rems.db.resource :as resource]
-            [rems.db.workflow :as workflow])
+            [rems.db.core :as db])
   (:import (java.io FileInputStream ByteArrayOutputStream)))
 
 (defn- format-license [license]
@@ -84,62 +82,3 @@
        (map (partial db/assoc-expired now))
        (remove :expired)
        (distinct-by :id)))
-
-(defn create-license! [{:keys [title licensetype textcontent localizations attachment-id]} user-id]
-  (let [license (db/create-license! {:owneruserid user-id
-                                     :modifieruserid user-id
-                                     :type licensetype
-                                     :title title
-                                     :textcontent textcontent
-                                     :attachmentId attachment-id})
-        licid (:id license)]
-    (doseq [[langcode localization] localizations]
-      (db/create-license-localization! {:licid licid
-                                        :langcode (name langcode)
-                                        :title (:title localization)
-                                        :textcontent (:textcontent localization)
-                                        :attachmentId (:attachment-id localization)}))
-    {:success (not (nil? licid))
-     :id licid}))
-
-(defn create-license-attachment! [{:keys [tempfile filename content-type]} user-id]
-  (let [byte-array (with-open [input (FileInputStream. tempfile)
-                               buffer (ByteArrayOutputStream.)]
-                     (clojure.java.io/copy input buffer)
-                     (.toByteArray buffer))]
-    (select-keys
-     (db/create-license-attachment! {:user user-id
-                                     :filename filename
-                                     :type content-type
-                                     :data byte-array})
-     [:id])))
-
-(defn remove-license-attachment!
-  [attachment-id]
-  (db/remove-license-attachment! {:id attachment-id}))
-
-(defn- get-license-usage [id]
-  ;; these could be db joins
-  (let [resources (->> (db/get-resources-for-license {:id id})
-                       (map :resid)
-                       (map resource/get-resource)
-                       (remove :archived)
-                       (map #(select-keys % [:id :resid])))
-        workflows (->> (db/get-workflows-for-license {:id id})
-                       (map :wfid)
-                       (map workflow/get-workflow)
-                       (remove :archived)
-                       (map #(select-keys % [:id :title])))]
-    (when (or (seq resources) (seq workflows))
-      {:resources resources
-       :workflows workflows})))
-
-(defn update-license! [command]
-  (let [usage (get-license-usage (:id command))]
-    (if (and (:archived command) usage)
-      {:success false
-       :errors [(merge {:type :t.administration.errors/license-in-use}
-                       usage)]}
-      (do
-        (db/set-license-state! command)
-        {:success true}))))
diff --git a/src/clj/rems/locales.clj b/src/clj/rems/locales.clj
index 3e420047a..953e67d64 100644
--- a/src/clj/rems/locales.clj
+++ b/src/clj/rems/locales.clj
@@ -16,9 +16,9 @@
                         (and file (.exists file)) file
                         resource resource
                         :else (throw (FileNotFoundException.
-                                      (if file
-                                        (str "translations could not be found in " file " file or " resource-path " resource")
-                                        (str "translations could not be found in " resource-path " resource and " :translations-directory " was not set")))))]
+                                       (if file
+                                         (str "translations could not be found in " file " file or " resource-path " resource")
+                                         (str "translations could not be found in " resource-path " resource and " :translations-directory " was not set")))))]
     (read-string (slurp file-contents))))
 
 (defn- extra-translations-path [theme-path]
@@ -43,5 +43,4 @@
 (defstate translations :start (load-translations env))
 
 (defn tempura-config []
-  (assert (map? translations) {:translations translations})
   {:dict translations})
diff --git a/src/clj/rems/permissions.clj b/src/clj/rems/permissions.clj
index a69dafb86..840fc0dc8 100644
--- a/src/clj/rems/permissions.clj
+++ b/src/clj/rems/permissions.clj
@@ -1,6 +1,7 @@
 (ns rems.permissions
-  (:require [clojure.test :refer [deftest is testing]]
-            [rems.util :refer [conj-set]]))
+  (:require [clojure.test :refer [deftest is testing]]))
+
+(def ^:private conj-set (fnil conj #{}))
 
 (defn- give-role-to-user [application role user]
   (assert (keyword? role) {:role role})
@@ -105,13 +106,13 @@
                                 :role-2 #{:bar}}}
            (-> {}
                (update-role-permissions {:role-1 [:foo]
-                                         :role-2 [:bar]})))))
+                                      :role-2 [:bar]})))))
   (testing "does not alter unrelated roles"
     (is (= {::role-permissions {:unrelated #{:foo}
                                 :role #{:gazonk}}}
            (-> {}
                (update-role-permissions {:unrelated [:foo]
-                                         :role [:bar]})
+                                      :role [:bar]})
                (update-role-permissions {:role [:gazonk]}))))))
 
 (defn remove-permission-from-all [application permission]
@@ -127,7 +128,7 @@
                                 :role-2 #{}}}
            (-> {}
                (update-role-permissions {:role-1 [:foo]
-                                         :role-2 [:foo]})
+                                      :role-2 [:foo]})
                (remove-permission-from-all :foo)))))
   (testing "leaves unrelated permissions unchanged"
     (is (= {::role-permissions {:role #{:bar}}}
diff --git a/src/clj/rems/standalone.clj b/src/clj/rems/standalone.clj
index d98c60b79..da8037e07 100644
--- a/src/clj/rems/standalone.clj
+++ b/src/clj/rems/standalone.clj
@@ -6,9 +6,7 @@
             [luminus.http-server :as http]
             [luminus.repl-server :as repl]
             [mount.core :as mount]
-            [rems.application.search :as search]
             [rems.config :refer [env]]
-            [rems.db.applications :as applications]
             [rems.db.test-data :as test-data]
             [rems.handler :as handler]
             [rems.validate :as validate])
@@ -41,12 +39,6 @@
   (doseq [component (:stopped (mount/stop))]
     (log/info component "stopped")))
 
-(defn- refresh-caches []
-  (log/info "Refreshing caches")
-  (applications/refresh-all-applications-cache!)
-  (search/refresh!)
-  (log/info "Caches refreshed"))
-
 (defn start-app [& args]
   (doseq [component (-> args
                         (parse-opts cli-options)
@@ -54,8 +46,7 @@
                         :started)]
     (log/info component "started"))
   (.addShutdownHook (Runtime/getRuntime) (Thread. stop-app))
-  (validate/validate)
-  (refresh-caches))
+  (validate/validate))
 
 (defn -main
   "Arguments can be either arguments to mount/start-with-args, or one of
diff --git a/src/clj/rems/util.clj b/src/clj/rems/util.clj
index 75e6a3a86..15d59d4ca 100644
--- a/src/clj/rems/util.clj
+++ b/src/clj/rems/util.clj
@@ -63,8 +63,6 @@
   ([user]
    (:mail user)))
 
-(def conj-set (fnil conj #{}))
-
 (defn update-present
   "Like clojure.core/update, but does nothing if the key `k` does not exist in `m`."
   [m k f & args]
diff --git a/src/clj/rems/validate.clj b/src/clj/rems/validate.clj
index 60a797a14..4ec221694 100644
--- a/src/clj/rems/validate.clj
+++ b/src/clj/rems/validate.clj
@@ -3,8 +3,7 @@
   (:require [clojure.pprint :refer [pprint]]
             [clojure.tools.logging :as log]
             [rems.api.forms]
-            [rems.application.events :as events]
-            [rems.db.events :as events-db]
+            [rems.db.applications :as applications]
             [rems.db.form :as form]
             [schema.core :as s]))
 
@@ -16,7 +15,8 @@
   (log/info "Validating data")
   (try
     (validate-forms)
-    (events/validate-events (events-db/get-all-events-since 0))
+    ;; will throw an exception if there are non-valid events
+    (applications/get-all-unrestricted-applications)
     (log/info "Validations passed")
     (catch Throwable t
       (log/error t "Validations failed" (with-out-str (when-let [data (ex-data t)]
diff --git a/src/cljs/rems/actions.cljs b/src/cljs/rems/actions.cljs
index 28a726403..32cb9a478 100644
--- a/src/cljs/rems/actions.cljs
+++ b/src/cljs/rems/actions.cljs
@@ -1,11 +1,9 @@
 (ns rems.actions
   "The /actions page that shows a list of applications you can act on."
   (:require [re-frame.core :as rf]
-            [reagent.core :as r]
             [rems.application-list :as application-list]
-            [rems.atoms :refer [document-title close-symbol]]
+            [rems.atoms :refer [document-title]]
             [rems.collapsible :as collapsible]
-            [rems.config :as config]
             [rems.guide-functions]
             [rems.spinner :as spinner]
             [rems.text :refer [text]]
@@ -14,11 +12,7 @@
 (rf/reg-event-fx
  ::enter-page
  (fn [{:keys [db]} _]
-   {:db (-> db
-            (dissoc ::todo-applications
-                    ::handled-applications)
-            (assoc ::initializing-todo-applications? true
-                   ::initializing-handled-applications? true))
+   {:db (dissoc db ::todo-applications ::handled-applications)
     :dispatch-n [[::fetch-todo-applications]
                  [:rems.table/reset]]}))
 
@@ -26,29 +20,17 @@
 
 (rf/reg-event-fx
  ::fetch-todo-applications
- (fn [{:keys [db]} [_ query]]
-   ;; do only one fetch at a time - will retry after the pending fetch is finished
-   (when-not (::fetching-todo-applications? db)
-     (fetch "/api/applications/todo"
-            {:url-params (when query
-                           {:query query})
-             :handler #(rf/dispatch [::fetch-todo-applications-result % query])
-             ;; TODO: show error message
-             :error-handler #(rf/dispatch [::fetch-todo-applications-result nil query])}))
-   {:db (assoc db
-               ::todo-applications-query query
-               ::fetching-todo-applications? true)}))
+ (fn [{:keys [db]} _]
+   (fetch "/api/applications/todo"
+          {:handler #(rf/dispatch [::fetch-todo-applications-result %])})
+   {:db (assoc db ::loading-todo-applications? true)}))
 
 (rf/reg-event-db
  ::fetch-todo-applications-result
- (fn [db [_ result query]]
-   ;; fetch again if the query that just finished was not the latest
-   (when-not (= query (::todo-applications-query db))
-     (rf/dispatch [::fetch-todo-applications (::todo-applications-query db)]))
+ (fn [db [_ result]]
    (-> db
        (assoc ::todo-applications result)
-       (dissoc ::initializing-todo-applications?
-               ::fetching-todo-applications?))))
+       (dissoc ::loading-todo-applications?))))
 
 (rf/reg-sub
  ::todo-applications
@@ -56,43 +38,25 @@
    (::todo-applications db)))
 
 (rf/reg-sub
- ::initializing-todo-applications?
- (fn [db _]
-   (::initializing-todo-applications? db)))
-
-(rf/reg-sub
- ::searching-todo-applications?
+ ::loading-todo-applications?
  (fn [db _]
-   (and (::fetching-todo-applications? db)
-        (not (::initializing-todo-applications? db)))))
+   (::loading-todo-applications? db)))
 
 ;;;; handled applications
 
 (rf/reg-event-fx
  ::fetch-handled-applications
- (fn [{:keys [db]} [_ query]]
-   ;; do only one fetch at a time - will retry after the pending fetch is finished
-   (when-not (::fetching-handled-applications? db)
-     (fetch "/api/applications/handled"
-            {:url-params (when query
-                           {:query query})
-             :handler #(rf/dispatch [::fetch-handled-applications-result % query])
-             ;; TODO: show error message
-             :error-handler #(rf/dispatch [::fetch-handled-applications-result nil query])}))
-   {:db (assoc db
-               ::handled-applications-query query
-               ::fetching-handled-applications? true)}))
+ (fn [{:keys [db]} _]
+   (fetch "/api/applications/handled"
+          {:handler #(rf/dispatch [::fetch-handled-applications-result %])})
+   {:db (assoc db ::loading-handled-applications? true)}))
 
 (rf/reg-event-db
  ::fetch-handled-applications-result
- (fn [db [_ result query]]
-   ;; fetch again if the query that just finished was not the latest
-   (when-not (= query (::handled-applications-query db))
-     (rf/dispatch [::fetch-handled-applications (::handled-applications-query db)]))
+ (fn [db [_ result]]
    (-> db
        (assoc ::handled-applications result)
-       (dissoc ::initializing-handled-applications?
-               ::fetching-handled-applications?))))
+       (dissoc ::loading-handled-applications?))))
 
 (rf/reg-sub
  ::handled-applications
@@ -100,15 +64,9 @@
    (::handled-applications db)))
 
 (rf/reg-sub
- ::initializing-handled-applications?
- (fn [db _]
-   (::initializing-handled-applications? db)))
-
-(rf/reg-sub
- ::searching-handled-applications?
+ ::loading-handled-applications?
  (fn [db _]
-   (and (::fetching-handled-applications? db)
-        (not (::initializing-handled-applications? db)))))
+   (::loading-handled-applications? db)))
 
 ;;;; UI
 
@@ -148,14 +106,8 @@
 
 (defn- todo-applications []
   (let [applications ::todo-applications]
-    (cond
-      @(rf/subscribe [::initializing-todo-applications?])
-      [spinner/big]
-
-      (empty? @(rf/subscribe [applications]))
+    (if (empty? @(rf/subscribe [applications]))
       [:div.actions.alert.alert-success (text :t.actions/empty)]
-
-      :else
       [application-list/component
        (-> (application-list-defaults)
            (assoc :id applications
@@ -164,7 +116,7 @@
 (defn- handled-applications []
   (let [applications ::handled-applications]
     (cond
-      @(rf/subscribe [::initializing-handled-applications?])
+      @(rf/subscribe [::loading-handled-applications?])
       [spinner/big]
 
       (empty? @(rf/subscribe [applications]))
@@ -177,64 +129,19 @@
            (assoc :id applications
                   :applications applications))])))
 
-(defn- search-field [{:keys [id on-search searching?]}]
-  (let [input-value (r/atom "")
-        input-element (atom nil)]
-    ;; TODO: localization & aria-labels
-    (fn [{:keys [id on-search searching?]}]
-      [:div.search-field.mb-3
-       [:label.mr-1 {:for id}
-        "Search"]
-
-       [:div.input-group.mr-2
-        [:input.form-control
-         {:id id
-          :type :text
-          :value @input-value
-          :ref (fn [element]
-                 (reset! input-element element))
-          :on-change (fn [event]
-                       (let [value (-> event .-target .-value)]
-                         (reset! input-value value)
-                         (on-search value)))}]
-
-        (when-not (= "" @input-value)
-          [:div.input-group-append
-           [:button.btn.btn-outline-secondary
-            {:id (str id "-clear")
-             :type :button
-             ;; override the custom font-size from .btn which breaks .input-group
-             :style {:font-size "inherit"}
-             :on-click (fn []
-                         (reset! input-value "")
-                         (on-search "")
-                         (.focus @input-element))}
-            [close-symbol]]])]
-
-       (when searching?
-         [spinner/small])])))
-
 (defn actions-page []
   [:div
    [document-title (text :t.navigation/actions)]
-   [:div.spaced-sections
-    [collapsible/component
-     {:id "todo-applications"
-      :open? true
-      :title (text :t.actions/todo-applications)
-      :collapse [:<>
-                 (when (config/dev-environment?) ; TODO: remove feature flag
-                   [search-field {:id "todo-search"
-                                  :on-search #(rf/dispatch [::fetch-todo-applications %])
-                                  :searching? @(rf/subscribe [::searching-todo-applications?])}])
-                 [todo-applications]]}]
-    [collapsible/component
-     {:id "handled-applications"
-      :on-open #(rf/dispatch [::fetch-handled-applications])
-      :title (text :t.actions/handled-applications)
-      :collapse [:<>
-                 (when (config/dev-environment?) ; TODO: remove feature flag
-                   [search-field {:id "handled-search"
-                                  :on-search #(rf/dispatch [::fetch-handled-applications %])
-                                  :searching? @(rf/subscribe [::searching-handled-applications?])}])
-                 [handled-applications]]}]]])
+   (if @(rf/subscribe [::loading-todo-applications?])
+     [spinner/big]
+     [:div.spaced-sections
+      [collapsible/component
+       {:id "todo-applications"
+        :open? true
+        :title (text :t.actions/todo-applications)
+        :collapse [todo-applications]}]
+      [collapsible/component
+       {:id "handled-applications"
+        :on-open #(rf/dispatch [::fetch-handled-applications])
+        :title (text :t.actions/handled-applications)
+        :collapse [handled-applications]}]])])


Test patch:
diff --git a/src/clj/rems/db/test_data.clj b/src/clj/rems/db/test_data.clj
index 1720236d4..d8dd32949 100644
--- a/src/clj/rems/db/test_data.clj
+++ b/src/clj/rems/db/test_data.clj
@@ -1,11 +1,11 @@
 (ns rems.db.test-data
   "Populating the database with nice test data."
   (:require [clj-time.core :as time]
+            [rems.api.services.licenses :as licenses]
             [rems.db.applications :as applications]
             [rems.db.catalogue :as catalogue]
             [rems.db.core :as db]
             [rems.db.form :as form]
-            [rems.db.licenses :as licenses]
             [rems.db.resource :as resource]
             [rems.db.roles :as roles]
             [rems.db.users :as users]
@@ -178,7 +178,7 @@
     (doseq [role roles]
       (roles/add-role! user role))))
 
-(defn create-users-and-roles! []
+(defn- create-users-and-roles! []
   ;; users provided by the fake login
   (let [users (comp +fake-user-data+ +fake-users+)]
     (create-user! (users :applicant1))
diff --git a/test-config.edn b/test-config.edn
index 5edd69d0d..6d6fae1e9 100644
--- a/test-config.edn
+++ b/test-config.edn
@@ -4,5 +4,4 @@
  :public-url "http://localhost:3001/"
  :database-url "postgresql://localhost/rems_test?user=rems_test"
  :test-database-url "postgresql://localhost/rems_test?user=rems_test"
- :search-index-path "target/search-index-test"
  :authentication :fake-shibboleth}
diff --git a/test/clj/rems/api/test_applications.clj b/test/clj/rems/api/test_applications.clj
index 184ca3735..e53229b0a 100644
--- a/test/clj/rems/api/test_applications.clj
+++ b/test/clj/rems/api/test_applications.clj
@@ -29,14 +29,14 @@
 (defn- catalogue-item-ids-for-application [application]
   (set (map :catalogue-item/id (:application/resources application))))
 
-(defn- get-my-applications [user-id & [params]]
-  (-> (request :get "/api/my-applications" params)
+(defn- get-my-applications [user-id]
+  (-> (request :get "/api/my-applications")
       (authenticate "42" user-id)
       handler
       read-ok-body))
 
-(defn- get-all-applications [user-id & [params]]
-  (-> (request :get "/api/applications" params)
+(defn- get-all-applications [user-id]
+  (-> (request :get "/api/applications")
       (authenticate "42" user-id)
       handler
       read-ok-body))
@@ -47,14 +47,14 @@
       handler
       read-ok-body))
 
-(defn- get-todos [user-id & [params]]
-  (-> (request :get "/api/applications/todo" params)
+(defn- get-todos [user-id]
+  (-> (request :get "/api/applications/todo")
       (authenticate "42" user-id)
       handler
       read-ok-body))
 
-(defn- get-handled-todos [user-id & [params]]
-  (-> (request :get "/api/applications/handled" params)
+(defn- get-handled-todos [user-id]
+  (-> (request :get "/api/applications/handled")
       (authenticate "42" user-id)
       handler
       read-ok-body))
@@ -127,7 +127,6 @@
                        handler
                        assert-response-is-ok)]
       (is (= "application/pdf" (get-in response [:headers "Content-Type"])))
-      (is (= "filename=\"13.pdf\"" (get-in response [:headers "Content-Disposition"])))
       (is (.startsWith (slurp (:body response)) "%PDF-1.")))))
 
 (deftest test-application-commands
@@ -623,19 +622,9 @@
       (is (contains? (get-ids (get-my-applications "alice"))
                      app-id)))
 
-    (testing "search user applications"
-      (is (contains? (get-ids (get-my-applications "alice" {:query "applicant:alice"}))
-                     app-id))
-      (is (empty? (get-ids (get-my-applications "alice" {:query "applicant:no-such-user"})))))
-
     (testing "list all applications"
       (is (contains? (get-ids (get-all-applications "alice"))
-                     app-id)))
-
-    (testing "search all applications"
-      (is (contains? (get-ids (get-all-applications "alice" {:query "applicant:alice"}))
-                     app-id))
-      (is (empty? (get-ids (get-all-applications "alice" {:query "applicant:no-such-user"})))))))
+                     app-id)))))
 
 (deftest test-todos
   (let [app-id (test-data/create-application! {:actor "alice"})]
@@ -652,11 +641,6 @@
       (is (not (contains? (get-ids (get-handled-todos "developer"))
                           app-id))))
 
-    (testing "search todos"
-      (is (contains? (get-ids (get-todos "developer" {:query "applicant:alice"}))
-                     app-id))
-      (is (empty? (get-ids (get-todos "developer" {:query "applicant:no-such-user"})))))
-
     (testing "commenter sees application in todos"
       (is (= {:success true} (send-command "developer" {:type :application.command/request-comment
                                                         :application-id app-id
@@ -676,11 +660,6 @@
       (is (contains? (get-ids (get-handled-todos "developer"))
                      app-id)))
 
-    (testing "search handled todos"
-      (is (contains? (get-ids (get-handled-todos "developer" {:query "applicant:alice"}))
-                     app-id))
-      (is (empty? (get-ids (get-handled-todos "developer" {:query "applicant:no-such-user"})))))
-
     (testing "commenter doesn't see accepted application in todos"
       (is (not (contains? (get-ids (get-todos "bob"))
                           app-id)))
diff --git a/test/clj/rems/api/testing.clj b/test/clj/rems/api/testing.clj
index c59e8b381..22fc99b81 100644
--- a/test/clj/rems/api/testing.clj
+++ b/test/clj/rems/api/testing.clj
@@ -3,7 +3,7 @@
   (:require [clojure.test :refer :all]
             [cheshire.core :refer [parse-stream]]
             [mount.core :as mount]
-            [rems.db.testing :refer [test-data-fixture test-db-fixture caches-fixture search-index-fixture]]
+            [rems.db.testing :refer [test-data-fixture test-db-fixture caches-fixture]]
             [rems.handler :refer :all]
             [ring.mock.request :refer :all]))
 
@@ -17,7 +17,6 @@
 
 (def api-fixture
   (join-fixtures [test-db-fixture
-                  search-index-fixture
                   caches-fixture
                   test-data-fixture
                   handler-fixture]))
diff --git a/test/clj/rems/application/test_search.clj b/test/clj/rems/application/test_search.clj
deleted file mode 100644
index 4b9757ba8..000000000
--- a/test/clj/rems/application/test_search.clj
+++ /dev/null
@@ -1,104 +0,0 @@
-(ns ^:integration rems.application.test-search
-  (:require [clojure.test :refer :all]
-            [rems.application.search :as search]
-            [rems.db.applications :as applications]
-            [rems.db.test-data :as test-data]
-            [rems.db.testing :refer [test-db-fixture search-index-fixture]]))
-
-(use-fixtures :once (join-fixtures [test-db-fixture
-                                    search-index-fixture]))
-
-(deftest test-application-search
-  ;; generate users with full names and emails
-  (test-data/create-users-and-roles!)
-  ;; unrelated application - it's an error if any of the tests finds this
-  (test-data/create-application! {:actor "developer"})
-
-  (testing "find by applicant"
-    (let [app-id (test-data/create-application! {:actor "alice"})]
-      (is (= #{app-id} (search/find-applications "alice")) "user ID, any field")
-      (is (= #{app-id} (search/find-applications "applicant:alice")) "user ID")
-      (is (= #{app-id} (search/find-applications "applicant:\"Alice Applicant\"")) "name")
-      (is (= #{app-id} (search/find-applications "applicant:\"alice@example.com\"")) "email")))
-
-  (testing "find by member"
-    (let [app-id (test-data/create-application! {:actor "alice"})]
-      (test-data/command! {:type :application.command/submit
-                           :application-id app-id
-                           :actor "alice"})
-      (test-data/command! {:type :application.command/add-member
-                           :application-id app-id
-                           :actor "developer"
-                           :member {:userid "bob"}})
-      (is (= #{app-id} (search/find-applications "bob")) "user ID, any field")
-      (is (= #{app-id} (search/find-applications "member:bob")) "user ID")
-      (is (= #{app-id} (search/find-applications "member:\"Bob Approver\"")) "name")
-      (is (= #{app-id} (search/find-applications "member:\"bob@example.com\"")) "email")))
-
-  (testing "find by ID"
-    (let [app-id (test-data/create-application! {:actor "alice"})
-          app (applications/get-unrestricted-application app-id)]
-      (is (= #{app-id} (search/find-applications (str app-id))) "app ID, any field")
-      (is (= #{app-id} (search/find-applications (str "id:" app-id))) "app ID")
-      (is (= #{app-id} (search/find-applications (str "id:\"" (:application/external-id app) "\""))) "external ID")))
-
-  (testing "find by title"
-    (let [form-id (test-data/create-form! {:form/fields [{:field/type :description
-                                                          :field/title {:en "Title"}
-                                                          :field/optional false}]})
-          cat-id (test-data/create-catalogue-item! {:form-id form-id})
-          app-id (test-data/create-application! {:catalogue-item-ids [cat-id]
-                                                 :actor "alice"})]
-      (test-data/command! {:type :application.command/save-draft
-                           :application-id app-id
-                           :actor "alice"
-                           :field-values [{:field 1
-                                           :value "Supercalifragilisticexpialidocious"}]})
-      (test-data/command! {:type :application.command/submit ; make sure that the required field was filled in
-                           :application-id app-id
-                           :actor "alice"})
-      (is (= #{app-id} (search/find-applications "Supercalifragilisticexpialidocious")) "any field")
-      (is (= #{app-id} (search/find-applications "title:Supercalifragilisticexpialidocious")) "title field")))
-
-  (testing "find by resource"
-    (let [cat-id (test-data/create-catalogue-item! {:title {:en "Spam"
-                                                            :fi "Nötkötti"}})
-          app-id (test-data/create-application! {:catalogue-item-ids [cat-id]
-                                                 :actor "alice"})]
-      (is (= #{app-id} (search/find-applications "Spam")) "en title, any field")
-      (is (= #{app-id} (search/find-applications "resource:Spam")) "en title")
-      (is (= #{app-id} (search/find-applications "resource:Nötkötti")) "fi title")))
-
-  (testing "find by state"
-    (let [app-id (test-data/create-application! {:actor "alice"})]
-      (test-data/command! {:type :application.command/submit
-                           :application-id app-id
-                           :actor "alice"})
-      (test-data/command! {:type :application.command/approve
-                           :application-id app-id
-                           :actor "developer"
-                           :comment ""})
-      (is (= #{app-id} (search/find-applications "Approved")) "en status, any field")
-      (is (= #{app-id} (search/find-applications "state:Approved")) "en status")
-      (is (= #{app-id} (search/find-applications "state:Hyväksytty")) "fi status")))
-
-  (testing "find by form content"
-    (let [form-id (test-data/create-form! {:form/fields [{:field/type :text
-                                                          :field/title {:en "Text field"}
-                                                          :field/optional false}]})
-          cat-id (test-data/create-catalogue-item! {:form-id form-id})
-          app-id (test-data/create-application! {:catalogue-item-ids [cat-id]
-                                                 :actor "alice"})]
-      (test-data/command! {:type :application.command/save-draft
-                           :application-id app-id
-                           :actor "alice"
-                           :field-values [{:field 1
-                                           :value "Tis but a scratch."}]})
-      (test-data/command! {:type :application.command/submit ; make sure that the required field was filled in
-                           :application-id app-id
-                           :actor "alice"})
-      (is (= #{app-id} (search/find-applications "scratch")) "any field")
-      (is (= #{app-id} (search/find-applications "form:scratch")) "form field")))
-
-  (testing "invalid query"
-    (is (= nil (search/find-applications "+")))))
diff --git a/test/clj/rems/db/testing.clj b/test/clj/rems/db/testing.clj
index 79413e851..cafbd1df8 100644
--- a/test/clj/rems/db/testing.clj
+++ b/test/clj/rems/db/testing.clj
@@ -5,12 +5,10 @@
             [conman.core :as conman]
             [luminus-migrations.core :as migrations]
             [mount.core :as mount]
-            [rems.application.search]
             [rems.config :refer [env]]
             [rems.db.applications]
             [rems.db.core :as db]
-            [rems.db.test-data :as test-data]
-            [rems.locales])
+            [rems.db.test-data :as test-data])
   (:import [org.joda.time Duration ReadableInstant]))
 
 (defn test-db-fixture [f]
@@ -23,12 +21,6 @@
   (f)
   (mount/stop))
 
-(defn search-index-fixture [f]
-  ;; no specific teardown. relies on the teardown of test-db-fixture.
-  (mount/start #'rems.application.search/search-index
-               #'rems.locales/translations)
-  (f))
-
 (defn caches-fixture [f]
   ;; no specific teardown. relies on the teardown of test-db-fixture.
   (mount/start #'rems.db.applications/application-cache