-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PCH: Enhance Request Handling in Provider Classes (#2401)
* Create BaseProvider class and apply it to SmartLinkingProvider * Apply new BaseProvider to PerformanceStats provider * Apply new BaseProvider to RelatedPostsProvider * Apply new BaseProvider to TitleSuggestionsProvider * Apply new BaseProvider to DashboardWidgetProvider * Refactor BaseProvider to use non-static methods instead. * Clean-up abort controller after each request. * Remove testing code * Rebuild assets * Adjust some whitespace/comments * build(deps-dev): bump @testing-library/react from 15.0.2 to 15.0.4 Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 15.0.2 to 15.0.4. - [Release notes](https://github.com/testing-library/react-testing-library/releases) - [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md) - [Commits](testing-library/react-testing-library@v15.0.2...v15.0.4) --- updated-dependencies: - dependency-name: "@testing-library/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * PCH Editor Sidebar: Update isDismissible in Notices * TypeScript DocBlocks: Remove spaces in braces * Remove unnecessary empty line * Add JSDoc comment to `GetAbortControllerResult` * Adjust some whitespace/comments * Clean-up docblock merge mess * Remove extra spaces. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Alex Cicovic <23142906+acicovic@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- Loading branch information
1 parent
f5558be
commit 5b6b04f
Showing
17 changed files
with
377 additions
and
270 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => '58c71232f1e12c0b0925'); | ||
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => 'a9ac73db3b76299e492d'); |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins', 'wp-primitives', 'wp-url'), 'version' => '7cda9ce01084868d6502'); | ||
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins', 'wp-primitives', 'wp-url'), 'version' => 'a522b73f43053e03c73b'); |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
// eslint-disable-next-line import/named | ||
import apiFetch, { APIFetchOptions } from '@wordpress/api-fetch'; | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { ContentHelperError, ContentHelperErrorCode } from './content-helper-error'; | ||
|
||
/** | ||
* The response structure of the API. | ||
* | ||
* @since 3.15.0 | ||
*/ | ||
export interface ContentHelperAPIResponse<T> { | ||
error?: Error; | ||
data: T; | ||
} | ||
|
||
/** | ||
* The result of the getOrCreateController method. | ||
* | ||
* @since 3.15.0 | ||
*/ | ||
type GetAbortControllerResult = { | ||
abortController: AbortController; | ||
abortId: string; | ||
}; | ||
|
||
/** | ||
* Base class for all providers. | ||
* | ||
* Provides a common interface for fetching data from the API, with support | ||
* for cancelling requests. | ||
* | ||
* @since 3.15.0 | ||
*/ | ||
export abstract class BaseProvider { | ||
/** | ||
* A map of AbortControllers used to cancel fetch requests. | ||
* | ||
* @since 3.15.0 | ||
*/ | ||
private abortControllers: Map<string, AbortController> = new Map(); | ||
|
||
/** | ||
* Protected empty constructor to prevent instantiation. | ||
* | ||
* @since 3.15.0 | ||
*/ | ||
protected constructor() {} // eslint-disable-line no-useless-constructor | ||
|
||
/** | ||
* Cancels the fetch request. | ||
* | ||
* If an ID is provided, it cancels the request with that ID. | ||
* If no ID is provided, it cancels the most recent request. | ||
* | ||
* @since 3.15.0 | ||
* | ||
* @param {string?} id The (optional) ID of the request to cancel. | ||
*/ | ||
public cancelRequest( id?: string ): void { | ||
// If an ID is provided, cancel the request with that ID. | ||
if ( id ) { | ||
const controller = this.abortControllers.get( id ); | ||
|
||
if ( controller ) { | ||
controller.abort(); | ||
this.abortControllers.delete( id ); | ||
} | ||
|
||
return; | ||
} | ||
|
||
// Otherwise, cancel the most recent request. | ||
const lastKey = Array.from( this.abortControllers.keys() ).pop(); | ||
if ( lastKey ) { | ||
const controller = this.abortControllers.get( lastKey ); | ||
|
||
if ( controller ) { | ||
controller.abort(); | ||
this.abortControllers.delete( lastKey ); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Cancels all fetch requests for the provider. | ||
* | ||
* @since 3.15.0 | ||
*/ | ||
public cancelAll(): void { | ||
this.abortControllers.forEach( ( controller ) => controller.abort() ); | ||
this.abortControllers.clear(); | ||
} | ||
|
||
/** | ||
* Private method to manage creating and storing AbortControllers. | ||
* | ||
* @since 3.15.0 | ||
* | ||
* @param {string?} id The (optional) ID of the request. | ||
* | ||
* @return {GetAbortControllerResult} The AbortController and its ID. | ||
*/ | ||
private getOrCreateController( id?: string ): GetAbortControllerResult { | ||
if ( id && this.abortControllers.has( id ) ) { | ||
return { | ||
abortController: this.abortControllers.get( id )!, | ||
abortId: id, | ||
}; | ||
} | ||
|
||
// If no ID is provided, generate one. | ||
const abortId = id ?? 'auto-' + Date.now(); | ||
// Create a new AbortController. | ||
const controller = new AbortController(); | ||
// Store the AbortController. | ||
this.abortControllers.set( abortId, controller ); | ||
|
||
return { | ||
abortController: controller, | ||
abortId, | ||
}; | ||
} | ||
|
||
/** | ||
* Fetches data from the API. Either resolves with the data or rejects with | ||
* an error. | ||
* | ||
* This method is a wrapper around apiFetch() that automatically adds the | ||
* AbortController signal. | ||
* | ||
* @since 3.15.0 | ||
* | ||
* @param {APIFetchOptions} options The options to pass to apiFetch | ||
* @param {string?} id The (optional) ID of the request | ||
* | ||
* @return {Promise<ContentHelperAPIResponse<any>>} The fetched data | ||
*/ | ||
protected async fetch<T>( options: APIFetchOptions, id?: string ): Promise<T> { | ||
const { abortController, abortId } = this.getOrCreateController( id ); | ||
options.signal = abortController.signal; | ||
|
||
try { | ||
const response = await apiFetch<ContentHelperAPIResponse<T>>( options ); | ||
|
||
// Validate API side errors. | ||
if ( response.error ) { | ||
return Promise.reject( | ||
new ContentHelperError( | ||
response.error.message, | ||
ContentHelperErrorCode.ParselyApiResponseContainsError, | ||
), | ||
); | ||
} | ||
|
||
return response.data; | ||
} catch ( wpError: any ) { // eslint-disable-line @typescript-eslint/no-explicit-any | ||
if ( wpError.name === 'AbortError' ) { | ||
return Promise.reject( | ||
new ContentHelperError( | ||
__( 'The operation was aborted.', 'wp-parsely' ), | ||
ContentHelperErrorCode.ParselyAborted, | ||
), | ||
); | ||
} | ||
|
||
return Promise.reject( new ContentHelperError( wpError.message, wpError.code ) ); | ||
} finally { | ||
// Clean-up the AbortController after a successful request. | ||
this.abortControllers.delete( abortId ); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.