From 27b4b0d18db2797e37a32a6970dcc779b6a046e9 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 15 Jul 2024 16:30:11 +0400 Subject: [PATCH] Dedupe logic --- packages/core-data/src/resolvers.js | 73 +++++-------------- packages/core-data/src/selectors.ts | 9 +-- packages/core-data/src/utils/index.js | 5 ++ .../core-data/src/utils/user-permissions.js | 40 ++++++++++ 4 files changed, 65 insertions(+), 62 deletions(-) create mode 100644 packages/core-data/src/utils/user-permissions.js diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index e0d03f4e72e7c..0b0cc017086cc 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -15,7 +15,13 @@ import apiFetch from '@wordpress/api-fetch'; */ import { STORE_NAME } from './name'; import { getOrLoadEntitiesConfig, DEFAULT_ENTITY_KEY } from './entities'; -import { forwardResolver, getNormalizedCommaSeparable } from './utils'; +import { + forwardResolver, + getNormalizedCommaSeparable, + getUserPermissionCacheKey, + getUserPermissionsFromResponse, + ALLOWED_RESOURCE_ACTIONS, +} from './utils'; import { getSyncProvider } from './sync'; import { fetchBlockPatterns } from './fetch'; @@ -167,35 +173,16 @@ export const getEntityRecord = const response = await apiFetch( { path, parse: false } ); const record = await response.json(); - - const allowHeader = response.headers?.get( 'allow' ); - const allowedMethods = allowHeader?.allow || allowHeader || ''; - const permissions = {}; - const methods = { - create: 'POST', - read: 'GET', - update: 'PUT', - delete: 'DELETE', - }; - for ( const [ actionName, methodName ] of Object.entries( - methods - ) ) { - permissions[ actionName ] = - allowedMethods.includes( methodName ); - } + const permissions = getUserPermissionsFromResponse( response ); registry.batch( () => { dispatch.receiveEntityRecords( kind, name, record, query ); - for ( const action of [ - 'create', - 'read', - 'update', - 'delete', - ] ) { - const permissionKey = [ action, kind, name, key ] - .filter( Boolean ) - .join( '/' ); + for ( const action of ALLOWED_RESOURCE_ACTIONS ) { + const permissionKey = getUserPermissionCacheKey( + action, + { kind, name, id: key } + ); dispatch.receiveUserPermission( permissionKey, @@ -395,9 +382,7 @@ export const getEmbedPreview = export const canUser = ( requestedAction, resource, id ) => async ( { dispatch, registry } ) => { - const retrievedActions = [ 'create', 'read', 'update', 'delete' ]; - - if ( ! retrievedActions.includes( requestedAction ) ) { + if ( ! ALLOWED_RESOURCE_ACTIONS.includes( requestedAction ) ) { throw new Error( `'${ requestedAction }' is not a valid action.` ); } @@ -429,7 +414,7 @@ export const canUser = const { hasStartedResolution } = registry.select( STORE_NAME ); // Prevent resolving the same resource twice. - for ( const relatedAction of retrievedActions ) { + for ( const relatedAction of ALLOWED_RESOURCE_ACTIONS ) { if ( relatedAction === requestedAction ) { continue; } @@ -456,32 +441,10 @@ export const canUser = return; } - // Optional chaining operator is used here because the API requests don't - // return the expected result in the native version. Instead, API requests - // only return the result, without including response properties like the headers. - const allowHeader = response.headers?.get( 'allow' ); - const allowedMethods = allowHeader?.allow || allowHeader || ''; - - const permissions = {}; - const methods = { - create: 'POST', - read: 'GET', - update: 'PUT', - delete: 'DELETE', - }; - for ( const [ actionName, methodName ] of Object.entries( methods ) ) { - permissions[ actionName ] = allowedMethods.includes( methodName ); - } - + const permissions = getUserPermissionsFromResponse( response ); registry.batch( () => { - for ( const action of retrievedActions ) { - const key = ( - typeof resource === 'object' - ? [ action, resource.kind, resource.name, resource.id ] - : [ action, resource, id ] - ) - .filter( Boolean ) - .join( '/' ); + for ( const action of ALLOWED_RESOURCE_ACTIONS ) { + const key = getUserPermissionCacheKey( action, resource, id ); dispatch.receiveUserPermission( key, permissions[ action ] ); diff --git a/packages/core-data/src/selectors.ts b/packages/core-data/src/selectors.ts index 6ff8e26d3684e..aeec14782ce4f 100644 --- a/packages/core-data/src/selectors.ts +++ b/packages/core-data/src/selectors.ts @@ -20,6 +20,7 @@ import { isRawAttribute, setNestedValue, isNumericID, + getUserPermissionCacheKey, } from './utils'; import type * as ET from './entity-types'; import type { UndoManager } from '@wordpress/undo-manager'; @@ -1156,13 +1157,7 @@ export function canUser( return false; } - const key = ( - isEntity - ? [ action, resource.kind, resource.name, resource.id ] - : [ action, resource, id ] - ) - .filter( Boolean ) - .join( '/' ); + const key = getUserPermissionCacheKey( action, resource, id ); return state.userPermissions[ key ]; } diff --git a/packages/core-data/src/utils/index.js b/packages/core-data/src/utils/index.js index d4d491fab9827..bb4b554443502 100644 --- a/packages/core-data/src/utils/index.js +++ b/packages/core-data/src/utils/index.js @@ -9,3 +9,8 @@ export { default as isRawAttribute } from './is-raw-attribute'; export { default as setNestedValue } from './set-nested-value'; export { default as getNestedValue } from './get-nested-value'; export { default as isNumericID } from './is-numeric-id'; +export { + getUserPermissionCacheKey, + getUserPermissionsFromResponse, + ALLOWED_RESOURCE_ACTIONS, +} from './user-permissions'; diff --git a/packages/core-data/src/utils/user-permissions.js b/packages/core-data/src/utils/user-permissions.js new file mode 100644 index 0000000000000..f425b68ad6c98 --- /dev/null +++ b/packages/core-data/src/utils/user-permissions.js @@ -0,0 +1,40 @@ +export const ALLOWED_RESOURCE_ACTIONS = [ + 'create', + 'read', + 'update', + 'delete', +]; + +export function getUserPermissionsFromResponse( response ) { + const permissions = {}; + + // Optional chaining operator is used here because the API requests don't + // return the expected result in the native version. Instead, API requests + // only return the result, without including response properties like the headers. + const allowHeader = response.headers?.get( 'allow' ); + const allowedMethods = allowHeader?.allow || allowHeader || ''; + + const methods = { + create: 'POST', + read: 'GET', + update: 'PUT', + delete: 'DELETE', + }; + for ( const [ actionName, methodName ] of Object.entries( methods ) ) { + permissions[ actionName ] = allowedMethods.includes( methodName ); + } + + return permissions; +} + +export function getUserPermissionCacheKey( action, resource, id ) { + const key = ( + typeof resource === 'object' + ? [ action, resource.kind, resource.name, resource.id ] + : [ action, resource, id ] + ) + .filter( Boolean ) + .join( '/' ); + + return key; +}