@adobe/spacecat-shared-utils 1.34.0 → 1.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/tracing-fetch.js +63 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-utils-v1.35.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.34.0...@adobe/spacecat-shared-utils-v1.35.0) (2025-03-06)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* tracing fetch timeout ([#657](https://github.com/adobe/spacecat-shared/issues/657)) ([67335e3](https://github.com/adobe/spacecat-shared/commit/67335e32b7cceeb500ca58dc05518876589a108a))
|
|
7
|
+
|
|
1
8
|
# [@adobe/spacecat-shared-utils-v1.34.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.33.3...@adobe/spacecat-shared-utils-v1.34.0) (2025-03-05)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/src/tracing-fetch.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
|
-
import { Request } from '@adobe/fetch';
|
|
12
|
+
import { Request, timeoutSignal, AbortError } from '@adobe/fetch';
|
|
13
13
|
import AWSXRay from 'aws-xray-sdk';
|
|
14
14
|
|
|
15
15
|
import { fetch as adobeFetch } from './adobe-fetch.js';
|
|
@@ -107,17 +107,60 @@ const handleSubSegmentError = (subSegment, request, error) => {
|
|
|
107
107
|
subSegment.close(error);
|
|
108
108
|
};
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Creates a timeout error with a consistent format
|
|
112
|
+
* @param {number} timeout - The timeout value in milliseconds
|
|
113
|
+
* @returns {Error} A formatted timeout error
|
|
114
|
+
*/
|
|
115
|
+
const createTimeoutError = (timeout) => {
|
|
116
|
+
const timeoutError = new Error(`Request timeout after ${timeout}ms`);
|
|
117
|
+
timeoutError.code = 'ETIMEOUT';
|
|
118
|
+
return timeoutError;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Performs a fetch request with timeout handling
|
|
123
|
+
* @param {string|Request} resource - The resource to fetch
|
|
124
|
+
* @param {Object} options - Options for the fetch call
|
|
125
|
+
* @param {Object} signal - The timeout signal
|
|
126
|
+
* @returns {Promise<Response>} The fetch response
|
|
127
|
+
*/
|
|
128
|
+
const fetchWithTimeout = async (resource, options, signal) => {
|
|
129
|
+
try {
|
|
130
|
+
const fetchOptions = { ...options, signal };
|
|
131
|
+
return await adobeFetch(resource, fetchOptions);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (error instanceof AbortError) {
|
|
134
|
+
// Extract timeout from signal (implementation detail, but necessary)
|
|
135
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
136
|
+
const timeout = signal._ms || 10000;
|
|
137
|
+
throw createTimeoutError(timeout);
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
} finally {
|
|
141
|
+
signal.clear(); // Clean up the signal
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
110
145
|
/**
|
|
111
146
|
* Performs a fetch request and adds AWS X-Ray tracing, including request/response tracking.
|
|
112
147
|
* @param {string} url - The URL for the request.
|
|
113
|
-
* @param {Object} options -
|
|
148
|
+
* @param {Object} [options] - Optional options to be passed to the fetch call.
|
|
149
|
+
* @param {number} [options.timeout=10000] - Timeout in milliseconds (defaults to 10 seconds).
|
|
114
150
|
* @returns {Promise<Response>} The response from the fetch request.
|
|
115
151
|
*/
|
|
116
152
|
export async function tracingFetch(url, options) {
|
|
117
153
|
const parentSegment = AWSXRay.getSegment();
|
|
118
154
|
|
|
119
|
-
options = isObject(options) ? options : {};
|
|
120
|
-
options.headers = isObject(options.headers) ? options.headers :
|
|
155
|
+
options = isObject(options) ? { ...options } : {};
|
|
156
|
+
options.headers = isObject(options.headers) ? options.headers : { };
|
|
157
|
+
|
|
158
|
+
// Set default timeout of 10 seconds if not specified
|
|
159
|
+
const timeout = options.timeout || 10000;
|
|
160
|
+
delete options.timeout; // Remove from options as we'll handle it separately
|
|
161
|
+
|
|
162
|
+
// Create a timeout signal
|
|
163
|
+
const signal = timeoutSignal(timeout);
|
|
121
164
|
|
|
122
165
|
// find user-agent header in headers case insensitively
|
|
123
166
|
let hasUserAgent = false;
|
|
@@ -131,10 +174,12 @@ export async function tracingFetch(url, options) {
|
|
|
131
174
|
options.headers['User-Agent'] = SPACECAT_USER_AGENT;
|
|
132
175
|
}
|
|
133
176
|
|
|
177
|
+
// If no parent segment, perform fetch without tracing
|
|
134
178
|
if (!parentSegment) {
|
|
135
|
-
return
|
|
179
|
+
return fetchWithTimeout(url, options, signal);
|
|
136
180
|
}
|
|
137
181
|
|
|
182
|
+
// With parent segment, create subsegment and add tracing
|
|
138
183
|
const request = new Request(url, options);
|
|
139
184
|
const { hostname } = new URL(request.url);
|
|
140
185
|
const subSegment = createSubsegment(parentSegment, hostname);
|
|
@@ -145,21 +190,22 @@ export async function tracingFetch(url, options) {
|
|
|
145
190
|
setTraceHeaders(request, parentSegment, subSegment);
|
|
146
191
|
}
|
|
147
192
|
|
|
148
|
-
|
|
149
|
-
let response = null;
|
|
150
|
-
try {
|
|
151
|
-
response = await adobeFetch(request);
|
|
152
|
-
} catch (e) {
|
|
153
|
-
handleSubSegmentError(subSegment, request, e);
|
|
154
|
-
throw e;
|
|
155
|
-
}
|
|
193
|
+
subSegment.addAnnotation('timeout_ms', timeout);
|
|
156
194
|
|
|
157
|
-
|
|
195
|
+
try {
|
|
196
|
+
// Create a new request with the signal
|
|
197
|
+
const requestWithSignal = new Request(request, { signal });
|
|
198
|
+
|
|
199
|
+
// Use the same fetchWithTimeout function but catch errors to handle subsegment
|
|
200
|
+
const response = await fetchWithTimeout(requestWithSignal, { }, signal);
|
|
158
201
|
|
|
202
|
+
setSubSegmentFlagsByStatusCode(subSegment, response.status);
|
|
159
203
|
addFetchRequestDataToSegment(subSegment, request, response);
|
|
160
204
|
subSegment.close();
|
|
161
|
-
return response;
|
|
162
|
-
};
|
|
163
205
|
|
|
164
|
-
|
|
206
|
+
return response;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
handleSubSegmentError(subSegment, request, error);
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
165
211
|
}
|