@adobe/spacecat-shared-utils 1.22.2 → 1.22.3

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 CHANGED
@@ -1,3 +1,10 @@
1
+ # [@adobe/spacecat-shared-utils-v1.22.3](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.22.2...@adobe/spacecat-shared-utils-v1.22.3) (2024-11-07)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * proper http data for x-ray trace ([#427](https://github.com/adobe/spacecat-shared/issues/427)) ([4fa4d2d](https://github.com/adobe/spacecat-shared/commit/4fa4d2d417d51e911957e37df5136453dea521b2))
7
+
1
8
  # [@adobe/spacecat-shared-utils-v1.22.2](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.22.1...@adobe/spacecat-shared-utils-v1.22.2) (2024-11-07)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-utils",
3
- "version": "1.22.2",
3
+ "version": "1.22.3",
4
4
  "description": "Shared modules of the Spacecat Services - utils",
5
5
  "type": "module",
6
6
  "engines": {
@@ -9,35 +9,140 @@
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
13
  import AWSXRay from 'aws-xray-sdk';
13
14
 
14
15
  import { fetch as adobeFetch } from './adobe-fetch.js';
16
+ import { isNumber } from './functions.js';
15
17
 
16
- export async function tracingFetch(url, options = {}) {
18
+ /**
19
+ * Creates a subsegment for a given hostname based on whether the parent segment is traced or not.
20
+ * @param {Object} parentSegment - The parent X-Ray segment.
21
+ * @param {string} hostname - The hostname to associate with the subsegment.
22
+ * @returns {Object} The created subsegment.
23
+ */
24
+ const createSubsegment = (parentSegment, hostname) => (parentSegment.notTraced
25
+ ? parentSegment.addNewSubsegmentWithoutSampling(hostname)
26
+ : parentSegment.addNewSubsegment(hostname));
27
+
28
+ /**
29
+ * Sets the AWS X-Ray trace headers on the request object.
30
+ * @param {Request} request - The request object on which headers are set.
31
+ * @param {Object} parentSegment - The parent X-Ray segment.
32
+ * @param {Object} subSegment - The subsegment to include in the headers.
33
+ */
34
+ const setTraceHeaders = (request, parentSegment, subSegment) => {
35
+ const effectiveParentSegment = parentSegment.segment || parentSegment;
36
+ request.headers.set(
37
+ 'X-Amzn-Trace-Id',
38
+ `Root=${effectiveParentSegment.trace_id};Parent=${subSegment.id};Sampled=0`,
39
+ );
40
+ };
41
+
42
+ /**
43
+ * Adds flags to the given subsegment based on the status code of the response.
44
+ * @param {Object} subSegment - The X-Ray subsegment to which flags are added.
45
+ * @param {number} status - The status code of the response.
46
+ */
47
+ const setSubSegmentFlagsByStatusCode = (
48
+ subSegment,
49
+ status,
50
+ ) => { /* eslint-disable no-param-reassign */
51
+ if (status === 429) {
52
+ subSegment.throttled = true;
53
+ return;
54
+ }
55
+ if (status >= 400 && status < 500) {
56
+ subSegment.error = true;
57
+ return;
58
+ }
59
+ if (status >= 500 && status < 600) {
60
+ subSegment.fault = true;
61
+ }
62
+ };
63
+
64
+ /**
65
+ * Adds request and response data to the given segment for AWS X-Ray tracing.
66
+ * @param {Object} segment - The X-Ray segment to which request and response data are added.
67
+ * @param {Request} request - The original request object.
68
+ * @param {Response} [response] - The response object (if available).
69
+ */
70
+ const addFetchRequestDataToSegment = (
71
+ segment,
72
+ request,
73
+ response,
74
+ ) => { /* eslint-disable no-param-reassign */
75
+ const { url, method } = request;
76
+ segment.http = {
77
+ request: { url, method },
78
+ };
79
+
80
+ if (!response) {
81
+ return;
82
+ }
83
+
84
+ segment.http.response = {
85
+ status: response.status,
86
+ };
87
+
88
+ const contentLength = Number.parseInt(response.headers.get('content-length'), 10);
89
+ if (isNumber(contentLength)) {
90
+ segment.http.response.content_length = contentLength;
91
+ }
92
+ };
93
+
94
+ /**
95
+ * Adds error data to the given segment for AWS X-Ray tracing.
96
+ * @param {Object} subSegment - The X-Ray subsegment to which error data is added.
97
+ * @param {Request} request - The original request object.
98
+ * @param {Error} error - The error object.
99
+ */
100
+ const handleSubSegmentError = (subSegment, request, error) => {
101
+ subSegment.addErrorFlag();
102
+ addFetchRequestDataToSegment(subSegment, request);
103
+ subSegment.addAnnotation('errorMessage', error.message);
104
+ subSegment.addAnnotation('errorStack', error.stack);
105
+ subSegment.close(error);
106
+ };
107
+
108
+ /**
109
+ * Performs a fetch request and adds AWS X-Ray tracing, including request/response tracking.
110
+ * @param {string} url - The URL for the request.
111
+ * @param {Object} options - Options to be passed to the fetch call.
112
+ * @returns {Promise<Response>} The response from the fetch request.
113
+ */
114
+ export async function tracingFetch(url, options) {
17
115
  const parentSegment = AWSXRay.getSegment();
18
116
 
19
117
  if (!parentSegment) {
20
118
  return adobeFetch(url, options);
21
119
  }
22
120
 
23
- const method = options.method || 'GET';
24
- const subsegment = parentSegment.addNewSubsegment(`HTTP ${method} ${url}`);
121
+ const request = new Request(url, options);
122
+ const { hostname } = new URL(request.url);
123
+ const subSegment = createSubsegment(parentSegment, hostname);
25
124
 
26
- try {
27
- subsegment.addAnnotation('url', url);
28
- subsegment.addAnnotation('method', method);
125
+ subSegment.namespace = 'remote';
29
126
 
30
- const response = await adobeFetch(url, options);
127
+ if (!parentSegment.noOp) {
128
+ setTraceHeaders(request, parentSegment, subSegment);
129
+ }
31
130
 
32
- subsegment.addMetadata('statusCode', response.status);
131
+ const capturedAdobeFetch = async () => {
132
+ let response = null;
133
+ try {
134
+ response = await adobeFetch(request);
135
+ } catch (e) {
136
+ handleSubSegmentError(subSegment, request, e);
137
+ throw e;
138
+ }
33
139
 
34
- subsegment.close();
140
+ setSubSegmentFlagsByStatusCode(subSegment, response.status);
35
141
 
142
+ addFetchRequestDataToSegment(subSegment, request, response);
143
+ subSegment.close();
36
144
  return response;
37
- } catch (error) {
38
- subsegment.addError(error);
39
- subsegment.close();
145
+ };
40
146
 
41
- throw error;
42
- }
147
+ return capturedAdobeFetch();
43
148
  }