Search Users for Chat Channels Frontend (#6626)

* hook up user search for chat channels to Elasticsearch

* Add spaces

* run specs

* add more spaces

* reset travis file

* add another data upadte script to reindex Users
This commit is contained in:
Molly Struve 2020-03-13 10:26:32 -04:00 committed by GitHub
parent 244ab297c8
commit a4ce1625e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 37 additions and 71 deletions

View file

@ -1,42 +0,0 @@
global.document.head.innerHTML =
"<meta name='algolia-public-id' content='abc123' />" +
"<meta name='algolia-public-key' content='abc123' />" +
"<meta name='environment' content='test' />";
const mockIndex = {
search: query =>
new Promise(resolve => {
process.nextTick(() => {
const searchResults = {
ma: {
hits: [
{
name: 'mat',
path: 'some_path',
title: 'some_title',
id: 'some_id',
},
],
nbHits: 1,
page: 0,
nbPages: 1,
hitsPerPage: 10,
processingTimeMS: 1,
exhaustiveNbHits: true,
query: 'ma',
params:
'query=ma&hitsPerPage=10&filters=supported%3Atrue&restrictIndices=searchables_development%2CTag_development%2Cordered_articles_development%2Cordered_articles_by_published_at_development%2Cordered_articles_by_positive_reactions_count_development',
},
};
const results = searchResults[query] || { hits: [] };
resolve(results);
});
}),
};
const client = {
initIndex: _index => mockIndex, // eslint-ignore-line
};
export default jest.fn().mockImplementation((_id_, _key) => client); // eslint-ignore-line

View file

@ -158,7 +158,7 @@ preact-render-spy (1 nodes)
<div class="channeldetails__inviteusers">
<h2>Invite Members</h2>
<input
onKeyUp={[Function value]}
onKeyUp={[Function debounced]}
placeholder="Find users"
/>
<div class="channeldetails__searchresults">

View file

@ -4,13 +4,11 @@ import { shallow } from 'preact-render-spy';
import { JSDOM } from 'jsdom';
import fetch from 'jest-fetch-mock';
import ChannelDetails from '../channelDetails';
import algoliasearch from '../__mocks__/algoliasearchUsers';
global.fetch = fetch;
const doc = new JSDOM('<!doctype html><html><body></body></html>');
global.document = doc;
global.window = doc.defaultView;
global.window.algoliasearch = algoliasearch;
global.window.currentUser = { id: 'modID' };
const channelDetails = mod => {
@ -106,9 +104,7 @@ describe('<ChannelDetails />', () => {
.at(i)
.childAt(1)
.attr('data-content'),
).toEqual(
`sidecar-user`,
);
).toEqual(`sidecar-user`);
}
// mod only divs
@ -292,9 +288,7 @@ describe('<ChannelDetails />', () => {
.at(i)
.childAt(1)
.attr('data-content'),
).toEqual(
`sidecar-user`,
);
).toEqual(`sidecar-user`);
}
// user only divs

View file

@ -1,5 +1,6 @@
import { h, Component } from 'preact';
import PropTypes from 'prop-types';
import debounceAction from '../src/utils/debounceAction';
class ChannelDetails extends Component {
static propTypes = {
@ -9,34 +10,36 @@ class ChannelDetails extends Component {
constructor(props) {
super(props);
this.debouncedUserSearch = debounceAction(
this.triggerUserSearch.bind(this),
{ config: { leading: true } },
);
this.state = {
searchedUsers: [],
// invitations: [],
hasLeftChannel: false,
};
const algoliaId = document.querySelector("meta[name='algolia-public-id']")
.content;
const algoliaKey = document.querySelector("meta[name='algolia-public-key']")
.content;
const env = document.querySelector("meta[name='environment']").content;
const client = algoliasearch(algoliaId, algoliaKey); // eslint-disable-line no-undef
this.index = client.initIndex(`searchables_${env}`);
}
triggerUserSearch = e => {
const component = this;
const query = e.target.value;
const filters = {
hitsPerPage: 20,
attributesToRetrieve: ['id', 'title', 'path', 'user'],
attributesToHighlight: [],
filters: 'class_name:User',
};
if (query.length > 0) {
this.index.search(query, filters).then(content => {
component.setState({ searchedUsers: content.hits });
});
const searchHash = { per_page: 20, search_fields: query };
const searchParams = new URLSearchParams(searchHash).toString();
fetch(`/search/users?${searchParams}`, {
method: 'GET',
headers: {
Accept: 'application/json',
'X-CSRF-Token': window.csrfToken,
'Content-Type': 'application/json',
},
credentials: 'same-origin',
})
.then(response => response.json())
.then(response => {
component.setState({ searchedUsers: response.result });
});
} else {
component.setState({ searchedUsers: [] });
}
@ -189,7 +192,7 @@ class ChannelDetails extends Component {
modSection = (
<div className="channeldetails__inviteusers">
<h2>Invite Members</h2>
<input onKeyUp={this.triggerUserSearch} placeholder="Find users" />
<input onKeyUp={this.debouncedUserSearch} placeholder="Find users" />
<div className="channeldetails__searchresults">{searchedUsers}</div>
<h2>Pending Invites:</h2>
{pendingInvites}

View file

@ -0,0 +1,11 @@
module DataUpdateScripts
class IndexUsersToElasticsearch
def run
User.select(:id).find_each do |user|
Search::IndexToElasticsearchWorker.set(queue: :low_priority).perform_async(
"User", user.id
)
end
end
end
end

View file

@ -1,5 +1,5 @@
require "rails_helper"
require Rails.root.join("lib/data_update_scripts/20200305201627_index_users_to_elasticsearch.rb")
require Rails.root.join("lib/data_update_scripts/20200313123108_index_users_to_elasticsearch.rb")
describe DataUpdateScripts::IndexUsersToElasticsearch, elasticsearch: true do
it "indexes users to Elasticsearch" do