hooks_use-hermes_mutation.js

import { isObject } from '@gravityforms/utils';
import { buildQueryString } from './query';
/**
 * @function buildInsertObject
 * @description Build an insert object for use in Hermes mutation.
 *
 * @since 4.1.0
 *
 * @param {object} obj The object to convert to an insert object.
 *
 * @return {object} The insert object.
 */
export const buildInsertObject = ( obj ) => {
	const { objects = [], returning = {} } = obj;

	return {
		_args: { objects },
		returning,
	};
};

/**
 * @function buildUpdateObject
 * @description Build an update object for use in Hermes mutation.
 *
 * @since 4.1.0
 *
 * @param {object} obj The object to convert to an update object.
 *
 * @return {object} The update object.
 */
export const buildUpdateObject = ( obj ) => {
	const { id, _args = {}, returning = {} } = obj;
	if ( ! id ) {
		throw new Error( 'An id is required for update mutations.' );
	}

	return {
		_args: {
			id,
			..._args,
		},
		returning,
	};
};

/**
 * @function buildDeleteObject
 * @description Build a delete object for use in Hermes mutation.
 *
 * @since 4.1.0
 *
 * @param {object} obj The object to convert to a delete object.
 *
 * @return {object} The delete object.
 */
export const buildDeleteObject = ( obj ) => {
	const { id } = obj;
	if ( ! id ) {
		throw new Error( 'An id is required for delete mutations.' );
	}

	return {
		_args: { id },
	};
};

/**
 * @function buildConnectObject
 * @description Build a connect or disconnect object for use in Hermes mutation.
 *
 * @since 4.1.0
 *
 * @param {object|Array} obj The object or array of objects to convert to a connect or disconnect object.
 *
 * @return {object} The connect or disconnect object.
 */
export const buildConnectObject = ( obj ) => {
	let objects = [];
	if ( Array.isArray( obj ) ) {
		// Check that each item has a from and to key and are not empty, otherwise throw error.
		for ( const item of obj ) {
			if ( ! isObject( item ) ) {
				throw new Error( 'An object or array of objects is required for connect mutations.' );
			}
			const { from, to } = item;
			if ( ! from || ! to ) {
				throw new Error( 'Both from id and to id are required for connect mutations.' );
			}
		}
		objects = obj;
	} else if ( isObject( obj ) ) {
		// Check that the object has a from and to key and are not empty, otherwise throw error.
		const { from, to } = obj;
		if ( ! from || ! to ) {
			throw new Error( 'Both from id and to id are required for connect mutations.' );
		}
		objects = [ obj ];
	} else {
		// Throw error if the object is not an object or array.
		throw new Error( 'An object or array of objects is required for connect mutations.' );
	}

	return {
		_args: {
			objects,
		},
	};
};

const INSERT = 'insert';
const UPDATE = 'update';
const DELETE = 'delete';
const CONNECT = 'connect';
const DISCONNECT = 'disconnect';

export const mutationTypes = [ INSERT, UPDATE, DELETE, CONNECT, DISCONNECT ];
const mutationTypesToBuildObjectMap = {
	[ INSERT ]: buildInsertObject,
	[ UPDATE ]: buildUpdateObject,
	[ DELETE ]: buildDeleteObject,
	[ CONNECT ]: buildConnectObject,
	[ DISCONNECT ]: buildConnectObject,
};

/**
 * @function buildMutationString
 * @description Build a mutation string from a mutation object and mutation type for use in Hermes mutation.
 *
 * @since 4.1.0
 *
 * @param {object} mutationObj  The mutation object to convert to a string.
 * @param {string} mutationType The type of mutation to build. Must be one of 'insert', 'update', 'delete', 'connect', or 'disconnect'.
 *
 * @return {string} The mutation string.
 *
 * @example
 * // Example inserting single company object.
 * const insertMutationObj = {
 *   company: {
 *     objects: [ { name: 'My Business', address: '123 Main St' } ],
 *     returning: {
 *       id: true,
 *       name: true,
 *       address: true,
 *     },
 *   },
 * };
 *
 * const insertMutationString = buildMutationString( insertMutationObj, 'insert' );
 * // insertMutationString = '{insert_company(objects: [{name: "My Business", address: "123 Main St"}]) {returning {id, name, address}}}';
 *
 *
 * // Example with single company updates.
 * const updateMutationObj = {
 *   company: {
 *     id: 1,
 *     _args: {
 *       name: 'My Business',
 *       address: '123 Main St',
 *     },
 *     returning: {
 *       id: true,
 *       name: true,
 *       address: true,
 *     },
 *   },
 * };
 *
 * const updateMutationString = buildMutationString( updateMutationObj, 'update' );
 * // updateMutationString = '{update_company(id: 1, name: "My Business", address: "123 Main St") {returning {id, name, address}}}';
 *
 *
 * // Example deleting single company object.
 * const deleteMutationObj = {
 *   company: {
 *     id: 1,
 *   },
 * };
 *
 * const deleteMutationString = buildMutationString( deleteMutationObj, 'delete' );
 * // deleteMutationString = '{delete_company(id: 1) {}}';
 *
 *
 * // Example connecting single company and department objects.
 * const singleConnectMutationObj = {
 *   company_department: {
 *     from: 1,
 *     to: 2,
 *   },
 * };
 *
 * const singleConnectMutationString = buildMutationString( singleConnectMutationObj, 'connect' );
 * // connectMutationString = '{connect_company_department(objects: [{from: 1, to: 2}]) {}}';
 *
 *
 * // Example connecting multiple company and department objects.
 * const multipleConnectMutationObj = {
 *   company_department: [
 *     {
 *       from: 1,
 *       to: 2,
 *     },
 *     {
 *       from: 3,
 *       to: 4,
 *     },
 *   ],
 * };
 *
 * const multipleConnectMutationString = buildMutationString( multipleConnectMutationObj, 'connect' );
 * // multipleConnectMutationString = '{connect_company_department(objects: [{from: 1, to: 2}, {from: 3, to: 4}]) {}}';
 *
 *
 * // Example disconnecting single company and department objects.
 * const singleDisconnectMutationObj = {
 *   company_department: {
 *     from: 1,
 *     to: 2,
 *   },
 * };
 *
 * const disconnectMutationString = buildMutationString( singleDisconnectMutationObj, 'disconnect' );
 * // disconnectMutationString = '{disconnect_company_department(objects: [{from: 1, to: 2}]) {}}';
 *
 *
 * // Example disconnecting multiple company and department objects.
 * const multipleDisconnectMutationObj = {
 *   company_department: [
 *     {
 *       from: 1,
 *       to: 2,
 *     },
 *     {
 *       from: 3,
 *       to: 4,
 *     },
 *   ],
 * };
 *
 * const multipleDisconnectMutationString = buildMutationString( multipleDisconnectMutationObj, 'disconnect' );
 * // multipleDisconnectMutationString = '{disconnect_company_department(objects: [{from: 1, to: 2}, {from: 3, to: 4}]) {}}';
 *
 */
export const buildMutationString = ( mutationObj, mutationType ) => {
	// If mutation type is not one of 'insert', 'update', 'delete', 'connect', or 'disconnect', throw an error.
	if ( ! mutationTypes.includes( mutationType ) ) {
		throw new Error( 'Invalid mutation type. Must be one of "insert", "update", "delete", "connect", or "disconnect".' );
	}

	// We only handle single key mutation objects for now.
	const keys = Object.keys( mutationObj );
	if ( keys.length !== 1 ) {
		throw new Error( 'Only a single key mutation object is allowed.' );
	}

	const key = keys[ 0 ];
	const buildObject = mutationTypesToBuildObjectMap[ mutationType ];

	const queryObj = {
		[ `${ mutationType }_${ key }` ]: buildObject( mutationObj[ key ] ),
	};

	return buildQueryString( queryObj );
};