Back

15% Statements 3/20
14.28% Branches 2/14
25% Functions 1/4
15% Lines 3/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80      1x                                                                       3x 3x                                                                              
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
 
const ROOT = '/jetpack/v4';
 
/**
 * Prepend `/jetpack/v4` to a path and optionally append query args.
 *
 * @param path - Path under `/jetpack/v4`, e.g. `/site/capabilities`.
 * @param args - Optional query args record.
 * @return The fully-qualified API path.
 */
export function apiPath(
	path: string,
	args?: Record< string, string | number | boolean | undefined >
): string {
	const base = `${ ROOT }${ path }`;
	if ( ! args ) {
		return base;
	}
	const filtered: Record< string, string | number | boolean > = {};
	for ( const key of Object.keys( args ) ) {
		const value = args[ key ];
		if ( value !== undefined && value !== '' ) {
			filtered[ key ] = value;
		}
	}
	return Object.keys( filtered ).length ? addQueryArgs( base, filtered ) : base;
}
 
/**
 * Strip the decimal suffix WPCOM ships on rewind ids (e.g.
 * `'1777035492.615'` → `'1777035492'`). The `/rewind/backup/$rewindId`
 * URLs reject the suffixed form with a 404.
 *
 * @param rewindId - Raw rewind id, possibly with a decimal suffix.
 * @return The integer-seconds portion only.
 */
export function toIntRewindId( rewindId: string ): string {
	const idx = rewindId.indexOf( '.' );
	return idx === -1 ? rewindId : rewindId.slice( 0, idx );
}
 
/**
 * Error thrown by the data-layer fetchers when WPCOM (via the bridge)
 * responds with a known error code. Consumers branch on `code` to pick
 * the right user-facing message.
 */
export class ApiError extends Error {
	public readonly code: string;
	public readonly data?: unknown;
 
	constructor( code: string, message: string, data?: unknown ) {
		super( message );
		this.name = 'ApiError';
		this.code = code;
		this.data = data;
	}
}
 
/**
 * Thin wrapper around `@wordpress/api-fetch` that re-throws bridge errors
 * as `ApiError` so React Query's onError handlers can branch on `code`.
 *
 * @param options - apiFetch options.
 * @return The decoded JSON response.
 */
export async function apiCall< T >( options: Parameters< typeof apiFetch >[ 0 ] ): Promise< T > {
	try {
		return await apiFetch< T >( options );
	} catch ( raw ) {
		const err = raw as { code?: string; message?: string; data?: unknown };
		throw new ApiError(
			typeof err.code === 'string' ? err.code : 'unknown',
			typeof err.message === 'string' ? err.message : 'Request failed',
			err.data
		);
	}
}