@xh/hoist 67.0.0-SNAPSHOT.1723668375053 → 67.0.0-SNAPSHOT.1723839594443
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 +11 -6
- package/build/types/core/XH.d.ts +3 -2
- package/build/types/core/exception/Exception.d.ts +3 -3
- package/build/types/core/types/Interfaces.d.ts +1 -13
- package/build/types/promise/Promise.d.ts +0 -5
- package/build/types/svc/FetchService.d.ts +67 -26
- package/core/XH.ts +5 -4
- package/core/exception/Exception.ts +9 -4
- package/core/types/Interfaces.ts +1 -14
- package/package.json +3 -3
- package/promise/Promise.ts +1 -7
- package/security/BaseOAuthClient.ts +1 -2
- package/svc/FetchService.ts +156 -88
- package/svc/TrackService.ts +4 -2
- package/tsconfig.tsbuildinfo +1 -1
package/svc/FetchService.ts
CHANGED
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
import {
|
|
8
8
|
Awaitable,
|
|
9
9
|
Exception,
|
|
10
|
-
FetchResponse,
|
|
11
10
|
HoistService,
|
|
12
11
|
LoadSpec,
|
|
13
12
|
PlainObject,
|
|
13
|
+
TrackOptions,
|
|
14
14
|
XH
|
|
15
15
|
} from '@xh/hoist/core';
|
|
16
16
|
import {PromiseTimeoutSpec} from '@xh/hoist/promise';
|
|
17
17
|
import {isLocalDate, SECONDS} from '@xh/hoist/utils/datetime';
|
|
18
|
-
import {apiDeprecated} from '@xh/hoist/utils/js';
|
|
18
|
+
import {apiDeprecated, warnIf} from '@xh/hoist/utils/js';
|
|
19
19
|
import {StatusCodes} from 'http-status-codes';
|
|
20
20
|
import {isDate, isFunction, isNil, isObject, isString, omit, omitBy} from 'lodash';
|
|
21
21
|
import {IStringifyOptions, stringify} from 'qs';
|
|
@@ -45,7 +45,11 @@ export class FetchService extends HoistService {
|
|
|
45
45
|
NO_JSON_RESPONSES = [StatusCodes.NO_CONTENT, StatusCodes.RESET_CONTENT];
|
|
46
46
|
|
|
47
47
|
private autoAborters = {};
|
|
48
|
-
|
|
48
|
+
private _defaultHeaders: DefaultHeaders[] = [];
|
|
49
|
+
private _interceptors: FetchInterceptor[] = [];
|
|
50
|
+
//-----------------------------------
|
|
51
|
+
// Public properties, Getters/Setters
|
|
52
|
+
//------------------------------------
|
|
49
53
|
/** True to auto-generate a Correlation ID for each request unless otherwise specified. */
|
|
50
54
|
autoGenCorrelationIds = false;
|
|
51
55
|
|
|
@@ -53,97 +57,87 @@ export class FetchService extends HoistService {
|
|
|
53
57
|
* Method for generating Correlation ID's. Defaults to `XH.genUUID()` but can be modified
|
|
54
58
|
* by applications looking to customize the format of their Correlation ID's.
|
|
55
59
|
*/
|
|
56
|
-
genCorrelationId = () => XH.genUUID();
|
|
60
|
+
genCorrelationId: () => string = () => XH.genUUID();
|
|
57
61
|
|
|
58
62
|
/** Request header name to be used for Correlation ID tracking. */
|
|
59
63
|
correlationIdHeaderKey: string = 'X-Correlation-ID';
|
|
60
64
|
|
|
61
|
-
/**
|
|
62
|
-
* Timeout to be used for all requests made via this service that do not themselves spec a
|
|
63
|
-
* custom timeout.
|
|
64
|
-
*/
|
|
65
|
+
/** Default timeout to be used for all requests made via this service */
|
|
65
66
|
defaultTimeout: PromiseTimeoutSpec = 30 * SECONDS;
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
get defaultHeaders(): Array<PlainObject | ((arg: FetchOptions) => Awaitable<PlainObject>)> {
|
|
68
|
+
/** Default headers to be sent with all subsequent requests. */
|
|
69
|
+
get defaultHeaders(): DefaultHeaders[] {
|
|
71
70
|
return this._defaultHeaders;
|
|
72
71
|
}
|
|
73
72
|
|
|
74
73
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
74
|
+
* Promise handlers to be executed before fufilling or rejecting returned Promise.
|
|
75
|
+
*
|
|
76
|
+
* Use the `onRejected` handler for apps requiring common handling for particular exceptions.
|
|
77
|
+
* Useful for recognizing 401s (i.e. session end), or wrapping, logging, or enhancing exceptions.
|
|
78
|
+
* The simplest onRejected handler will simply rethrow the passed exception, or a wrapped version of it.
|
|
79
|
+
* Such handlers may also return `never()` to prevent further processing of the request -- this
|
|
80
|
+
* is useful, i.e. if the handler is going to redirect the entire app, or otherwise end normal
|
|
81
|
+
* app processing. Rejected handlers may also be able to retry and return valid results via
|
|
82
|
+
* another call to fetch.
|
|
83
|
+
*
|
|
84
|
+
* Use the `onFulfilled` hander for enhancing, tracking, or even rejecting "successful" returns.
|
|
85
|
+
* For example, a handler of this form could be used to transform a 200 response returned by
|
|
86
|
+
* an API with an "error" flag into a proper client-side exception.
|
|
78
87
|
*/
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
this.addDefaultHeaders(headers);
|
|
88
|
+
addInterceptor(handler: FetchInterceptor) {
|
|
89
|
+
this._interceptors.push(handler);
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
/**
|
|
85
93
|
* Add default headers to be sent with all subsequent requests.
|
|
86
94
|
* @param headers - to be sent with all fetch requests, or a function to generate.
|
|
87
95
|
*/
|
|
88
|
-
addDefaultHeaders(headers:
|
|
96
|
+
addDefaultHeaders(headers: DefaultHeaders) {
|
|
89
97
|
this._defaultHeaders.push(headers);
|
|
90
98
|
}
|
|
91
99
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
* @deprecated modify `defaultTimeout` directly instead.
|
|
96
|
-
*/
|
|
97
|
-
setDefaultTimeout(timeout: PromiseTimeoutSpec) {
|
|
98
|
-
apiDeprecated('setDefaultTimeout', {
|
|
99
|
-
v: '68',
|
|
100
|
-
msg: 'Modify `defaultTimeout` directly instead.'
|
|
101
|
-
});
|
|
102
|
-
this.defaultTimeout = timeout;
|
|
103
|
-
}
|
|
104
|
-
|
|
100
|
+
//--------------------
|
|
101
|
+
// Main Entry Points
|
|
102
|
+
//--------------------
|
|
105
103
|
/**
|
|
106
104
|
* Send a request via the underlying fetch API.
|
|
107
|
-
*
|
|
105
|
+
*
|
|
106
|
+
* This is the main entry point for this API, and can be used to satisfy all
|
|
107
|
+
* requests. Other shortcut variants will delegate to this method, after setting
|
|
108
|
+
* default options and pre-processing content.
|
|
109
|
+
*
|
|
110
|
+
* Set `asJson` to true return a parsed JSON result, rather than the raw Response.
|
|
111
|
+
* Note that shortcut variant of this method (e.g. `fetchJson`, `postJson`) will set this
|
|
112
|
+
* flag for you.
|
|
113
|
+
*
|
|
114
|
+
* @returns Promise which resolves to a Response or JSON.
|
|
108
115
|
*/
|
|
109
|
-
fetch(opts: FetchOptions): Promise<
|
|
110
|
-
|
|
111
|
-
const ret = this.withDefaultHeadersAsync(opts).then(opts => this.managedFetchAsync(opts));
|
|
112
|
-
ret.correlationId = opts.correlationId as string;
|
|
113
|
-
return ret;
|
|
116
|
+
async fetch(opts: FetchOptions): Promise<any> {
|
|
117
|
+
return this.fetchInternalAsync(opts);
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
/**
|
|
117
121
|
* Send an HTTP request and decode the response as JSON.
|
|
118
122
|
* @returns the decoded JSON object, or null if the response has status in {@link NO_JSON_RESPONSES}.
|
|
119
123
|
*/
|
|
120
|
-
fetchJson(opts: FetchOptions): Promise<any> {
|
|
121
|
-
|
|
122
|
-
const ret = this.withDefaultHeadersAsync(opts, {Accept: 'application/json'}).then(opts =>
|
|
123
|
-
this.managedFetchAsync(opts, async r => {
|
|
124
|
-
if (this.NO_JSON_RESPONSES.includes(r.status)) return null;
|
|
125
|
-
return r.json().catchWhen('SyntaxError', e => {
|
|
126
|
-
throw Exception.fetchJsonParseError(opts, e);
|
|
127
|
-
});
|
|
128
|
-
})
|
|
129
|
-
);
|
|
130
|
-
ret.correlationId = opts.correlationId as string;
|
|
131
|
-
return ret;
|
|
124
|
+
async fetchJson(opts: FetchOptions): Promise<any> {
|
|
125
|
+
return this.fetchInternalAsync({asJson: true, ...opts});
|
|
132
126
|
}
|
|
133
127
|
|
|
134
128
|
/**
|
|
135
129
|
* Send a GET request and decode the response as JSON.
|
|
136
130
|
* @returns the decoded JSON object, or null if the response status is in {@link NO_JSON_RESPONSES}.
|
|
137
131
|
*/
|
|
138
|
-
getJson(opts: FetchOptions): Promise<any> {
|
|
139
|
-
return this.
|
|
132
|
+
async getJson(opts: FetchOptions): Promise<any> {
|
|
133
|
+
return this.fetchInternalAsync({asJson: true, method: 'GET', ...opts});
|
|
140
134
|
}
|
|
141
135
|
|
|
142
136
|
/**
|
|
143
137
|
* Send a POST request with a JSON body and decode the response as JSON.
|
|
144
138
|
* @returns the decoded JSON object, or null if the response status is in {@link NO_JSON_RESPONSES}.
|
|
145
139
|
*/
|
|
146
|
-
postJson(opts: FetchOptions): Promise<any> {
|
|
140
|
+
async postJson(opts: FetchOptions): Promise<any> {
|
|
147
141
|
return this.sendJsonInternalAsync({method: 'POST', ...opts});
|
|
148
142
|
}
|
|
149
143
|
|
|
@@ -151,7 +145,7 @@ export class FetchService extends HoistService {
|
|
|
151
145
|
* Send a PUT request with a JSON body and decode the response as JSON.
|
|
152
146
|
* @returns the decoded JSON object, or null if the response status is in {@link NO_JSON_RESPONSES}.
|
|
153
147
|
*/
|
|
154
|
-
putJson(opts: FetchOptions): Promise<any> {
|
|
148
|
+
async putJson(opts: FetchOptions): Promise<any> {
|
|
155
149
|
return this.sendJsonInternalAsync({method: 'PUT', ...opts});
|
|
156
150
|
}
|
|
157
151
|
|
|
@@ -159,7 +153,7 @@ export class FetchService extends HoistService {
|
|
|
159
153
|
* Send a PATCH request with a JSON body and decode the response as JSON.
|
|
160
154
|
* @returns the decoded JSON object, or null if the response status is in {@link NO_JSON_RESPONSES}.
|
|
161
155
|
*/
|
|
162
|
-
patchJson(opts: FetchOptions): Promise<any> {
|
|
156
|
+
async patchJson(opts: FetchOptions): Promise<any> {
|
|
163
157
|
return this.sendJsonInternalAsync({method: 'PATCH', ...opts});
|
|
164
158
|
}
|
|
165
159
|
|
|
@@ -167,7 +161,7 @@ export class FetchService extends HoistService {
|
|
|
167
161
|
* Send a DELETE request with optional JSON body and decode the optional response as JSON.
|
|
168
162
|
* @returns the decoded JSON object, or null if the response status is in {@link NO_JSON_RESPONSES}.
|
|
169
163
|
*/
|
|
170
|
-
deleteJson(opts: FetchOptions): Promise<any> {
|
|
164
|
+
async deleteJson(opts: FetchOptions): Promise<any> {
|
|
171
165
|
return this.sendJsonInternalAsync({method: 'DELETE', ...opts});
|
|
172
166
|
}
|
|
173
167
|
|
|
@@ -186,11 +180,75 @@ export class FetchService extends HoistService {
|
|
|
186
180
|
return true;
|
|
187
181
|
}
|
|
188
182
|
|
|
183
|
+
//-------------
|
|
184
|
+
// Deprecations
|
|
185
|
+
//-------------
|
|
186
|
+
/**
|
|
187
|
+
* Set the timeout (default 30 seconds) to be used for all requests made via this service that
|
|
188
|
+
* do not themselves spec a custom timeout.
|
|
189
|
+
* @deprecated modify `defaultTimeout` directly instead.
|
|
190
|
+
*/
|
|
191
|
+
setDefaultTimeout(timeout: PromiseTimeoutSpec) {
|
|
192
|
+
apiDeprecated('setDefaultTimeout', {
|
|
193
|
+
v: '68',
|
|
194
|
+
msg: 'Modify `defaultTimeout` directly instead.'
|
|
195
|
+
});
|
|
196
|
+
this.defaultTimeout = timeout;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Set default headers to be sent with all subsequent requests.
|
|
201
|
+
* @param headers - to be sent with all fetch requests, or a function to generate.
|
|
202
|
+
* @deprecated use addDefaultHeaders instead.
|
|
203
|
+
*/
|
|
204
|
+
setDefaultHeaders(headers: DefaultHeaders) {
|
|
205
|
+
apiDeprecated('setDefaultHeaders', {v: '66', msg: 'Use addDefaultHeaders instead'});
|
|
206
|
+
this.addDefaultHeaders(headers);
|
|
207
|
+
}
|
|
208
|
+
|
|
189
209
|
//-----------------------
|
|
190
210
|
// Implementation
|
|
191
211
|
//-----------------------
|
|
212
|
+
private async fetchInternalAsync(opts: FetchOptions): Promise<any> {
|
|
213
|
+
opts = this.withCorrelationId(opts);
|
|
214
|
+
opts = await this.withDefaultHeadersAsync(opts);
|
|
215
|
+
let ret = this.managedFetchAsync(opts);
|
|
216
|
+
|
|
217
|
+
// Apply tracking
|
|
218
|
+
const {correlationId, loadSpec, track} = opts;
|
|
219
|
+
if (track) {
|
|
220
|
+
const trackOptions = isString(track) ? {message: track} : track;
|
|
221
|
+
warnIf(
|
|
222
|
+
trackOptions.correlationId || trackOptions.loadSpec,
|
|
223
|
+
'Neither Correlation ID nor LoadSpec should be set in `FetchOptions.track`. Use `FetchOptions` top-level properties instead.'
|
|
224
|
+
);
|
|
225
|
+
ret = ret.track({...trackOptions, correlationId: correlationId as string, loadSpec});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Apply interceptors
|
|
229
|
+
for (const interceptor of this._interceptors) {
|
|
230
|
+
ret = ret.then(
|
|
231
|
+
value => interceptor.onFulfilled(opts, value),
|
|
232
|
+
cause => interceptor.onRejected(opts, cause)
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return ret;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private sendJsonInternalAsync(opts: FetchOptions) {
|
|
240
|
+
return this.fetchInternalAsync({
|
|
241
|
+
asJson: true,
|
|
242
|
+
...opts,
|
|
243
|
+
body: JSON.stringify(opts.body),
|
|
244
|
+
headers: {
|
|
245
|
+
'Content-Type': 'application/json',
|
|
246
|
+
...opts.headers
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
}
|
|
192
250
|
|
|
193
|
-
|
|
251
|
+
// Resolve convenience options for Correlation ID to server-ready string
|
|
194
252
|
private withCorrelationId(opts: FetchOptions): FetchOptions {
|
|
195
253
|
const {correlationId} = opts;
|
|
196
254
|
if (isString(correlationId)) return opts;
|
|
@@ -201,10 +259,7 @@ export class FetchService extends HoistService {
|
|
|
201
259
|
return opts;
|
|
202
260
|
}
|
|
203
261
|
|
|
204
|
-
private async withDefaultHeadersAsync(
|
|
205
|
-
opts: FetchOptions,
|
|
206
|
-
extraHeaders: PlainObject = null
|
|
207
|
-
): Promise<FetchOptions> {
|
|
262
|
+
private async withDefaultHeadersAsync(opts: FetchOptions): Promise<FetchOptions> {
|
|
208
263
|
const method = opts.method ?? (opts.params ? 'POST' : 'GET'),
|
|
209
264
|
isPost = method === 'POST';
|
|
210
265
|
|
|
@@ -216,7 +271,7 @@ export class FetchService extends HoistService {
|
|
|
216
271
|
const headers = {
|
|
217
272
|
'Content-Type': isPost ? 'application/x-www-form-urlencoded' : 'text/plain',
|
|
218
273
|
...defaultHeaders,
|
|
219
|
-
...
|
|
274
|
+
...(opts.asJson ? {Accept: 'application/json'} : {}),
|
|
220
275
|
...opts.headers
|
|
221
276
|
};
|
|
222
277
|
|
|
@@ -234,10 +289,7 @@ export class FetchService extends HoistService {
|
|
|
234
289
|
return {...opts, method, headers};
|
|
235
290
|
}
|
|
236
291
|
|
|
237
|
-
private async managedFetchAsync(
|
|
238
|
-
opts: FetchOptions,
|
|
239
|
-
postProcess: (r: FetchResponse) => Awaitable<FetchResponse> = null
|
|
240
|
-
): Promise<FetchResponse> {
|
|
292
|
+
private async managedFetchAsync(opts: FetchOptions): Promise<any> {
|
|
241
293
|
// Prepare auto-aborter
|
|
242
294
|
const {autoAborters, defaultTimeout} = this,
|
|
243
295
|
{autoAbortKey, timeout = defaultTimeout} = opts,
|
|
@@ -250,7 +302,9 @@ export class FetchService extends HoistService {
|
|
|
250
302
|
}
|
|
251
303
|
|
|
252
304
|
try {
|
|
253
|
-
return await this.
|
|
305
|
+
return await this.abortableFetchAsync(opts, aborter)
|
|
306
|
+
.then(opts.asJson ? r => this.parseJsonAsync(opts, r) : null)
|
|
307
|
+
.timeout(timeout);
|
|
254
308
|
} catch (e) {
|
|
255
309
|
if (e.isTimeout) {
|
|
256
310
|
aborter.abort();
|
|
@@ -261,12 +315,13 @@ export class FetchService extends HoistService {
|
|
|
261
315
|
throw Exception.fetchTimeout(opts, e, msg);
|
|
262
316
|
}
|
|
263
317
|
|
|
264
|
-
if (e.isHoistException)
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
318
|
+
if (!e.isHoistException) {
|
|
319
|
+
// Just two other cases where we expect this to *throw* -- Typically we get a fail status
|
|
320
|
+
throw e.name === 'AbortError'
|
|
321
|
+
? Exception.fetchAborted(opts, e)
|
|
322
|
+
: Exception.serverUnavailable(opts, e);
|
|
323
|
+
}
|
|
324
|
+
throw e;
|
|
270
325
|
} finally {
|
|
271
326
|
if (autoAborters[autoAbortKey] === aborter) {
|
|
272
327
|
delete autoAborters[autoAbortKey];
|
|
@@ -274,10 +329,10 @@ export class FetchService extends HoistService {
|
|
|
274
329
|
}
|
|
275
330
|
}
|
|
276
331
|
|
|
277
|
-
private async
|
|
332
|
+
private async abortableFetchAsync(
|
|
278
333
|
opts: FetchOptions,
|
|
279
334
|
aborter: AbortController
|
|
280
|
-
): Promise<
|
|
335
|
+
): Promise<Response> {
|
|
281
336
|
// 1) Prepare URL
|
|
282
337
|
let {url, method, headers, body, params} = opts,
|
|
283
338
|
isRelativeUrl = !url.startsWith('/') && !url.includes('//');
|
|
@@ -318,24 +373,17 @@ export class FetchService extends HoistService {
|
|
|
318
373
|
}
|
|
319
374
|
|
|
320
375
|
// 4) Await underlying fetch and post-process response.
|
|
321
|
-
const ret =
|
|
376
|
+
const ret = await fetch(url, fetchOpts);
|
|
322
377
|
|
|
323
|
-
if (!ret.ok)
|
|
324
|
-
ret.responseText = await this.safeResponseTextAsync(ret);
|
|
325
|
-
throw Exception.fetchError(opts, ret);
|
|
326
|
-
}
|
|
378
|
+
if (!ret.ok) throw Exception.fetchError(opts, ret, await this.safeResponseTextAsync(ret));
|
|
327
379
|
|
|
328
380
|
return ret;
|
|
329
381
|
}
|
|
330
382
|
|
|
331
|
-
private async
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
headers: {
|
|
336
|
-
'Content-Type': 'application/json',
|
|
337
|
-
...opts.headers
|
|
338
|
-
}
|
|
383
|
+
private async parseJsonAsync(opts: FetchOptions, r: Response): Promise<any> {
|
|
384
|
+
if (this.NO_JSON_RESPONSES.includes(r.status)) return null;
|
|
385
|
+
return r.json().catchWhen('SyntaxError', e => {
|
|
386
|
+
throw Exception.fetchJsonParseError(opts, e);
|
|
339
387
|
});
|
|
340
388
|
}
|
|
341
389
|
|
|
@@ -354,6 +402,15 @@ export class FetchService extends HoistService {
|
|
|
354
402
|
};
|
|
355
403
|
}
|
|
356
404
|
|
|
405
|
+
/** Headers to be applied to all requests. Specified as object, or dynamic function to create. */
|
|
406
|
+
export type DefaultHeaders = PlainObject | ((opts: FetchOptions) => Awaitable<PlainObject>);
|
|
407
|
+
|
|
408
|
+
/** Handlers to be executed before fufilling or rejecting any exception to caller. */
|
|
409
|
+
export interface FetchInterceptor {
|
|
410
|
+
onFulfilled: (opts: FetchOptions, value: any) => Promise<any>;
|
|
411
|
+
onRejected: (opts: FetchOptions, cause: unknown) => Promise<any>;
|
|
412
|
+
}
|
|
413
|
+
|
|
357
414
|
/**
|
|
358
415
|
* Standard options to pass through to fetch, with some additions.
|
|
359
416
|
* See MDN for available options - {@link https://developer.mozilla.org/en-US/docs/Web/API/Request}.
|
|
@@ -420,4 +477,15 @@ export interface FetchOptions {
|
|
|
420
477
|
* aborted in favor of the new request.
|
|
421
478
|
*/
|
|
422
479
|
autoAbortKey?: string;
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* True to decode the HTTP response as JSON. Default false.
|
|
483
|
+
*/
|
|
484
|
+
asJson?: boolean;
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* If set, the request will be tracked via Hoist activity tracking. (Do not set `correlationId`
|
|
488
|
+
* here - use the top-level `correlationId` property instead.)
|
|
489
|
+
*/
|
|
490
|
+
track?: string | TrackOptions;
|
|
423
491
|
}
|
package/svc/TrackService.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import {HoistService, TrackOptions, XH} from '@xh/hoist/core';
|
|
8
8
|
import {isOmitted} from '@xh/hoist/utils/impl';
|
|
9
9
|
import {stripTags, withDefault} from '@xh/hoist/utils/js';
|
|
10
|
-
import {isString} from 'lodash';
|
|
10
|
+
import {isNil, isString} from 'lodash';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Primary service for tracking any activity that an application's admins want to track.
|
|
@@ -104,7 +104,9 @@ export class TrackService extends HoistService {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const elapsedStr = query.elapsed != null ? `${query.elapsed}ms` : null,
|
|
107
|
-
consoleMsgs = [query.category, query.msg, elapsedStr].filter(
|
|
107
|
+
consoleMsgs = [query.category, query.msg, query.correlationId, elapsedStr].filter(
|
|
108
|
+
it => !isNil(it)
|
|
109
|
+
);
|
|
108
110
|
|
|
109
111
|
this.logInfo(...consoleMsgs);
|
|
110
112
|
|