-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an apiFetch middleware to automatically handle media upload failu…
…res (#17858) * Add an apiFetch middleware to automatically handle media upload failures * Remove the attachement on failures * Handle errors properly * limit the media upload middleware to the 500 responses * Fix the error handling and unit tests * Api Fetch: Check for 502s and parse uncaught errors in Media Upload middleware.
- Loading branch information
1 parent
70986bd
commit 0ffde5a
Showing
4 changed files
with
151 additions
and
46 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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { | ||
parseAndThrowError, | ||
parseResponseAndNormalizeError, | ||
} from '../utils/response'; | ||
|
||
/** | ||
* Middleware handling media upload failures and retries. | ||
* | ||
* @param {Object} options Fetch options. | ||
* @param {Function} next [description] | ||
* | ||
* @return {*} The evaluated result of the remaining middleware chain. | ||
*/ | ||
function mediaUploadMiddleware( options, next ) { | ||
const isMediaUploadRequest = | ||
( options.path && options.path.indexOf( '/wp/v2/media' ) !== -1 ) || | ||
( options.url && options.url.indexOf( '/wp/v2/media' ) !== -1 ); | ||
|
||
if ( ! isMediaUploadRequest ) { | ||
return next( options, next ); | ||
} | ||
let retries = 0; | ||
const maxRetries = 5; | ||
|
||
const postProcess = ( attachmentId ) => { | ||
retries++; | ||
return next( { | ||
path: `/wp/v2/media/${ attachmentId }/post-process`, | ||
method: 'POST', | ||
data: { action: 'create-image-subsizes' }, | ||
parse: false, | ||
} ) | ||
.catch( () => { | ||
if ( retries < maxRetries ) { | ||
return postProcess( attachmentId ); | ||
} | ||
next( { | ||
path: `/wp/v2/media/${ attachmentId }?force=true`, | ||
method: 'DELETE', | ||
} ); | ||
|
||
return Promise.reject(); | ||
} ); | ||
}; | ||
|
||
return next( { ...options, parse: false } ) | ||
.catch( ( response ) => { | ||
const attachmentId = response.headers.get( 'x-wp-upload-attachment-id' ); | ||
if ( ( response.status === 500 || response.status === 502 ) && attachmentId ) { | ||
return postProcess( attachmentId ).catch( () => { | ||
if ( options.parse !== false ) { | ||
return Promise.reject( { | ||
code: 'post_process', | ||
message: __( 'Media upload failed. If this is a photo or a large image, please scale it down and try again.' ), | ||
} ); | ||
} | ||
|
||
return Promise.reject( response ); | ||
} ); | ||
} | ||
return parseAndThrowError( response, options.parse ); | ||
} ) | ||
.then( ( response ) => parseResponseAndNormalizeError( response, options.parse ) ); | ||
} | ||
|
||
export default mediaUploadMiddleware; |
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,70 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Parses the apiFetch response. | ||
* | ||
* @param {Response} response | ||
* @param {boolean} shouldParseResponse | ||
* | ||
* @return {Promise} Parsed response | ||
*/ | ||
const parseResponse = ( response, shouldParseResponse = true ) => { | ||
if ( shouldParseResponse ) { | ||
if ( response.status === 204 ) { | ||
return null; | ||
} | ||
|
||
return response.json ? response.json() : Promise.reject( response ); | ||
} | ||
|
||
return response; | ||
}; | ||
|
||
const parseJsonAndNormalizeError = ( response ) => { | ||
const invalidJsonError = { | ||
code: 'invalid_json', | ||
message: __( 'The response is not a valid JSON response.' ), | ||
}; | ||
|
||
if ( ! response || ! response.json ) { | ||
throw invalidJsonError; | ||
} | ||
|
||
return response.json() | ||
.catch( () => { | ||
throw invalidJsonError; | ||
} ); | ||
}; | ||
|
||
/** | ||
* Parses the apiFetch response properly and normalize response errors. | ||
* | ||
* @param {Response} response | ||
* @param {boolean} shouldParseResponse | ||
* | ||
* @return {Promise} Parsed response. | ||
*/ | ||
export const parseResponseAndNormalizeError = ( response, shouldParseResponse = true ) => { | ||
return Promise.resolve( parseResponse( response, shouldParseResponse ) ) | ||
.catch( ( res ) => parseAndThrowError( res, shouldParseResponse ) ); | ||
}; | ||
|
||
export function parseAndThrowError( response, shouldParseResponse = true ) { | ||
if ( ! shouldParseResponse ) { | ||
throw response; | ||
} | ||
|
||
return parseJsonAndNormalizeError( response ) | ||
.then( ( error ) => { | ||
const unknownError = { | ||
code: 'unknown_error', | ||
message: __( 'An unknown error occurred.' ), | ||
}; | ||
|
||
throw error || unknownError; | ||
} ); | ||
} | ||
|