@wordpress/core-data
Version:
Access to and manipulation of core WordPress entities.
849 lines (814 loc) • 28.9 kB
JavaScript
/**
* External dependencies
*/
import { camelCase } from 'change-case';
/**
* WordPress dependencies
*/
import { addQueryArgs } from '@wordpress/url';
import { decodeEntities } from '@wordpress/html-entities';
import apiFetch from '@wordpress/api-fetch';
/**
* Internal dependencies
*/
import { STORE_NAME } from './name';
import { additionalEntityConfigLoaders, DEFAULT_ENTITY_KEY } from './entities';
import { forwardResolver, getNormalizedCommaSeparable, getUserPermissionCacheKey, getUserPermissionsFromAllowHeader, ALLOWED_RESOURCE_ACTIONS, RECEIVE_INTERMEDIATE_RESULTS } from './utils';
import { getSyncProvider } from './sync';
import { fetchBlockPatterns } from './fetch';
/**
* Requests authors from the REST API.
*
* @param {Object|undefined} query Optional object of query parameters to
* include with request.
*/
export const getAuthors = query => async ({
dispatch
}) => {
const path = addQueryArgs('/wp/v2/users/?who=authors&per_page=100', query);
const users = await apiFetch({
path
});
dispatch.receiveUserQuery(path, users);
};
/**
* Requests the current user from the REST API.
*/
export const getCurrentUser = () => async ({
dispatch
}) => {
const currentUser = await apiFetch({
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.
*/
export 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(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 getSyncProvider().bootstrap(entityConfig.syncObjectType, objectId, record => {
dispatch.receiveEntityRecords(kind, name, record, query);
});
// Bootstraps the edited document as well (and load from peers).
await 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([...(getNormalizedCommaSeparable(query._fields) || []), entityConfig.key || 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 = addQueryArgs(entityConfig.baseURL + (key ? '/' + key : ''), {
...entityConfig.baseURLParams,
...query
});
const response = await apiFetch({
path,
parse: false
});
const record = await response.json();
const permissions = getUserPermissionsFromAllowHeader(response.headers?.get('allow'));
const canUserResolutionsArgs = [];
const receiveUserPermissionArgs = {};
for (const action of ALLOWED_RESOURCE_ACTIONS) {
receiveUserPermissionArgs[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);
}
};
export 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.
*/
export const getRawEntityRecord = forwardResolver('getEntityRecord');
/**
* Requests an entity's record from the REST API.
*/
export const getEditedEntityRecord = 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.
*/
export 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(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 || 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([...(getNormalizedCommaSeparable(query._fields) || []), key])].join()
};
}
const path = addQueryArgs(entityConfig.baseURL, {
...entityConfig.baseURLParams,
...query
});
let records = [],
meta;
if (entityConfig.supportsPagination && query.per_page !== -1) {
const response = await apiFetch({
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[RECEIVE_INTERMEDIATE_RESULTS] === true) {
let page = 1;
let totalPages;
do {
const response = await apiFetch({
path: 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 apiFetch({
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: getUserPermissionsFromAllowHeader(record._links.self[0].targetHints.allow)
}));
const canUserResolutionsArgs = [];
const receiveUserPermissionArgs = {};
for (const targetHint of targetHints) {
for (const action of ALLOWED_RESOURCE_ACTIONS) {
canUserResolutionsArgs.push([action, {
kind,
name,
id: targetHint.id
}]);
receiveUserPermissionArgs[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);
}
};
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.
*/
export const getEntityRecordsTotalItems = forwardResolver('getEntityRecords');
/**
* Requests the number of available pages for the given query.
*/
export const getEntityRecordsTotalPages = forwardResolver('getEntityRecords');
/**
* Requests the current theme.
*/
export 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.
*/
export const getThemeSupports = forwardResolver('getCurrentTheme');
/**
* Requests a preview from the Embed API.
*
* @param {string} url URL to get the preview for.
*/
export const getEmbedPreview = url => async ({
dispatch
}) => {
try {
const embedProxyResponse = await apiFetch({
path: 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.
*/
export const canUser = (requestedAction, resource, id) => async ({
dispatch,
registry,
resolveSelect
}) => {
if (!ALLOWED_RESOURCE_ACTIONS.includes(requestedAction)) {
throw new Error(`'${requestedAction}' is not a valid action.`);
}
const {
hasStartedResolution
} = registry.select(STORE_NAME);
// Prevent resolving the same resource twice.
for (const relatedAction of 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 apiFetch({
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 = getUserPermissionsFromAllowHeader(response.headers?.get('allow'));
registry.batch(() => {
for (const action of ALLOWED_RESOURCE_ACTIONS) {
const key = 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.
*/
export 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.
*/
export 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 apiFetch({
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.
*/
export const getAutosave = (postType, postId) => async ({
resolveSelect
}) => {
await resolveSelect.getAutosaves(postType, postId);
};
export 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);
}
};
export const __experimentalGetCurrentThemeBaseGlobalStyles = () => async ({
resolveSelect,
dispatch
}) => {
const currentTheme = await resolveSelect.getCurrentTheme();
// Please adjust the preloaded requests if this changes!
const themeGlobalStyles = await apiFetch({
path: `/wp/v2/global-styles/themes/${currentTheme.stylesheet}?context=view`
});
dispatch.__experimentalReceiveThemeBaseGlobalStyles(currentTheme.stylesheet, themeGlobalStyles);
};
export const __experimentalGetCurrentThemeGlobalStylesVariations = () => async ({
resolveSelect,
dispatch
}) => {
const currentTheme = await resolveSelect.getCurrentTheme();
// Please adjust the preloaded requests if this changes!
const variations = await apiFetch({
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.
*/
export 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 apiFetch({
url: revisionsURL
});
const revisions = resetRevisions?.map(revision => Object.fromEntries(Object.entries(revision).map(([key, value]) => [camelCase(key), value])));
dispatch.receiveThemeGlobalStyleRevisions(globalStylesId, revisions);
}
};
getCurrentThemeGlobalStylesRevisions.shouldInvalidate = action => {
return action.type === 'SAVE_ENTITY_RECORD_FINISH' && action.kind === 'root' && !action.error && action.name === 'globalStyles';
};
export const getBlockPatterns = () => async ({
dispatch
}) => {
const patterns = await fetchBlockPatterns();
dispatch({
type: 'RECEIVE_BLOCK_PATTERNS',
patterns
});
};
export const getBlockPatternCategories = () => async ({
dispatch
}) => {
const categories = await apiFetch({
path: '/wp/v2/block-patterns/categories'
});
dispatch({
type: 'RECEIVE_BLOCK_PATTERN_CATEGORIES',
categories
});
};
export 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: decodeEntities(userCategory.name),
name: userCategory.slug
})) || [];
dispatch({
type: 'RECEIVE_USER_PATTERN_CATEGORIES',
patternCategories: mappedPatternCategories
});
};
export const getNavigationFallbackId = () => async ({
dispatch,
select,
registry
}) => {
const fallback = await apiFetch({
path: 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]);
});
};
export const getDefaultTemplateId = query => async ({
dispatch,
registry,
resolveSelect
}) => {
const template = await apiFetch({
path: 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]);
});
}
};
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.
*/
export 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([...(getNormalizedCommaSeparable(query._fields) || []), entityConfig.revisionKey || DEFAULT_ENTITY_KEY])].join()
};
}
const path = addQueryArgs(entityConfig.getRevisionsUrl(recordKey), query);
let records, response;
const meta = {};
const isPaginated = entityConfig.supportsPagination && query.per_page !== -1;
try {
response = await apiFetch({
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 || 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.
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.
*/
export 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([...(getNormalizedCommaSeparable(query._fields) || []), entityConfig.revisionKey || DEFAULT_ENTITY_KEY])].join()
};
}
const path = addQueryArgs(entityConfig.getRevisionsUrl(recordKey, revisionKey), query);
let record;
try {
record = await apiFetch({
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.
*/
export const getRegisteredPostMeta = postType => async ({
dispatch,
resolveSelect
}) => {
let options;
try {
const {
rest_namespace: restNamespace = 'wp/v2',
rest_base: restBase
} = (await resolveSelect.getPostType(postType)) || {};
options = await apiFetch({
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.
*/
export const getEntitiesConfig = kind => async ({
dispatch
}) => {
const loader = 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.
}
};
//# sourceMappingURL=resolvers.js.map