import { React } from '@gravityforms/libraries';
import { post } from '@gravityforms/request';
import { isObject } from '@gravityforms/utils';
import { buildQueryString } from './use-hermes/query';
import { buildMutationString, mutationTypes } from './use-hermes/mutation';
const { useCallback, useState } = React;
/**
* @module useHermes
* @description A hook to interact with the Hermes API.
*
* @since 4.0.5
*
* @param {object} props The props for the hook.
* @param {string} props.action The fallback action to use if `queryAction` or `mutationAction` is not provided.
* @param {object} props.customMutationTypeMap A map of custom mutation types to mutation string builder functions.
* @param {string} props.endpoint The Hermes AJAX endpoint.
* @param {string} props.mutationAction The mutation action to use.
* @param {string} props.mutationKey The key in the request body for mutations.
* @param {string} props.mutationSecurity The security nonce for mutations.
* @param {string} props.queryAction The query action to use.
* @param {string} props.queryKey The key in the request body for queries.
* @param {string} props.querySecurity The security nonce for queries.
* @param {string} props.security The security nonce to use if `querySecurity` or `mutationSecurity` is not provided.
*
* @return {object} The Hermes state and actions.
*/
const useHermes = ( {
action = '',
customMutationTypeMap = {},
endpoint = '',
mutationAction = '',
mutationKey = 'mutation',
mutationSecurity = '',
queryAction = '',
queryKey = 'query',
querySecurity = '',
security = '',
} ) => {
const [ activeRequests, setActiveRequests ] = useState( 0 );
if ( ! queryAction && ! action ) {
throw new Error( 'A `queryAction` or `action` is required.' );
}
if ( ! mutationAction && ! action ) {
throw new Error( 'A `mutationAction` or `action` is required.' );
}
const qAction = queryAction || action;
const mAction = mutationAction || action;
const qSecurity = querySecurity || security;
const mSecurity = mutationSecurity || security;
/**
* @function query
* @description Performs query on the Hermes API.
*
* @since 4.0.5
*
* @async
*
* @param {object} args Args for the query.
* @param {string|object} args.queryObj The query object.
* @param {object} args.headers The headers for the query.
* @param {object} args.body The body for the query.
* @param {object} args.options The options for the query.
*
* @return {Promise} The query promise.
*/
const query = useCallback( async ( {
queryObj,
headers = {},
body = {},
options = {},
} ) => {
// Validate the query object. Must be a string or an object.
if ( typeof queryObj !== 'string' && ! isObject( queryObj ) ) {
throw new Error( 'Invalid query parameter.' );
}
// Build the query string.
let queryString = '';
if ( typeof queryObj === 'string' ) {
queryString = queryObj;
} else if ( isObject( queryObj ) ) {
queryString = buildQueryString( queryObj );
}
// Increment active requests.
setActiveRequests( ( prev ) => prev + 1 );
const response = await post( {
endpoint,
headers,
body: {
...body,
action: qAction,
security: qSecurity,
[ queryKey ]: queryString,
},
options,
} );
setActiveRequests( ( prev ) => prev - 1 );
return response;
}, [ endpoint, qAction, qSecurity, queryKey ] );
/**
* @function mutation
* @description Performs mutation on the Hermes API.
*
* @since 4.0.5
*
* @async
*
* @param {object} args Args for the mutation.
* @param {string|object} args.mutationObj The mutation object.
* @param {string} args.type The mutation type, one of the base types of `insert`, `update`, `delete`, `connect`, or `disconnect`, or a custom mutation type.
* @param {object} args.headers The headers for the mutation.
* @param {object} args.body The body for the mutation.
* @param {object} args.options The options for the mutation.
*
* @return {Promise} The mutation promise.
*/
const mutation = useCallback( async ( {
mutationObj,
type,
headers = {},
body = {},
options = {},
} ) => {
// Validate the mutation object. Must be a string or an object.
if ( typeof mutationObj !== 'string' && ! isObject( mutationObj ) ) {
throw new Error( 'Invalid mutation parameter.' );
}
// Build the mutation string.
let mutationString = '';
if ( typeof mutationObj === 'string' ) {
mutationString = mutationObj;
} else if ( mutationTypes.includes( type ) ) {
mutationString = buildMutationString( mutationObj, type );
} else if ( customMutationTypeMap[ type ] ) {
mutationString = customMutationTypeMap[ type ]( mutationObj );
} else {
throw new Error( 'Invalid mutation type' );
}
// Increment active requests.
setActiveRequests( ( prev ) => prev + 1 );
const response = await post( {
endpoint,
headers,
body: {
...body,
action: mAction,
security: mSecurity,
[ mutationKey ]: mutationString,
},
options,
} );
setActiveRequests( ( prev ) => prev - 1 );
return response;
}, [ Object.keys( customMutationTypeMap ).join(), endpoint, mAction, mSecurity, mutationKey ] ); // eslint-disable-line react-hooks/exhaustive-deps
return {
isLoading: activeRequests > 0,
query,
mutation,
};
};
export default useHermes;