PluginProbe
ElasticPress / 5.0.1
ElasticPress v5.0.1
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
elasticpress / assets / js / sync / src / hooks.js

hooks.js in ElasticPress 5.0.1, at assets/js/sync/src/hooks.js

219 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * WordPress dependencies.
3 */
4 import { useCallback, useRef } from '@wordpress/element';
5 import { __ } from '@wordpress/i18n';
6
7 /**
8 * Indexing hook.
9 *
10 * Provides methods for indexing, getting indexing status, and cancelling
11 * indexing. Methods share an abort controller so that requests can
12 * interrupt eachother to avoid multiple sync requests causing race conditions
13 * or duplicate output, such as by rapidly pausing and unpausing indexing.
14 *
15 * @param {string} apiUrl AJAX endpoint URL.
16 * @param {string} nonce WordPress nonce.
17 * @returns {object} Sync, sync status, and cancel functions.
18 */
19 export const useIndex = (apiUrl, nonce) => {
20 const abort = useRef(new AbortController());
21 const request = useRef(null);
22
23 const onResponse = useCallback(
24 /**
25 * Handle the response to the request.
26 *
27 * @param {Response} response Request response.
28 * @throws {Error} An error for unexpected responses.
29 * @returns {void}
30 */
31 async (response) => {
32 const responseBody = await response.text();
33
34 const errorMessage = `${__(
35 'ElasticPress: Unexpected response.',
36 'elasticpress',
37 )}\n${responseBody}`;
38
39 /**
40 * Throw an error for non-20X responses.
41 */
42 if (!response.ok) {
43 if (response.status === 403) {
44 /**
45 * A 403 response will occur if the nonce has expired or
46 * if the user's session has expired. Reloading the page
47 * will reset the nonce or prompt the user to log in again.
48 */
49 throw new Error(
50 __(
51 'Permission denied. Reload the sync page and try again.',
52 'elasticpress',
53 ),
54 );
55 } else {
56 /**
57 * Log the raw response to the console to assist with
58 * debugging.
59 */
60 console.error(errorMessage); // eslint-disable-line no-console
61
62 /**
63 * Any other response is unexpected, and the user will
64 * need to troubleshoot.
65 */
66 throw new Error(
67 __(
68 'Something went wrong. Find troubleshooting steps at https://elasticpress.zendesk.com/hc/en-us/articles/20857557098125/.',
69 'elasticpress',
70 ),
71 );
72 }
73 }
74
75 /**
76 * Parse the response and throw an error if it fails.
77 */
78 try {
79 return JSON.parse(responseBody);
80 } catch (e) {
81 /**
82 * Log the raw response to the console to assist with
83 * debugging.
84 */
85 console.error(e.message); // eslint-disable-line no-console
86 console.error(errorMessage); // eslint-disable-line no-console
87
88 /**
89 * Invalid JSON is unexpected at this stage, and the user will
90 * need to troubleshoot.
91 */
92 throw new Error(
93 __(
94 'Unable to parse response. Find troubleshooting steps at https://elasticpress.zendesk.com/hc/en-us/articles/20857557098125/.',
95 'elasticpress',
96 ),
97 );
98 }
99 },
100 [],
101 );
102
103 const onComplete = useCallback(
104 /**
105 * Handle completion of the request, whether successful or not.
106 *
107 * @returns {void}
108 */
109 () => {
110 request.current = null;
111 },
112 [],
113 );
114
115 const sendRequest = useCallback(
116 /**
117 * Send AJAX request.
118 *
119 * Silently catches abort errors and clears the current request on
120 * completion.
121 *
122 * @param {URL} url API URL.
123 * @param {object} options Request options.
124 * @throws {Error} Any non-abort errors.
125 * @returns {Promise} Current request promise.
126 */
127 (url, options) => {
128 request.current = fetch(url, options).then(onResponse).finally(onComplete);
129
130 return request.current;
131 },
132 [onComplete, onResponse],
133 );
134
135 const cancelIndex = useCallback(
136 /**
137 * Send a request to cancel sync.
138 *
139 * @returns {Promise} Fetch request promise.
140 */
141 async () => {
142 abort.current.abort();
143 abort.current = new AbortController();
144
145 const url = new URL(apiUrl);
146
147 const options = {
148 headers: {
149 'X-WP-Nonce': nonce,
150 },
151 method: 'DELETE',
152 signal: abort.current.signal,
153 };
154
155 return sendRequest(url, options);
156 },
157 [apiUrl, nonce, sendRequest],
158 );
159
160 const index = useCallback(
161 /**
162 * Send a request to sync.
163 *
164 * @param {object} args Sync args.
165 * @returns {Promise} Fetch request promise.
166 */
167 async (args) => {
168 abort.current.abort();
169 abort.current = new AbortController();
170
171 const url = new URL(apiUrl);
172
173 Object.keys(args).forEach((arg) => {
174 if (args[arg]) {
175 url.searchParams.append(arg, args[arg]);
176 }
177 });
178
179 const options = {
180 headers: {
181 'X-WP-Nonce': nonce,
182 },
183 method: 'POST',
184 signal: abort.current.signal,
185 };
186
187 return sendRequest(url, options);
188 },
189 [apiUrl, nonce, sendRequest],
190 );
191
192 const indexStatus = useCallback(
193 /**
194 * Send a request for CLI sync status.
195 *
196 * @returns {Promise} Fetch request promise.
197 */
198 async () => {
199 abort.current.abort();
200 abort.current = new AbortController();
201
202 const url = new URL(apiUrl);
203
204 const options = {
205 headers: {
206 'X-WP-Nonce': nonce,
207 },
208 method: 'GET',
209 signal: abort.current.signal,
210 };
211
212 return sendRequest(url, options);
213 },
214 [apiUrl, nonce, sendRequest],
215 );
216
217 return { cancelIndex, index, indexStatus };
218 };
219