Skip to content
This repository has been archived by the owner on Dec 30, 2022. It is now read-only.

feat(search-client): Add support for Custom Search Clients #1216

Merged
merged 15 commits into from
May 17, 2018
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions packages/react-instantsearch/src/core/InstantSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function validateNextProps(props, nextProps) {
* @propType {string} indexName - Main index in which to search.
* @propType {boolean} [refresh=false] - Flag to activate when the cache needs to be cleared so that the front-end is updated when a change occurs in the index.
* @propType {object} [algoliaClient] - Provide a custom Algolia client instead of the internal one.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this prop from the documentation since we deprecate the option. What is the strategy in the others flavours?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe description can have “deprecated” in it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, we deprecate algoliaClient before removing it in the next major version. I'll add a mention.

* @propType {object} [searchClient] - Provide a custom search client.
* @propType {func} [onSearchStateChange] - Function to be called everytime a new search is done. Useful for [URL Routing](guide/Routing.html).
* @propType {object} [searchState] - Object to inject some search state. Switches the InstantSearch component in controlled mode. Useful for [URL Routing](guide/Routing.html).
* @propType {func} [createURL] - Function to call when creating links, useful for [URL Routing](guide/Routing.html).
Expand Down Expand Up @@ -59,7 +60,7 @@ class InstantSearch extends Component {

this.aisManager = createInstantSearchManager({
indexName: props.indexName,
algoliaClient: props.algoliaClient,
searchClient: props.searchClient || props.algoliaClient,
initialState,
resultsState: props.resultsState,
stalledSearchDelay: props.stalledSearchDelay,
Expand All @@ -83,6 +84,10 @@ class InstantSearch extends Component {
this.aisManager.updateClient(nextProps.algoliaClient);
}

if (this.props.searchClient !== nextProps.searchClient) {
this.aisManager.updateClient(nextProps.searchClient);
}

if (this.isControlled) {
this.aisManager.onExternalStateUpdate(nextProps.searchState);
}
Expand Down Expand Up @@ -177,7 +182,9 @@ InstantSearch.propTypes = {
// @TODO: These props are currently constant.
indexName: PropTypes.string.isRequired,

algoliaClient: PropTypes.object.isRequired,
algoliaClient: PropTypes.object,

searchClient: PropTypes.object.isRequired,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to duplicate the props, we already do the logic to chose the correct implementation on the previous layer. But we can still rename it to searchClient instead of algoliaClient.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We somehow need to keep algoliaClient since we deprecate it until the next major version, right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but this component is not part of the public API. The part that is exposed is createInstantSearch, it's required to keep both algoliaClient & searchClient in this component but not in InstantSearch.

const InstantSearch = createInstantSearch(algoliasearch, {
Root: 'div',
props: { className: 'ais-InstantSearch__root' },
});
export { InstantSearch };


createURL: PropTypes.func,

Expand Down
6 changes: 3 additions & 3 deletions packages/react-instantsearch/src/core/InstantSearch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const DEFAULT_PROPS = {
appId: 'foo',
apiKey: 'bar',
indexName: 'foobar',
algoliaClient: {},
searchClient: {},
root: {
Root: 'div',
},
Expand Down Expand Up @@ -114,7 +114,7 @@ describe('InstantSearch', () => {
expect(createInstantSearchManager.mock.calls[0][0]).toEqual({
indexName: DEFAULT_PROPS.indexName,
initialState: {},
algoliaClient: {},
searchClient: {},
stalledSearchDelay: 200,
});
});
Expand All @@ -135,7 +135,7 @@ describe('InstantSearch', () => {
expect(ism.updateClient.mock.calls).toHaveLength(0);
wrapper.setProps({
...DEFAULT_PROPS,
algoliaClient: {},
searchClient: {},
});

expect(ism.updateClient.mock.calls).toHaveLength(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ Object {
},
},
},
"searchClient": Object {
"addAlgoliaAgent": [MockFunction] {
"calls": Array [
Array [
"react-instantsearch 5.0.3",
],
],
},
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the previous implementation we don't pass the alogliaClient to the Snapshot, otherwise every time we release a new version we need to update them. We need to do the same with searchClient

it('wraps InstantSearch', () => {
const wrapper = shallow(
<CustomInstantSearch appId="app" apiKey="key" indexName="name" />
);
// eslint-disable-next-line no-shadow
const { algoliaClient, ...propsWithoutClient } = wrapper.props();
expect(wrapper.is(InstantSearch)).toBe(true);
expect(propsWithoutClient).toMatchSnapshot();
expect(wrapper.props().algoliaClient).toBe(algoliaClient);
});

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get it!

"searchState": undefined,
"stalledSearchDelay": 200,
}
Expand All @@ -34,6 +43,15 @@ Object {
"root": Object {
"Root": "div",
},
"searchClient": Object {
"addAlgoliaAgent": [MockFunction] {
"calls": Array [
Array [
"react-instantsearch 5.0.3",
],
],
},
},
"searchState": undefined,
"stalledSearchDelay": 200,
}
Expand Down
33 changes: 30 additions & 3 deletions packages/react-instantsearch/src/core/createInstantSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default function createInstantSearch(defaultAlgoliaClient, root) {
return class CreateInstantSearch extends Component {
static propTypes = {
algoliaClient: PropTypes.object,
searchClient: PropTypes.object,
appId: PropTypes.string,
apiKey: PropTypes.string,
children: PropTypes.oneOfType([
Expand Down Expand Up @@ -42,24 +43,49 @@ export default function createInstantSearch(defaultAlgoliaClient, root) {
constructor(...args) {
super(...args);

if (this.props.searchClient) {
if (this.props.appId || this.props.apiKey || this.props.algoliaClient) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can cover those cases with tests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I messed up with my rebase. Will add it back!

// eslint-disable-next-line no-console
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this comment, we don't use the console.

throw new Error(
'react-instantsearch:: `searchClient` cannot be used with `appId`, `apiKey` or `algoliaClient`.'
);
}
}

if (this.props.algoliaClient) {
// eslint-disable-next-line no-console
console.warn(
'`algoliaClient` option was renamed `searchClient`. Please use this new option before the next major version.'
);
}

this.client =
this.props.searchClient ||
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can cover this case with a test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

this.props.algoliaClient ||
defaultAlgoliaClient(this.props.appId, this.props.apiKey);

this.client.addAlgoliaAgent(`react-instantsearch ${version}`);
if (typeof this.client.addAlgoliaAgent === 'function') {
this.client.addAlgoliaAgent(`react-instantsearch ${version}`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can cover this case with a test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
}

componentWillReceiveProps(nextProps) {
const props = this.props;
if (nextProps.algoliaClient) {

if (nextProps.searchClient) {
this.client = nextProps.searchClient;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can cover this case with a test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! (I messed up with my rebase)

} else if (nextProps.algoliaClient) {
this.client = nextProps.algoliaClient;
} else if (
props.appId !== nextProps.appId ||
props.apiKey !== nextProps.apiKey
) {
this.client = defaultAlgoliaClient(nextProps.appId, nextProps.apiKey);
}
this.client.addAlgoliaAgent(`react-instantsearch ${version}`);

if (typeof this.client.addAlgoliaAgent === 'function') {
this.client.addAlgoliaAgent(`react-instantsearch ${version}`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can cover this case with a test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
}

render() {
Expand All @@ -71,6 +97,7 @@ export default function createInstantSearch(defaultAlgoliaClient, root) {
onSearchStateChange={this.props.onSearchStateChange}
onSearchParameters={this.props.onSearchParameters}
root={this.props.root}
searchClient={this.client}
algoliaClient={this.client}
refresh={this.props.refresh}
resultsState={this.props.resultsState}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('createInstantSearchManager', () => {
indexName: 'first',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

ism.widgetsManager.registerWidget({
Expand Down Expand Up @@ -122,7 +122,7 @@ describe('createInstantSearchManager', () => {
indexName: 'first',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

// <SearchBox defaultRefinement="query" />
Expand Down Expand Up @@ -190,7 +190,7 @@ describe('createInstantSearchManager', () => {
indexName: 'first',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

ism.widgetsManager.registerWidget({
Expand Down Expand Up @@ -241,7 +241,7 @@ describe('createInstantSearchManager', () => {
indexName: 'first',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

ism.widgetsManager.registerWidget({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('createInstantSearchManager errors', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

ism.widgetsManager.registerWidget({
Expand Down Expand Up @@ -70,7 +70,7 @@ describe('createInstantSearchManager errors', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

ism.onExternalStateUpdate({});
Expand All @@ -95,7 +95,7 @@ describe('createInstantSearchManager errors', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

ism.onSearchForFacetValues({ facetName: 'facetName', query: 'query' });
Expand All @@ -118,7 +118,7 @@ describe('createInstantSearchManager errors', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

ism.widgetsManager.registerWidget({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import highlightTags from './highlightTags.js';
export default function createInstantSearchManager({
indexName,
initialState = {},
algoliaClient,
searchClient,
resultsState,
stalledSearchDelay,
}) {
Expand All @@ -27,7 +27,7 @@ export default function createInstantSearchManager({

let stalledSearchTimer = null;

const helper = algoliasearchHelper(algoliaClient, indexName, baseSP);
const helper = algoliasearchHelper(searchClient, indexName, baseSP);
helper.on('result', handleSearchSuccess);
helper.on('error', handleSearchError);
helper.on('search', handleNewSearch);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ jest.mock('algoliasearch-helper', () => {
});

const client = algoliaClient('latency', '249078a3d4337a8231f1665ec5a44966');
client.addAlgoliaAgent = () => {};
client.search = jest.fn(() => Promise.resolve({ results: [{ hits: [] }] }));

describe('createInstantSearchManager', () => {
Expand All @@ -45,7 +44,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

ism.widgetsManager.registerWidget({
Expand Down Expand Up @@ -97,7 +96,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

ism.onExternalStateUpdate({});
Expand Down Expand Up @@ -145,7 +144,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

ism.onSearchForFacetValues({ facetName: 'facetName', query: 'query' });
Expand Down Expand Up @@ -191,7 +190,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client,
searchClient: client,
});

ism.onSearchForFacetValues({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: makeClient(defaultResponse()),
searchClient: makeClient(defaultResponse()),
});

const store = ism.store.getState();
Expand Down Expand Up @@ -55,7 +55,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: makeClient(defaultResponse()),
searchClient: makeClient(defaultResponse()),
resultsState: { some: 'results' },
});

Expand All @@ -79,7 +79,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: makeClient(defaultResponse()),
searchClient: makeClient(defaultResponse()),
});

ism.widgetsManager.registerWidget({
Expand All @@ -106,7 +106,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: makeClient(defaultResponse()),
searchClient: makeClient(defaultResponse()),
});

const nextSearchState = {};
Expand Down Expand Up @@ -136,7 +136,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: makeClient(defaultResponse()),
searchClient: makeClient(defaultResponse()),
});

const widgetIDsT0 = ism.getWidgetsIds().sort();
Expand Down Expand Up @@ -164,7 +164,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: managedClient,
searchClient: managedClient,
});

ism.widgetsManager.registerWidget({
Expand Down Expand Up @@ -237,7 +237,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client0,
searchClient: client0,
});

ism.widgetsManager.registerWidget({
Expand All @@ -260,7 +260,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client0,
searchClient: client0,
});

const client1 = makeClient(defaultResponse());
Expand All @@ -282,7 +282,7 @@ describe('createInstantSearchManager', () => {
indexName: 'index',
initialState: {},
searchParameters: {},
algoliaClient: client0,
searchClient: client0,
});

ism.skipSearch();
Expand All @@ -304,7 +304,6 @@ function makeClient(response) {
'latency',
'249078a3d4337a8231f1665ec5a44966'
);
clientInstance.addAlgoliaAgent = () => {};
clientInstance.search = jest.fn(() => {
const clonedResponse = JSON.parse(JSON.stringify(response));
return Promise.resolve(clonedResponse);
Expand All @@ -321,7 +320,6 @@ function makeManagedClient() {
searchResultsPromises.push(results);
return results;
}),
addAlgoliaAgent: () => {},
searchResultsPromises,
};

Expand Down
Loading