UNPKG

@wordpress/core-data

Version:
880 lines (844 loc) 31.8 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.getUserPatternCategories = exports.getThemeSupports = exports.getTemplateAutoDraftId = exports.getRevisions = exports.getRevision = exports.getRegisteredPostMeta = exports.getRawEntityRecord = exports.getNavigationFallbackId = exports.getEntityRecordsTotalPages = exports.getEntityRecordsTotalItems = exports.getEntityRecords = exports.getEntityRecord = exports.getEntitiesConfig = exports.getEmbedPreview = exports.getEditedEntityRecord = exports.getDefaultTemplateId = exports.getCurrentUser = exports.getCurrentThemeGlobalStylesRevisions = exports.getCurrentTheme = exports.getBlockPatterns = exports.getBlockPatternCategories = exports.getAutosaves = exports.getAutosave = exports.getAuthors = exports.canUserEditEntityRecord = exports.canUser = exports.__experimentalGetCurrentThemeGlobalStylesVariations = exports.__experimentalGetCurrentThemeBaseGlobalStyles = exports.__experimentalGetCurrentGlobalStylesId = void 0; var _changeCase = require("change-case"); var _url = require("@wordpress/url"); var _htmlEntities = require("@wordpress/html-entities"); var _apiFetch = _interopRequireDefault(require("@wordpress/api-fetch")); var _name = require("./name"); var _entities = require("./entities"); var _utils = require("./utils"); var _sync = require("./sync"); var _fetch = require("./fetch"); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ /** * Requests authors from the REST API. * * @param {Object|undefined} query Optional object of query parameters to * include with request. */ const getAuthors = query => async ({ dispatch }) => { const path = (0, _url.addQueryArgs)('/wp/v2/users/?who=authors&per_page=100', query); const users = await (0, _apiFetch.default)({ path }); dispatch.receiveUserQuery(path, users); }; /** * Requests the current user from the REST API. */ exports.getAuthors = getAuthors; const getCurrentUser = () => async ({ dispatch }) => { const currentUser = await (0, _apiFetch.default)({ path: '/wp/v2/users/me' }); dispatch.receiveCurrentUser(currentUser); }; /** * Requests an entity's record from the REST API. * * @param {string} kind Entity kind. * @param {string} name Entity name. * @param {number|string} key Record's key * @param {Object|undefined} query Optional object of query parameters to * include with request. If requesting specific * fields, fields must always include the ID. */ exports.getCurrentUser = getCurrentUser; const getEntityRecord = (kind, name, key = '', query) => async ({ select, dispatch, registry, resolveSelect }) => { // For back-compat, we allow querying for static templates through // wp_template. if (kind === 'postType' && name === 'wp_template' && typeof key === 'string' && // __experimentalGetDirtyEntityRecords always calls getEntityRecord // with a string key, so we need that it's not a numeric ID. !/^\d+$/.test(key)) { name = 'wp_registered_template'; } const configs = await resolveSelect.getEntitiesConfig(kind); const entityConfig = configs.find(config => config.name === name && config.kind === kind); if (!entityConfig) { return; } const lock = await dispatch.__unstableAcquireStoreLock(_name.STORE_NAME, ['entities', 'records', kind, name, key], { exclusive: false }); try { // Entity supports configs, // use the sync algorithm instead of the old fetch behavior. if (window.__experimentalEnableSync && entityConfig.syncConfig && !query) { if (globalThis.IS_GUTENBERG_PLUGIN) { const objectId = entityConfig.getSyncObjectId(key); // Loads the persisted document. await (0, _sync.getSyncProvider)().bootstrap(entityConfig.syncObjectType, objectId, record => { dispatch.receiveEntityRecords(kind, name, record, query); }); // Bootstraps the edited document as well (and load from peers). await (0, _sync.getSyncProvider)().bootstrap(entityConfig.syncObjectType + '--edit', objectId, record => { dispatch({ type: 'EDIT_ENTITY_RECORD', kind, name, recordId: key, edits: record, meta: { undo: undefined } }); }); } } else { if (query !== undefined && query._fields) { // If requesting specific fields, items and query association to said // records are stored by ID reference. Thus, fields must always include // the ID. query = { ...query, _fields: [...new Set([...((0, _utils.getNormalizedCommaSeparable)(query._fields) || []), entityConfig.key || _entities.DEFAULT_ENTITY_KEY])].join() }; } if (query !== undefined && query._fields) { // The resolution cache won't consider query as reusable based on the // fields, so it's tested here, prior to initiating the REST request, // and without causing `getEntityRecord` resolution to occur. const hasRecord = select.hasEntityRecord(kind, name, key, query); if (hasRecord) { return; } } const path = (0, _url.addQueryArgs)(entityConfig.baseURL + (key ? '/' + key : ''), { ...entityConfig.baseURLParams, ...query }); const response = await (0, _apiFetch.default)({ path, parse: false }); const record = await response.json(); const permissions = (0, _utils.getUserPermissionsFromAllowHeader)(response.headers?.get('allow')); const canUserResolutionsArgs = []; const receiveUserPermissionArgs = {}; for (const action of _utils.ALLOWED_RESOURCE_ACTIONS) { receiveUserPermissionArgs[(0, _utils.getUserPermissionCacheKey)(action, { kind, name, id: key })] = permissions[action]; canUserResolutionsArgs.push([action, { kind, name, id: key }]); } registry.batch(() => { dispatch.receiveEntityRecords(kind, name, record, query); dispatch.receiveUserPermissions(receiveUserPermissionArgs); dispatch.finishResolutions('canUser', canUserResolutionsArgs); }); } } finally { dispatch.__unstableReleaseStoreLock(lock); } }; exports.getEntityRecord = getEntityRecord; const getTemplateAutoDraftId = staticTemplateId => async ({ resolveSelect, dispatch }) => { const record = await resolveSelect.getEntityRecord('postType', 'wp_registered_template', staticTemplateId); const autoDraft = await dispatch.saveEntityRecord('postType', 'wp_template', { ...record, id: undefined, type: 'wp_template', status: 'auto-draft' }); await dispatch.receiveTemplateAutoDraftId(staticTemplateId, autoDraft.id); }; /** * Requests an entity's record from the REST API. */ exports.getTemplateAutoDraftId = getTemplateAutoDraftId; const getRawEntityRecord = exports.getRawEntityRecord = (0, _utils.forwardResolver)('getEntityRecord'); /** * Requests an entity's record from the REST API. */ const getEditedEntityRecord = exports.getEditedEntityRecord = (0, _utils.forwardResolver)('getEntityRecord'); /** * Requests the entity's records from the REST API. * * @param {string} kind Entity kind. * @param {string} name Entity name. * @param {?Object} query Query Object. If requesting specific fields, fields * must always include the ID. */ const getEntityRecords = (kind, name, query = {}) => async ({ dispatch, registry, resolveSelect }) => { const configs = await resolveSelect.getEntitiesConfig(kind); const entityConfig = configs.find(config => config.name === name && config.kind === kind); if (!entityConfig) { return; } const lock = await dispatch.__unstableAcquireStoreLock(_name.STORE_NAME, ['entities', 'records', kind, name], { exclusive: false }); // Keep a copy of the original query for later use in getResolutionsArgs. // The query object may be modified below (for example, when _fields is // specified), but we want to use the original query when marking // resolutions as finished. const rawQuery = { ...query }; const key = entityConfig.key || _entities.DEFAULT_ENTITY_KEY; function getResolutionsArgs(records, recordsQuery) { const queryArgs = Object.fromEntries(Object.entries(recordsQuery).filter(([k, v]) => { return ['context', '_fields'].includes(k) && !!v; })); return records.filter(record => record?.[key]).map(record => [kind, name, record[key], Object.keys(queryArgs).length > 0 ? queryArgs : undefined]); } try { if (query._fields) { // If requesting specific fields, items and query association to said // records are stored by ID reference. Thus, fields must always include // the ID. query = { ...query, _fields: [...new Set([...((0, _utils.getNormalizedCommaSeparable)(query._fields) || []), key])].join() }; } const path = (0, _url.addQueryArgs)(entityConfig.baseURL, { ...entityConfig.baseURLParams, ...query }); let records = [], meta; if (entityConfig.supportsPagination && query.per_page !== -1) { const response = await (0, _apiFetch.default)({ path, parse: false }); records = Object.values(await response.json()); meta = { totalItems: parseInt(response.headers.get('X-WP-Total')), totalPages: parseInt(response.headers.get('X-WP-TotalPages')) }; } else if (query.per_page === -1 && query[_utils.RECEIVE_INTERMEDIATE_RESULTS] === true) { let page = 1; let totalPages; do { const response = await (0, _apiFetch.default)({ path: (0, _url.addQueryArgs)(path, { page, per_page: 100 }), parse: false }); const pageRecords = Object.values(await response.json()); totalPages = parseInt(response.headers.get('X-WP-TotalPages')); if (!meta) { meta = { totalItems: parseInt(response.headers.get('X-WP-Total')), totalPages: 1 }; } records.push(...pageRecords); registry.batch(() => { dispatch.receiveEntityRecords(kind, name, records, query, false, undefined, meta); dispatch.finishResolutions('getEntityRecord', getResolutionsArgs(pageRecords, rawQuery)); }); page++; } while (page <= totalPages); } else { records = Object.values(await (0, _apiFetch.default)({ path })); meta = { totalItems: records.length, totalPages: 1 }; } // If we request fields but the result doesn't contain the fields, // explicitly set these fields as "undefined" // that way we consider the query "fulfilled". if (query._fields) { records = records.map(record => { query._fields.split(',').forEach(field => { if (!record.hasOwnProperty(field)) { record[field] = undefined; } }); return record; }); } registry.batch(() => { dispatch.receiveEntityRecords(kind, name, records, query, false, undefined, meta); const targetHints = records.filter(record => !!record?.[key] && !!record?._links?.self?.[0]?.targetHints?.allow).map(record => ({ id: record[key], permissions: (0, _utils.getUserPermissionsFromAllowHeader)(record._links.self[0].targetHints.allow) })); const canUserResolutionsArgs = []; const receiveUserPermissionArgs = {}; for (const targetHint of targetHints) { for (const action of _utils.ALLOWED_RESOURCE_ACTIONS) { canUserResolutionsArgs.push([action, { kind, name, id: targetHint.id }]); receiveUserPermissionArgs[(0, _utils.getUserPermissionCacheKey)(action, { kind, name, id: targetHint.id })] = targetHint.permissions[action]; } } if (targetHints.length > 0) { dispatch.receiveUserPermissions(receiveUserPermissionArgs); dispatch.finishResolutions('canUser', canUserResolutionsArgs); } dispatch.finishResolutions('getEntityRecord', getResolutionsArgs(records, rawQuery)); dispatch.__unstableReleaseStoreLock(lock); }); } catch (e) { dispatch.__unstableReleaseStoreLock(lock); } }; exports.getEntityRecords = getEntityRecords; getEntityRecords.shouldInvalidate = (action, kind, name) => { return (action.type === 'RECEIVE_ITEMS' || action.type === 'REMOVE_ITEMS') && action.invalidateCache && kind === action.kind && name === action.name; }; /** * Requests the total number of entity records. */ const getEntityRecordsTotalItems = exports.getEntityRecordsTotalItems = (0, _utils.forwardResolver)('getEntityRecords'); /** * Requests the number of available pages for the given query. */ const getEntityRecordsTotalPages = exports.getEntityRecordsTotalPages = (0, _utils.forwardResolver)('getEntityRecords'); /** * Requests the current theme. */ const getCurrentTheme = () => async ({ dispatch, resolveSelect }) => { const activeThemes = await resolveSelect.getEntityRecords('root', 'theme', { status: 'active' }); dispatch.receiveCurrentTheme(activeThemes[0]); }; /** * Requests theme supports data from the index. */ exports.getCurrentTheme = getCurrentTheme; const getThemeSupports = exports.getThemeSupports = (0, _utils.forwardResolver)('getCurrentTheme'); /** * Requests a preview from the Embed API. * * @param {string} url URL to get the preview for. */ const getEmbedPreview = url => async ({ dispatch }) => { try { const embedProxyResponse = await (0, _apiFetch.default)({ path: (0, _url.addQueryArgs)('/oembed/1.0/proxy', { url }) }); dispatch.receiveEmbedPreview(url, embedProxyResponse); } catch (error) { // Embed API 404s if the URL cannot be embedded, so we have to catch the error from the apiRequest here. dispatch.receiveEmbedPreview(url, false); } }; /** * Checks whether the current user can perform the given action on the given * REST resource. * * @param {string} requestedAction Action to check. One of: 'create', 'read', 'update', * 'delete'. * @param {string|Object} resource Entity resource to check. Accepts entity object `{ kind: 'postType', name: 'attachment', id: 1 }` * or REST base as a string - `media`. * @param {?string} id ID of the rest resource to check. */ exports.getEmbedPreview = getEmbedPreview; const canUser = (requestedAction, resource, id) => async ({ dispatch, registry, resolveSelect }) => { if (!_utils.ALLOWED_RESOURCE_ACTIONS.includes(requestedAction)) { throw new Error(`'${requestedAction}' is not a valid action.`); } const { hasStartedResolution } = registry.select(_name.STORE_NAME); // Prevent resolving the same resource twice. for (const relatedAction of _utils.ALLOWED_RESOURCE_ACTIONS) { if (relatedAction === requestedAction) { continue; } const isAlreadyResolving = hasStartedResolution('canUser', [relatedAction, resource, id]); if (isAlreadyResolving) { return; } } let resourcePath = null; if (typeof resource === 'object') { if (!resource.kind || !resource.name) { throw new Error('The entity resource object is not valid.'); } const configs = await resolveSelect.getEntitiesConfig(resource.kind); const entityConfig = configs.find(config => config.name === resource.name && config.kind === resource.kind); if (!entityConfig) { return; } resourcePath = entityConfig.baseURL + (resource.id ? '/' + resource.id : ''); } else { resourcePath = `/wp/v2/${resource}` + (id ? '/' + id : ''); } let response; try { response = await (0, _apiFetch.default)({ path: resourcePath, method: 'OPTIONS', parse: false }); } catch (error) { // Do nothing if our OPTIONS request comes back with an API error (4xx or // 5xx). The previously determined isAllowed value will remain in the store. return; } // Optional chaining operator is used here because the API requests don't // return the expected result in the React native version. Instead, API requests // only return the result, without including response properties like the headers. const permissions = (0, _utils.getUserPermissionsFromAllowHeader)(response.headers?.get('allow')); registry.batch(() => { for (const action of _utils.ALLOWED_RESOURCE_ACTIONS) { const key = (0, _utils.getUserPermissionCacheKey)(action, resource, id); dispatch.receiveUserPermission(key, permissions[action]); // Mark related action resolutions as finished. if (action !== requestedAction) { dispatch.finishResolution('canUser', [action, resource, id]); } } }); }; /** * Checks whether the current user can perform the given action on the given * REST resource. * * @param {string} kind Entity kind. * @param {string} name Entity name. * @param {number|string} recordId Record's id. */ exports.canUser = canUser; const canUserEditEntityRecord = (kind, name, recordId) => async ({ dispatch }) => { await dispatch(canUser('update', { kind, name, id: recordId })); }; /** * Request autosave data from the REST API. * * @param {string} postType The type of the parent post. * @param {number} postId The id of the parent post. */ exports.canUserEditEntityRecord = canUserEditEntityRecord; const getAutosaves = (postType, postId) => async ({ dispatch, resolveSelect }) => { const { rest_base: restBase, rest_namespace: restNamespace = 'wp/v2', supports } = await resolveSelect.getPostType(postType); if (!supports?.autosave) { return; } const autosaves = await (0, _apiFetch.default)({ path: `/${restNamespace}/${restBase}/${postId}/autosaves?context=edit` }); if (autosaves && autosaves.length) { dispatch.receiveAutosaves(postId, autosaves); } }; /** * Request autosave data from the REST API. * * This resolver exists to ensure the underlying autosaves are fetched via * `getAutosaves` when a call to the `getAutosave` selector is made. * * @param {string} postType The type of the parent post. * @param {number} postId The id of the parent post. */ exports.getAutosaves = getAutosaves; const getAutosave = (postType, postId) => async ({ resolveSelect }) => { await resolveSelect.getAutosaves(postType, postId); }; exports.getAutosave = getAutosave; const __experimentalGetCurrentGlobalStylesId = () => async ({ dispatch, resolveSelect }) => { const activeThemes = await resolveSelect.getEntityRecords('root', 'theme', { status: 'active' }); const globalStylesURL = activeThemes?.[0]?._links?.['wp:user-global-styles']?.[0]?.href; if (!globalStylesURL) { return; } // Regex matches the ID at the end of a URL or immediately before // the query string. const matches = globalStylesURL.match(/\/(\d+)(?:\?|$)/); const id = matches ? Number(matches[1]) : null; if (id) { dispatch.__experimentalReceiveCurrentGlobalStylesId(id); } }; exports.__experimentalGetCurrentGlobalStylesId = __experimentalGetCurrentGlobalStylesId; const __experimentalGetCurrentThemeBaseGlobalStyles = () => async ({ resolveSelect, dispatch }) => { const currentTheme = await resolveSelect.getCurrentTheme(); // Please adjust the preloaded requests if this changes! const themeGlobalStyles = await (0, _apiFetch.default)({ path: `/wp/v2/global-styles/themes/${currentTheme.stylesheet}?context=view` }); dispatch.__experimentalReceiveThemeBaseGlobalStyles(currentTheme.stylesheet, themeGlobalStyles); }; exports.__experimentalGetCurrentThemeBaseGlobalStyles = __experimentalGetCurrentThemeBaseGlobalStyles; const __experimentalGetCurrentThemeGlobalStylesVariations = () => async ({ resolveSelect, dispatch }) => { const currentTheme = await resolveSelect.getCurrentTheme(); // Please adjust the preloaded requests if this changes! const variations = await (0, _apiFetch.default)({ path: `/wp/v2/global-styles/themes/${currentTheme.stylesheet}/variations?context=view` }); dispatch.__experimentalReceiveThemeGlobalStyleVariations(currentTheme.stylesheet, variations); }; /** * Fetches and returns the revisions of the current global styles theme. */ exports.__experimentalGetCurrentThemeGlobalStylesVariations = __experimentalGetCurrentThemeGlobalStylesVariations; const getCurrentThemeGlobalStylesRevisions = () => async ({ resolveSelect, dispatch }) => { const globalStylesId = await resolveSelect.__experimentalGetCurrentGlobalStylesId(); const record = globalStylesId ? await resolveSelect.getEntityRecord('root', 'globalStyles', globalStylesId) : undefined; const revisionsURL = record?._links?.['version-history']?.[0]?.href; if (revisionsURL) { const resetRevisions = await (0, _apiFetch.default)({ url: revisionsURL }); const revisions = resetRevisions?.map(revision => Object.fromEntries(Object.entries(revision).map(([key, value]) => [(0, _changeCase.camelCase)(key), value]))); dispatch.receiveThemeGlobalStyleRevisions(globalStylesId, revisions); } }; exports.getCurrentThemeGlobalStylesRevisions = getCurrentThemeGlobalStylesRevisions; getCurrentThemeGlobalStylesRevisions.shouldInvalidate = action => { return action.type === 'SAVE_ENTITY_RECORD_FINISH' && action.kind === 'root' && !action.error && action.name === 'globalStyles'; }; const getBlockPatterns = () => async ({ dispatch }) => { const patterns = await (0, _fetch.fetchBlockPatterns)(); dispatch({ type: 'RECEIVE_BLOCK_PATTERNS', patterns }); }; exports.getBlockPatterns = getBlockPatterns; const getBlockPatternCategories = () => async ({ dispatch }) => { const categories = await (0, _apiFetch.default)({ path: '/wp/v2/block-patterns/categories' }); dispatch({ type: 'RECEIVE_BLOCK_PATTERN_CATEGORIES', categories }); }; exports.getBlockPatternCategories = getBlockPatternCategories; const getUserPatternCategories = () => async ({ dispatch, resolveSelect }) => { const patternCategories = await resolveSelect.getEntityRecords('taxonomy', 'wp_pattern_category', { per_page: -1, _fields: 'id,name,description,slug', context: 'view' }); const mappedPatternCategories = patternCategories?.map(userCategory => ({ ...userCategory, label: (0, _htmlEntities.decodeEntities)(userCategory.name), name: userCategory.slug })) || []; dispatch({ type: 'RECEIVE_USER_PATTERN_CATEGORIES', patternCategories: mappedPatternCategories }); }; exports.getUserPatternCategories = getUserPatternCategories; const getNavigationFallbackId = () => async ({ dispatch, select, registry }) => { const fallback = await (0, _apiFetch.default)({ path: (0, _url.addQueryArgs)('/wp-block-editor/v1/navigation-fallback', { _embed: true }) }); const record = fallback?._embedded?.self; registry.batch(() => { dispatch.receiveNavigationFallbackId(fallback?.id); if (!record) { return; } // If the fallback is already in the store, don't invalidate navigation queries. // Otherwise, invalidate the cache for the scenario where there were no Navigation // posts in the state and the fallback created one. const existingFallbackEntityRecord = select.getEntityRecord('postType', 'wp_navigation', fallback.id); const invalidateNavigationQueries = !existingFallbackEntityRecord; dispatch.receiveEntityRecords('postType', 'wp_navigation', record, undefined, invalidateNavigationQueries); // Resolve to avoid further network requests. dispatch.finishResolution('getEntityRecord', ['postType', 'wp_navigation', fallback.id]); }); }; exports.getNavigationFallbackId = getNavigationFallbackId; const getDefaultTemplateId = query => async ({ dispatch, registry, resolveSelect }) => { const template = await (0, _apiFetch.default)({ path: (0, _url.addQueryArgs)('/wp/v2/templates/lookup', query) }); // Wait for the the entities config to be loaded, otherwise receiving // the template as an entity will not work. await resolveSelect.getEntitiesConfig('postType'); const id = template?.wp_id || template?.id; // Endpoint may return an empty object if no template is found. if (id) { template.id = id; template.type = typeof id === 'string' ? 'wp_registered_template' : 'wp_template'; registry.batch(() => { dispatch.receiveDefaultTemplateId(query, id); dispatch.receiveEntityRecords('postType', template.type, [template]); // Avoid further network requests. dispatch.finishResolution('getEntityRecord', ['postType', template.type, id]); }); } }; exports.getDefaultTemplateId = getDefaultTemplateId; getDefaultTemplateId.shouldInvalidate = action => { return action.type === 'EDIT_ENTITY_RECORD' && action.kind === 'root' && action.name === 'site'; }; /** * Requests an entity's revisions from the REST API. * * @param {string} kind Entity kind. * @param {string} name Entity name. * @param {number|string} recordKey The key of the entity record whose revisions you want to fetch. * @param {Object|undefined} query Optional object of query parameters to * include with request. If requesting specific * fields, fields must always include the ID. */ const getRevisions = (kind, name, recordKey, query = {}) => async ({ dispatch, registry, resolveSelect }) => { const configs = await resolveSelect.getEntitiesConfig(kind); const entityConfig = configs.find(config => config.name === name && config.kind === kind); if (!entityConfig) { return; } if (query._fields) { // If requesting specific fields, items and query association to said // records are stored by ID reference. Thus, fields must always include // the ID. query = { ...query, _fields: [...new Set([...((0, _utils.getNormalizedCommaSeparable)(query._fields) || []), entityConfig.revisionKey || _entities.DEFAULT_ENTITY_KEY])].join() }; } const path = (0, _url.addQueryArgs)(entityConfig.getRevisionsUrl(recordKey), query); let records, response; const meta = {}; const isPaginated = entityConfig.supportsPagination && query.per_page !== -1; try { response = await (0, _apiFetch.default)({ path, parse: !isPaginated }); } catch (error) { // Do nothing if our request comes back with an API error. return; } if (response) { if (isPaginated) { records = Object.values(await response.json()); meta.totalItems = parseInt(response.headers.get('X-WP-Total')); } else { records = Object.values(response); } // If we request fields but the result doesn't contain the fields, // explicitly set these fields as "undefined" // that way we consider the query "fulfilled". if (query._fields) { records = records.map(record => { query._fields.split(',').forEach(field => { if (!record.hasOwnProperty(field)) { record[field] = undefined; } }); return record; }); } registry.batch(() => { dispatch.receiveRevisions(kind, name, recordKey, records, query, false, meta); // When requesting all fields, the list of results can be used to // resolve the `getRevision` selector in addition to `getRevisions`. if (!query?._fields && !query.context) { const key = entityConfig.key || _entities.DEFAULT_ENTITY_KEY; const resolutionsArgs = records.filter(record => record[key]).map(record => [kind, name, recordKey, record[key]]); dispatch.finishResolutions('getRevision', resolutionsArgs); } }); } }; // Invalidate cache when a new revision is created. exports.getRevisions = getRevisions; getRevisions.shouldInvalidate = (action, kind, name, recordKey) => action.type === 'SAVE_ENTITY_RECORD_FINISH' && name === action.name && kind === action.kind && !action.error && recordKey === action.recordId; /** * Requests a specific Entity revision from the REST API. * * @param {string} kind Entity kind. * @param {string} name Entity name. * @param {number|string} recordKey The key of the entity record whose revisions you want to fetch. * @param {number|string} revisionKey The revision's key. * @param {Object|undefined} query Optional object of query parameters to * include with request. If requesting specific * fields, fields must always include the ID. */ const getRevision = (kind, name, recordKey, revisionKey, query) => async ({ dispatch, resolveSelect }) => { const configs = await resolveSelect.getEntitiesConfig(kind); const entityConfig = configs.find(config => config.name === name && config.kind === kind); if (!entityConfig) { return; } if (query !== undefined && query._fields) { // If requesting specific fields, items and query association to said // records are stored by ID reference. Thus, fields must always include // the ID. query = { ...query, _fields: [...new Set([...((0, _utils.getNormalizedCommaSeparable)(query._fields) || []), entityConfig.revisionKey || _entities.DEFAULT_ENTITY_KEY])].join() }; } const path = (0, _url.addQueryArgs)(entityConfig.getRevisionsUrl(recordKey, revisionKey), query); let record; try { record = await (0, _apiFetch.default)({ path }); } catch (error) { // Do nothing if our request comes back with an API error. return; } if (record) { dispatch.receiveRevisions(kind, name, recordKey, record, query); } }; /** * Requests a specific post type options from the REST API. * * @param {string} postType Post type slug. */ exports.getRevision = getRevision; const getRegisteredPostMeta = postType => async ({ dispatch, resolveSelect }) => { let options; try { const { rest_namespace: restNamespace = 'wp/v2', rest_base: restBase } = (await resolveSelect.getPostType(postType)) || {}; options = await (0, _apiFetch.default)({ path: `${restNamespace}/${restBase}/?context=edit`, method: 'OPTIONS' }); } catch (error) { // Do nothing if the request comes back with an API error. return; } if (options) { dispatch.receiveRegisteredPostMeta(postType, options?.schema?.properties?.meta?.properties); } }; /** * Requests entity configs for the given kind from the REST API. * * @param {string} kind Entity kind. */ exports.getRegisteredPostMeta = getRegisteredPostMeta; const getEntitiesConfig = kind => async ({ dispatch }) => { const loader = _entities.additionalEntityConfigLoaders.find(l => l.kind === kind); if (!loader) { return; } try { const configs = await loader.loadEntities(); if (!configs.length) { return; } dispatch.addEntities(configs); } catch { // Do nothing if the request comes back with an API error. } }; exports.getEntitiesConfig = getEntitiesConfig; //# sourceMappingURL=resolvers.js.map