/** * External dependencies */ import { select, dispatch } from '@wordpress/data-controls'; import { addQueryArgs } from '@wordpress/url'; /** * Internal dependencies */ import { receiveCollection, receiveCollectionError } from './actions'; import { STORE_KEY as SCHEMA_STORE_KEY } from '../schema/constants'; import { STORE_KEY, DEFAULT_EMPTY_ARRAY } from './constants'; import { apiFetchWithHeaders } from '../shared-controls'; /** * Check if the store needs invalidating due to a change in last modified headers. * * @param {number} timestamp Last update timestamp. */ function* invalidateModifiedCollection( timestamp ) { const lastModified = yield select( STORE_KEY, 'getCollectionLastModified' ); if ( ! lastModified ) { yield dispatch( STORE_KEY, 'receiveLastModified', timestamp ); } else if ( timestamp > lastModified ) { yield dispatch( STORE_KEY, 'invalidateResolutionForStore' ); yield dispatch( STORE_KEY, 'receiveLastModified', timestamp ); } } /** * Resolver for retrieving a collection via a api route. * * @param {string} namespace * @param {string} resourceName * @param {Object} query * @param {Array} ids */ export function* getCollection( namespace, resourceName, query, ids ) { const route = yield select( SCHEMA_STORE_KEY, 'getRoute', namespace, resourceName, ids ); const queryString = addQueryArgs( '', query ); if ( ! route ) { yield receiveCollection( namespace, resourceName, queryString, ids ); return; } try { const { response = DEFAULT_EMPTY_ARRAY, headers, } = yield apiFetchWithHeaders( { path: route + queryString } ); if ( headers && headers.get && headers.has( 'last-modified' ) ) { // Do any invalidation before the collection is received to prevent // this query running again. yield invalidateModifiedCollection( parseInt( headers.get( 'last-modified' ), 10 ) ); } yield receiveCollection( namespace, resourceName, queryString, ids, { items: response, headers, } ); } catch ( error ) { yield receiveCollectionError( namespace, resourceName, queryString, ids, error ); } } /** * Resolver for retrieving a specific collection header for the given arguments * * Note: This triggers the `getCollection` resolver if it hasn't been resolved * yet. * * @param {string} header * @param {string} namespace * @param {string} resourceName * @param {Object} query * @param {Array} ids */ export function* getCollectionHeader( header, namespace, resourceName, query, ids ) { // feed the correct number of args in for the select so we don't resolve // unnecessarily. Any undefined args will be excluded. This is important // because resolver resolution is cached by both number and value of args. const args = [ namespace, resourceName, query, ids ].filter( ( arg ) => typeof arg !== 'undefined' ); //we call this simply to do any resolution of the collection if necessary. yield select( STORE_KEY, 'getCollection', ...args ); }