Skip to content

Commit

Permalink
keep results on search failure (#1747)
Browse files Browse the repository at this point in the history
 Rendering failures leading to the catch statements in Universal and Vertical search would clear all results. User would still expects other results from the response to render on page. This pr updates the `storage.set` calls to only update the searchState, instead of storing a new empty results, so the non-problematic result sets can still be render.
 
The catch statements in core.js will terminate any further rendering of sub components once it encounter a problematic sub component. User expects sdk to render as much as it can. To ensure non-problematic sub-components can still render, this pr added more catch statements in component.js to gracefully handle errors coming from individual child component's data transformation, initialization and mount stage.

J=none
TEST=manual

use `dev-keep-data-on-search-failure` for SDK version ([from this test branch](#1745)) in the config file of the site with the issue. Searched 'Listings', and see that the same error appeared in console but the other cards still render as normal.
use `dev-dont-clear-data-on-failure` to test changes in this pr through local html pages that use SDK directly. See that errors are logged but the loading icon is reset to complete during error handling process
  • Loading branch information
yen-tt committed May 17, 2022
1 parent 5aa0fd7 commit 50ff2ab
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 20 deletions.
53 changes: 43 additions & 10 deletions src/core/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,11 +321,13 @@ export default class Core {
}
this.updateHistoryAfterSearch(queryTrigger);
window.performance.mark('yext.answers.verticalQueryResponseRendered');
}).catch(error => {
console.error('Vertical search failed with the following error: ' + error);
this.storage.set(StorageKeys.VERTICAL_RESULTS, new VerticalResults());
this.storage.set(StorageKeys.DIRECT_ANSWER, new DirectAnswer());
this.storage.set(StorageKeys.LOCATION_BIAS, new LocationBias({}));
})
.catch(error => {
this._markSearchComplete(Searcher.VERTICAL);
throw error;
})
.catch(error => {
console.error('The following problem was encountered during vertical search: ' + error);
});
}

Expand Down Expand Up @@ -415,14 +417,45 @@ export default class Core {
}
this.updateHistoryAfterSearch(queryTrigger);
window.performance.mark('yext.answers.universalQueryResponseRendered');
}).catch(error => {
console.error('Universal search failed with the following error: ' + error);
this.storage.set(StorageKeys.UNIVERSAL_RESULTS, new UniversalResults({}));
this.storage.set(StorageKeys.DIRECT_ANSWER, new DirectAnswer());
this.storage.set(StorageKeys.LOCATION_BIAS, new LocationBias({}));
})
.catch(error => {
this._markSearchComplete(Searcher.UNIVERSAL);
throw error;
})
.catch(error => {
console.error('The following problem was encountered during universal search: ' + error);
});
}

/**
* Update the search state of the results in storage to SEARCH_COMPLETE
* when handling errors from universal and vertical search. This will
* trigger a rerender of the components, which could potentially throw
* a new error.
*
* @param {Searcher} searcherType
*/
_markSearchComplete (searcherType) {
const resultStorageKey = searcherType === Searcher.UNIVERSAL
? StorageKeys.UNIVERSAL_RESULTS
: StorageKeys.VERTICAL_RESULTS;
const results = this.storage.get(resultStorageKey);
if (results && results.searchState !== SearchStates.SEARCH_COMPLETE) {
results.searchState = SearchStates.SEARCH_COMPLETE;
this.storage.set(resultStorageKey, results);
}
const directanswer = this.storage.get(StorageKeys.DIRECT_ANSWER);
if (directanswer && directanswer.searchState !== SearchStates.SEARCH_COMPLETE) {
directanswer.searchState = SearchStates.SEARCH_COMPLETE;
this.storage.set(StorageKeys.DIRECT_ANSWER, directanswer);
}
const locationbias = this.storage.get(StorageKeys.LOCATION_BIAS);
if (locationbias && locationbias.searchState !== SearchStates.SEARCH_COMPLETE) {
locationbias.searchState = SearchStates.SEARCH_COMPLETE;
this.storage.set(StorageKeys.LOCATION_BIAS, locationbias);
}
}

/**
* Builds the object passed as a parameter to onUniversalSearch. This object
* contains information about the universal search's query and result counts.
Expand Down
1 change: 0 additions & 1 deletion src/core/models/directanswer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import SearchStates from '../storage/searchstates';
export default class DirectAnswer {
constructor (directAnswer = {}) {
Object.assign(this, { searchState: SearchStates.SEARCH_COMPLETE }, directAnswer);
Object.freeze(this);
}

/**
Expand Down
2 changes: 0 additions & 2 deletions src/core/models/verticalresults.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ export default class VerticalResults {
* @type {ResultsContext}
*/
this.resultsContext = data.resultsContext;

Object.freeze(this);
}

/**
Expand Down
32 changes: 25 additions & 7 deletions src/ui/components/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -376,20 +376,38 @@ export default class Component {
// Process the DOM to determine if we should create
// in-memory sub-components for rendering
const domComponents = DOM.queryAll(this._container, '[data-component]:not([data-is-component-mounted])');
const data = this.transformData
? this.transformData(cloneDeep(this._state.get()))
: this._state.get();
domComponents.forEach(c => this._createSubcomponent(c, data));

let data;
try {
data = this.transformData
? this.transformData(cloneDeep(this._state.get()))
: this._state.get();
} catch (e) {
console.error(`The following problem occurred while transforming data for sub-components of ${this.name}: `, e);
}
domComponents.forEach(c => {
try {
this._createSubcomponent(c, data);
} catch (e) {
console.error('The following problem occurred while initializing sub-component: ', c, e);
}
});
if (this._progressivelyRenderChildren) {
this._children.forEach(child => {
setTimeout(() => {
child.mount();
try {
child.mount();
} catch (e) {
console.error('The following problem occurred while mounting sub-component: ', child, e);
}
});
});
} else {
this._children.forEach(child => {
child.mount();
try {
child.mount();
} catch (e) {
console.error('The following problem occurred while mounting sub-component: ', child, e);
}
});
}

Expand Down

0 comments on commit 50ff2ab

Please sign in to comment.