DEV: Add plugin API to extend search results (#12966)

DEV: Add plugin API to extend search results (#12966)

diff --git a/app/assets/javascripts/admin/addon/routes/admin-search-logs-term.js b/app/assets/javascripts/admin/addon/routes/admin-search-logs-term.js
index b9613fe..9847f44 100644
--- a/app/assets/javascripts/admin/addon/routes/admin-search-logs-term.js
+++ b/app/assets/javascripts/admin/addon/routes/admin-search-logs-term.js
@@ -20,7 +20,7 @@ export default DiscourseRoute.extend({
         search_type: params.searchType,
         term: params.term,
       },
-    }).then((json) => {
+    }).then(async (json) => {
       // Add zero values for missing dates
       if (json.term.data.length > 0) {
         const startDate =
@@ -31,7 +31,9 @@ export default DiscourseRoute.extend({
         json.term.data = fillMissingDates(json.term.data, startDate, endDate);
       }
       if (json.term.search_result) {
-        json.term.search_result = translateResults(json.term.search_result);
+        json.term.search_result = await translateResults(
+          json.term.search_result
+        );
       }
 
       const model = EmberObject.create({ type: "search_log_term" });
diff --git a/app/assets/javascripts/discourse/app/controllers/full-page-search.js b/app/assets/javascripts/discourse/app/controllers/full-page-search.js
index a7ba222..4fdd54a 100644
--- a/app/assets/javascripts/discourse/app/controllers/full-page-search.js
+++ b/app/assets/javascripts/discourse/app/controllers/full-page-search.js
@@ -245,8 +245,8 @@ export default Controller.extend({
     const searchKey = getSearchKey(args);
 
     ajax("/search", { data: args })
-      .then((results) => {
-        const model = translateResults(results) || {};
+      .then(async (results) => {
+        const model = (await translateResults(results)) || {};
 
         if (results.grouped_search_result) {
           this.set("q", results.grouped_search_result.term);
diff --git a/app/assets/javascripts/discourse/app/lib/plugin-api.js b/app/assets/javascripts/discourse/app/lib/plugin-api.js
index 5f8a2ff..5c25a4a 100644
--- a/app/assets/javascripts/discourse/app/lib/plugin-api.js
+++ b/app/assets/javascripts/discourse/app/lib/plugin-api.js
@@ -72,9 +72,10 @@ import { registerTopicFooterButton } from "discourse/lib/register-topic-footer-b
 import { replaceFormatter } from "discourse/lib/utilities";
 import { replaceTagRenderer } from "discourse/lib/render-tag";
 import { setNewCategoryDefaultColors } from "discourse/routes/new-category";
+import { addSearchResultsCallback } from "discourse/lib/search";
 
 // If you add any methods to the API ensure you bump up this number
-const PLUGIN_API_VERSION = "0.11.4";
+const PLUGIN_API_VERSION = "0.11.5";
 
 class PluginApi {
   constructor(version, container) {
@@ -1281,6 +1282,21 @@ class PluginApi {
   setNewCategoryDefaultColors(backgroundColor, textColor) {
     setNewCategoryDefaultColors(backgroundColor, textColor);
   }
+
+  /**
+   * Add a callback to modify search results before displaying them.
+   *
+   * `‍``
+   * api.addSearchResultsCallback((results) => {
+   *   results.topics.push(Topic.create({ ... }));
+   *   return results;
+   * });
+   * `‍``
+   *
+   */
+  addSearchResultsCallback(callback) {
+    addSearchResultsCallback(callback);
+  }
 }
 
 // from http://stackoverflow.com/questions/6832596/how-to-compare-software-version-number-using-js-only-number
diff --git a/app/assets/javascripts/discourse/app/lib/search.js b/app/assets/javascripts/discourse/app/lib/search.js
index e5b2002..635b16f 100644
--- a/app/assets/javascripts/discourse/app/lib/search.js
+++ b/app/assets/javascripts/discourse/app/lib/search.js
@@ -1,6 +1,7 @@
 import Category from "discourse/models/category";
 import EmberObject from "@ember/object";
 import I18n from "I18n";
+import { Promise } from "rsvp";
 import Post from "discourse/models/post";
 import Topic from "discourse/models/topic";
 import User from "discourse/models/user";
@@ -15,6 +16,12 @@ import { search as searchCategoryTag } from "discourse/lib/category-tag-search";
 import { userPath } from "discourse/lib/url";
 import userSearch from "discourse/lib/user-search";
 
+const translateResultsCallbacks = [];
+
+export function addSearchResultsCallback(callback) {
+  translateResultsCallbacks.push(callback);
+}
+
 export function translateResults(results, opts) {
   opts = opts || {};
 
@@ -84,9 +91,29 @@ export function translateResults(results, opts) {
     })
     .compact();
 
-  results.resultTypes = [];
+  return translateResultsCallbacks
+    .reduce(
+      (promise, callback) => promise.then((r) => callback(r)),
+      Promise.resolve(results)
+    )
+    .then((results_) => {
+      translateGroupedSearchResults(results_, opts);
 
-  // TODO: consider refactoring front end to take a better structure
+      if (
+        !results_.topics.length &&
+        !results_.posts.length &&
+        !results_.users.length &&
+        !results_.categories.length
+      ) {
+        return null;
+      }
+
+      return EmberObject.create(results_);
+    });
+}
+
+function translateGroupedSearchResults(results, opts) {
+  results.resultTypes = [];
   const groupedSearchResult = results.grouped_search_result;
   if (groupedSearchResult) {
     [
@@ -121,15 +148,6 @@ export function translateResults(results, opts) {
       }
     });
   }
-
-  const noResults = !!(
-    !results.topics.length &&
-    !results.posts.length &&
-    !results.users.length &&
-    !results.categories.length
-  );
-
-  return noResults ? null : EmberObject.create(results);
 }
 
 export function searchForTerm(term, opts) {
@@ -157,12 +175,9 @@ export function searchForTerm(term, opts) {
     };
   }
 
-  let promise = ajax("/search/query", { data: data });
-
-  promise.then((results) => {
-    return translateResults(results, opts);
-  });
-
+  let ajaxPromise = ajax("/search/query", { data });
+  const promise = ajaxPromise.then((res) => translateResults(res, opts));
+  promise.abort = ajaxPromise.abort;
   return promise;
 }
 
diff --git a/app/assets/javascripts/discourse/app/routes/full-page-search.js b/app/assets/javascripts/discourse/app/routes/full-page-search.js
index 3c4fe84..71ccb8f 100644
--- a/app/assets/javascripts/discourse/app/routes/full-page-search.js
+++ b/app/assets/javascripts/discourse/app/routes/full-page-search.js
@@ -52,11 +52,11 @@ export default DiscourseRoute.extend({
       } else {
         return null;
       }
-    }).then((results) => {
+    }).then(async (results) => {
       const grouped_search_result = results
         ? results.grouped_search_result
         : {};
-      const model = (results && translateResults(results)) || {
+      const model = (results && (await translateResults(results))) || {
         grouped_search_result,
       };
       setTransient("lastSearch", { searchKey, model }, 5);
diff --git a/app/assets/javascripts/discourse/app/widgets/search-menu.js b/app/assets/javascripts/discourse/app/widgets/search-menu.js
index 28d8ce5..246aeb4 100644
--- a/app/assets/javascripts/discourse/app/widgets/search-menu.js
+++ b/app/assets/javascripts/discourse/app/widgets/search-menu.js
@@ -55,12 +55,12 @@ const SearchHelper = {
         fullSearchUrl,
       });
       this._activeSearch
-        .then((content) => {
+        .then((results) => {
           // we ensure the current search term is the one used
           // when starting the query
-          if (term === searchData.term) {
-            searchData.noResults = content.resultTypes.length === 0;
-            searchData.results = content;
+          if (results && term === searchData.term) {
+            searchData.noResults = results.resultTypes.length === 0;
+            searchData.results = results;
 
             if (searchContext && searchContext.type === "topic") {
               widget.appEvents.trigger("post-stream:refresh", { force: true });
diff --git a/app/assets/javascripts/discourse/tests/unit/lib/search-test.js b/app/assets/javascripts/discourse/tests/unit/lib/search-test.js
index 61f9876..af28d03 100644

[... diff too long, it was truncated ...]

GitHub sha: ff4fb9c77119ec7e6b2091078b4ff405b8e5a629

This commit appears in #12966 which was approved by ZogStriP. It was merged by SamSaffron.

This commit has been mentioned on Discourse Meta. There might be relevant details there: