diff --git a/client/state/data-layer/wpcom-http/index.js b/client/state/data-layer/wpcom-http/index.js index 2e7b79400d2315..9867ea51893c28 100644 --- a/client/state/data-layer/wpcom-http/index.js +++ b/client/state/data-layer/wpcom-http/index.js @@ -28,8 +28,8 @@ const fetcherMap = method => get( { POST: wpcom.req.post.bind( wpcom.req ), }, method, null ); -export const successMeta = data => ( { meta: { dataLayer: { data } } } ); -export const failureMeta = error => ( { meta: { dataLayer: { error } } } ); +export const successMeta = ( data, headers ) => ( { meta: { dataLayer: { data, headers } } } ); +export const failureMeta = ( error, headers ) => ( { meta: { dataLayer: { error, headers } } } ); export const progressMeta = ( { total, loaded } ) => ( { meta: { dataLayer: { progress: { total, loaded } } } } ); export const queueRequest = ( processOutbound, processInbound ) => ( { dispatch }, rawAction, next ) => { @@ -54,22 +54,23 @@ export const queueRequest = ( processOutbound, processInbound ) => ( { dispatch { path, formData }, query, method === 'POST' && body, - ( error, data ) => { + ( error, data, headers ) => { const { failures, nextData, nextError, + nextHeaders, shouldAbort, successes - } = processInbound( action, { dispatch }, data, error ); + } = processInbound( action, { dispatch }, data, error, headers ); if ( true === shouldAbort ) { return null; } return !! nextError - ? failures.forEach( handler => dispatch( extendAction( handler, failureMeta( nextError ) ) ) ) - : successes.forEach( handler => dispatch( extendAction( handler, successMeta( nextData ) ) ) ); + ? failures.forEach( handler => dispatch( extendAction( handler, failureMeta( nextError, nextHeaders ) ) ) ) + : successes.forEach( handler => dispatch( extendAction( handler, successMeta( nextData, nextHeaders ) ) ) ); } ] ) ); diff --git a/client/state/data-layer/wpcom-http/pipeline/index.js b/client/state/data-layer/wpcom-http/pipeline/index.js index f8cb171f9c3a80..1681c77cd8e52d 100644 --- a/client/state/data-layer/wpcom-http/pipeline/index.js +++ b/client/state/data-layer/wpcom-http/pipeline/index.js @@ -17,8 +17,10 @@ import { applyDuplicatesHandlers, removeDuplicateGets } from './remove-duplicate * @property {ReduxStore} store Redux store * @property {*} originalData response data from returned network request * @property {*} originalError response error from returned network request + * @property {*} originalHeaders response headers from returned network request * @property {*} nextData transformed response data * @property {*} nextError transformed response error + * @property {*} nextHeaders transformed repsonse headers * @property {Object[]} failures list of `onFailure` actions to dispatch * @property {Object[]} successes list of `onSuccess` actions to dispatch * @property {Boolean} [shouldAbort] whether or not no further processing should occur for request @@ -63,19 +65,21 @@ const applyOutboundProcessor = ( outboundData, nextProcessor ) => ? nextProcessor( outboundData ) : outboundData; -export const processInboundChain = chain => ( originalRequest, store, originalData, originalError ) => +export const processInboundChain = chain => ( originalRequest, store, originalData, originalError, originalHeaders ) => pick( chain.reduce( applyInboundProcessor, { originalRequest, store, originalData, originalError, + originalHeaders, nextData: originalData, nextError: originalError, + nextHeaders: originalHeaders, failures: compact( [ originalRequest.onFailure ] ), successes: compact( [ originalRequest.onSuccess ] ), } ), - [ 'failures', 'nextData', 'nextError', 'successes', 'shouldAbort' ], + [ 'failures', 'nextData', 'nextError', 'nextHeaders', 'successes', 'shouldAbort' ], ); export const processOutboundChain = chain => ( originalRequest, store ) => diff --git a/client/state/data-layer/wpcom-http/pipeline/test/test.js b/client/state/data-layer/wpcom-http/pipeline/test/test.js index 9ec65045ceace7..3151903d5e11ac 100644 --- a/client/state/data-layer/wpcom-http/pipeline/test/test.js +++ b/client/state/data-layer/wpcom-http/pipeline/test/test.js @@ -35,44 +35,48 @@ describe( '#processInboundChain', () => { it( 'should pass through data given an empty chain', () => { expect( - processInboundChain( [] )( getSites, {}, { value: 1 }, { error: 'bad' } ) + processInboundChain( [] )( getSites, {}, { value: 1 }, { error: 'bad' }, {} ) ).to.eql( { failures: [ getSites.onFailure ], nextData: { value: 1 }, nextError: { error: 'bad' }, + nextHeaders: {}, successes: [ getSites.onSuccess ], } ); } ); it( 'should sequence a single processor', () => { expect( - processInboundChain( [ responderDoubler ] )( getSites, {}, {}, {} ) + processInboundChain( [ responderDoubler ] )( getSites, {}, {}, {}, {} ) ).to.eql( { failures: [ getSites.onFailure, getSites.onFailure ], nextData: {}, nextError: {}, + nextHeaders: {}, successes: [ getSites.onSuccess, getSites.onSuccess ], } ); } ); it( 'should sequence multiple processors', () => { expect( - processInboundChain( [ responderDoubler, responderDoubler ] )( getSites, {}, {}, {} ) + processInboundChain( [ responderDoubler, responderDoubler ] )( getSites, {}, {}, {}, {} ) ).to.eql( { failures: ( new Array( 4 ) ).fill( getSites.onFailure ), nextData: {}, nextError: {}, + nextHeaders: {}, successes: ( new Array( 4 ) ).fill( getSites.onSuccess ), } ); } ); it( 'should abort the chain as soon as `shouldAbort` is set', () => { expect( - processInboundChain( [ aborter, responderDoubler ] )( getSites, {}, {}, {} ) + processInboundChain( [ aborter, responderDoubler ] )( getSites, {}, {}, {}, {} ) ).to.eql( { failures: [], nextData: {}, nextError: {}, + nextHeaders: {}, successes: [], shouldAbort: true, } ); diff --git a/client/state/data-layer/wpcom-http/utils.js b/client/state/data-layer/wpcom-http/utils.js index f92a03e25aca67..5b0fb17db137f6 100644 --- a/client/state/data-layer/wpcom-http/utils.js +++ b/client/state/data-layer/wpcom-http/utils.js @@ -19,6 +19,14 @@ export const getData = action => get( action, 'meta.dataLayer.data', null ); */ export const getError = action => get( action, 'meta.dataLayer.error', null ); +/** + * Returns (response) headers data from an HTTP request action if available + * + * @param {Object} action may contain HTTP response headers data + * @returns {?*} headers data if available + */ +export const getHeaders = action => get( action, 'meta.dataLayer.headers', null ); + /** * @typedef {Object} ProgressData * @property {number} loaded number of bytes already transferred