@trackunit/react-core-contexts 2.7.3 → 2.7.5-alpha-3e7014314a8.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/index.cjs.js +7 -68
- package/index.esm.js +9 -70
- package/package.json +8 -8
- package/src/createApolloClient.d.ts +1 -2
- package/src/errorLink/errorLink.d.ts +1 -2
- package/src/errorLink/subscriptionErrorLink.d.ts +2 -4
- package/migrations/entry.js.map +0 -1
- package/src/errorLink/missingScopeErrors.d.ts +0 -16
package/index.cjs.js
CHANGED
|
@@ -40,25 +40,10 @@ const FeatureFlagProviderIrisApp = ({ children }) => {
|
|
|
40
40
|
return jsxRuntime.jsx(reactCoreContextsApi.FeatureFlagContextProvider, { value: featureFlags, children: children });
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
/**
|
|
44
|
-
* Picks out the messages that report a missing authorization scope.
|
|
45
|
-
*
|
|
46
|
-
* The server offers no structured signal to key off: `verifyScopes`
|
|
47
|
-
* (libs/graphql/common/src/schemabuilder/AuthorizationDirective.ts) throws a plain
|
|
48
|
-
* `ForbiddenError` whose message is assembled as "Missing " + the allowed scopes joined with
|
|
49
|
-
* " or " + " scope". Matching on that shape is therefore the only option available, and it
|
|
50
|
-
* lives here so the HTTP and subscription links cannot drift apart on what counts as a
|
|
51
|
-
* missing scope.
|
|
52
|
-
*
|
|
53
|
-
* Filtering rather than inspecting the first error matters in both links: a response can
|
|
54
|
-
* carry several errors in any order, since the directive throws per resolved field.
|
|
55
|
-
*/
|
|
56
|
-
const getMissingScopeMessages = (errors) => errors.map(error => error.message).filter(message => message.startsWith("Missing ") && message.endsWith(" scope"));
|
|
57
|
-
|
|
58
43
|
/**
|
|
59
44
|
* This error link is used to capture error information, i. e. traceId, graphQL errors, network errors, etc.
|
|
60
45
|
*/
|
|
61
|
-
const createErrorLink = ({ errorHandler, getToken,
|
|
46
|
+
const createErrorLink = ({ errorHandler, getToken, }) => {
|
|
62
47
|
return error.onError(({ graphQLErrors, networkError, operation, response, forward }) => {
|
|
63
48
|
if (networkError) {
|
|
64
49
|
// We skip the error logging if the error is an AbortError
|
|
@@ -85,10 +70,6 @@ const createErrorLink = ({ errorHandler, getToken, showErrorToast, }) => {
|
|
|
85
70
|
}
|
|
86
71
|
// eslint-disable-next-line no-console
|
|
87
72
|
console.error(`Error calling: '${operation.getContext().clientAwareness.name}' fetching Data for: ${operation.operationName}`, graphQLErrors);
|
|
88
|
-
const missingScopeMessages = getMissingScopeMessages(graphQLErrors);
|
|
89
|
-
if (missingScopeMessages.length > 0) {
|
|
90
|
-
showErrorToast(new Error(missingScopeMessages.join(", ")));
|
|
91
|
-
}
|
|
92
73
|
/**
|
|
93
74
|
* We want to see the full graphQL error since
|
|
94
75
|
* it contains extra details like the query/mutation
|
|
@@ -139,20 +120,18 @@ const DEFAULT_AUTH_GRACE_MS = 30000;
|
|
|
139
120
|
/**
|
|
140
121
|
* Wraps an SSE subscription link and provides the same error monitoring as the
|
|
141
122
|
* HTTP error link — capturing GraphQL errors, traceIds, FORCE_RELOAD_BROWSER,
|
|
142
|
-
* and UNAUTHENTICATED codes
|
|
143
|
-
* long-lived subscription Observables.
|
|
123
|
+
* and UNAUTHENTICATED codes — for long-lived subscription Observables.
|
|
144
124
|
*
|
|
145
125
|
* UNAUTHENTICATED errors are deferred by a grace period (default 30s) to allow
|
|
146
126
|
* Okta's autoRenew to refresh the token. If a successful response arrives
|
|
147
127
|
* within the window the capture is cancelled; if the timer fires, a single
|
|
148
128
|
* captureException is sent — an actionable signal that token refresh failed.
|
|
149
129
|
*/
|
|
150
|
-
const createSubscriptionErrorLink = ({ errorHandler, getToken,
|
|
130
|
+
const createSubscriptionErrorLink = ({ errorHandler, getToken, graceMs = DEFAULT_AUTH_GRACE_MS, }) => {
|
|
151
131
|
return new client.ApolloLink((operation, forward) => {
|
|
152
132
|
return new client.Observable(observer => {
|
|
153
133
|
let authGraceTimer = null;
|
|
154
134
|
let authErrorReported = false;
|
|
155
|
-
let missingScopeReported = false;
|
|
156
135
|
const clearGraceTimer = () => {
|
|
157
136
|
if (authGraceTimer !== null) {
|
|
158
137
|
clearTimeout(authGraceTimer);
|
|
@@ -182,16 +161,6 @@ const createSubscriptionErrorLink = ({ errorHandler, getToken, showErrorToast, g
|
|
|
182
161
|
level: "error",
|
|
183
162
|
data: { log: JSON.stringify(errors) },
|
|
184
163
|
});
|
|
185
|
-
// A subscription is long-lived and a scope failure is static for the operation, so
|
|
186
|
-
// the same error can arrive on every payload. Report it once per subscription
|
|
187
|
-
// rather than re-raising the toast, which the provider's de-dup would otherwise
|
|
188
|
-
// make visibly disappear and re-enter. Reset when a clean payload arrives, as the
|
|
189
|
-
// auth grace handling below does.
|
|
190
|
-
const missingScopeMessages = getMissingScopeMessages(errors);
|
|
191
|
-
if (missingScopeMessages.length > 0 && !missingScopeReported) {
|
|
192
|
-
missingScopeReported = true;
|
|
193
|
-
showErrorToast(new Error(missingScopeMessages.join(", ")));
|
|
194
|
-
}
|
|
195
164
|
const invalidToken = errors.some(x => x.extensions?.code === "UNAUTHENTICATED" ||
|
|
196
165
|
x.message.includes("Invalid token specified") ||
|
|
197
166
|
x.message.includes("Access denied! You need to be authorized to perform this action!"));
|
|
@@ -211,7 +180,6 @@ const createSubscriptionErrorLink = ({ errorHandler, getToken, showErrorToast, g
|
|
|
211
180
|
else {
|
|
212
181
|
clearGraceTimer();
|
|
213
182
|
authErrorReported = false;
|
|
214
|
-
missingScopeReported = false;
|
|
215
183
|
}
|
|
216
184
|
observer.next(response);
|
|
217
185
|
},
|
|
@@ -313,7 +281,7 @@ const isInternalGqlContext = () => {
|
|
|
313
281
|
/**
|
|
314
282
|
* @internal
|
|
315
283
|
*/
|
|
316
|
-
const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, isDev, tracingHeaders: initialTracingHeaders, firstToken, errorHandler,
|
|
284
|
+
const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, isDev, tracingHeaders: initialTracingHeaders, firstToken, errorHandler, }) => {
|
|
317
285
|
let token = firstToken;
|
|
318
286
|
let tracingHeaders = initialTracingHeaders;
|
|
319
287
|
const publicGraphQLLink = client.createHttpLink({
|
|
@@ -333,7 +301,7 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
333
301
|
},
|
|
334
302
|
};
|
|
335
303
|
});
|
|
336
|
-
const errorLink = createErrorLink({ errorHandler, getToken: () => token
|
|
304
|
+
const errorLink = createErrorLink({ errorHandler, getToken: () => token });
|
|
337
305
|
const defaultOptions = {
|
|
338
306
|
watchQuery: {
|
|
339
307
|
fetchPolicy: "no-cache",
|
|
@@ -371,7 +339,7 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
371
339
|
headers: () => generateHeaders(token, tracingHeaders),
|
|
372
340
|
});
|
|
373
341
|
// Split links based on operation type
|
|
374
|
-
const subscriptionErrorLink = createSubscriptionErrorLink({ errorHandler, getToken: () => token
|
|
342
|
+
const subscriptionErrorLink = createSubscriptionErrorLink({ errorHandler, getToken: () => token });
|
|
375
343
|
const splitLink = client.from([
|
|
376
344
|
authLink,
|
|
377
345
|
client.split(({ query }) => {
|
|
@@ -426,34 +394,6 @@ const useApolloClient = () => {
|
|
|
426
394
|
const { graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, environment, tracingHeaders } = reactCoreHooks.useEnvironment();
|
|
427
395
|
const { token: currentToken } = reactCoreHooks.useToken();
|
|
428
396
|
const errorHandler = reactCoreHooks.useErrorHandler();
|
|
429
|
-
const { addToast } = reactCoreHooks.useToast();
|
|
430
|
-
const { permissions } = reactCoreHooks.useCurrentUser();
|
|
431
|
-
const showErrorToast = react.useCallback((error) => {
|
|
432
|
-
// Only a developer running an Iris App locally can act on a missing scope, since only
|
|
433
|
-
// they can edit the manifest. The server throws the same message for real customer
|
|
434
|
-
// sessions and for token clients that have no manifest at all, so surfacing it outside
|
|
435
|
-
// local mode tells users to fix something they cannot reach.
|
|
436
|
-
//
|
|
437
|
-
// Read from storage rather than through useIrisAppsSDK().developerMode: that context is
|
|
438
|
-
// created below this provider (it queries GraphQL, so it needs this Apollo client) and
|
|
439
|
-
// lives in a visibility:host library this one cannot import.
|
|
440
|
-
const isDeveloper = permissions?.some(permission => permission === "FAKE_PERMISSION");
|
|
441
|
-
if (!isDeveloper) {
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
void addToast({
|
|
445
|
-
// ToastProviderHost de-dups on id *or* title, so both must be constant and
|
|
446
|
-
// specific: a message-derived id churned on every scope combination, and the
|
|
447
|
-
// generic "Error" title evicted unrelated toasts (and was evicted by them).
|
|
448
|
-
id: "apollo-missing-scope",
|
|
449
|
-
intent: "danger",
|
|
450
|
-
title: "Missing scope",
|
|
451
|
-
// A missing scope leaves the view permanently empty, so the only explanation
|
|
452
|
-
// the user gets must not time out.
|
|
453
|
-
behavior: "persistent",
|
|
454
|
-
description: `${error.message} - you must add the scope to your iris app manifest`,
|
|
455
|
-
});
|
|
456
|
-
}, [addToast, permissions]);
|
|
457
397
|
const [client] = react.useState(() => {
|
|
458
398
|
return createApolloClient({
|
|
459
399
|
/* DON'T CHANGE THIS ! its there to ensure we don't recreate apollo client just because the token changes,
|
|
@@ -466,7 +406,6 @@ const useApolloClient = () => {
|
|
|
466
406
|
tracingHeaders,
|
|
467
407
|
firstToken: currentToken,
|
|
468
408
|
errorHandler,
|
|
469
|
-
showErrorToast,
|
|
470
409
|
});
|
|
471
410
|
});
|
|
472
411
|
// Synchronously propagate the token into the Apollo client closure during render
|
|
@@ -1034,7 +973,7 @@ const TrackunitProviders = ({ translations, children, errorHandler }) => {
|
|
|
1034
973
|
i18nLibraryTranslation.registerTranslations(translations); // Register the apps translations if passed.
|
|
1035
974
|
}
|
|
1036
975
|
i18nLibraryTranslation.initializeTranslationsForApp(); // Initialize all registered translations
|
|
1037
|
-
return (jsxRuntime.jsx(EnvironmentProviderIrisApp, { children: jsxRuntime.jsx(ErrorHandlingProviderIrisApp, { errorHandler: errorHandler, children: jsxRuntime.jsx(ThemeCssProviderIrisApp, { children: jsxRuntime.jsx(TokenProviderIrisApp, { children: jsxRuntime.jsx(CurrentUserPreferenceProviderIrisApp, { children: jsxRuntime.jsx(CurrentUserProviderIrisApp, { children: jsxRuntime.jsx(UserSubscriptionProviderIrisApp, { children: jsxRuntime.jsx(AnalyticsProviderIrisApp, { children: jsxRuntime.jsx(OemBrandingProviderIrisApp, { children: jsxRuntime.jsx(AssetSortingProviderIrisApp, { children: jsxRuntime.jsx(
|
|
976
|
+
return (jsxRuntime.jsx(EnvironmentProviderIrisApp, { children: jsxRuntime.jsx(ErrorHandlingProviderIrisApp, { errorHandler: errorHandler, children: jsxRuntime.jsx(ThemeCssProviderIrisApp, { children: jsxRuntime.jsx(TokenProviderIrisApp, { children: jsxRuntime.jsx(CurrentUserPreferenceProviderIrisApp, { children: jsxRuntime.jsx(CurrentUserProviderIrisApp, { children: jsxRuntime.jsx(UserSubscriptionProviderIrisApp, { children: jsxRuntime.jsx(AnalyticsProviderIrisApp, { children: jsxRuntime.jsx(OemBrandingProviderIrisApp, { children: jsxRuntime.jsx(AssetSortingProviderIrisApp, { children: jsxRuntime.jsx(ManagerApolloProvider, { children: jsxRuntime.jsx(NavigationProviderIrisApp, { children: jsxRuntime.jsx(ToastProviderIrisApp, { children: jsxRuntime.jsx(ModalDialogContextProviderIrisApp, { children: jsxRuntime.jsx(ConfirmationDialogProviderIrisApp, { children: jsxRuntime.jsx(FilterBarProviderIrisApp, { children: jsxRuntime.jsx(ExportDataProviderIrisApp, { children: jsxRuntime.jsx(TimeRangeProviderIrisApp, { children: jsxRuntime.jsx(WidgetConfigProviderIrisApp, { children: jsxRuntime.jsx(GeolocationProviderIrisApp, { children: jsxRuntime.jsx(react.Suspense, { fallback: jsxRuntime.jsx(reactComponents.Spinner, { centering: "centered", "data-testid": "trackunit-providers" }), children: jsxRuntime.jsx(FeatureFlagProviderIrisApp, { children: children }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }));
|
|
1038
977
|
};
|
|
1039
978
|
|
|
1040
979
|
exports.FeatureFlagProviderIrisApp = FeatureFlagProviderIrisApp;
|
package/index.esm.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { FeatureFlagRuntime, ToastRuntime, AnalyticsRuntime, registerHostChangeHandler, AssetSortingRuntime, ConfirmationDialogRuntime, EnvironmentRuntime, ExportDataRuntime, AssetsFilterBarRuntime, CustomersFilterBarRuntime, SitesFilterBarRuntime, GeolocationRuntime, ModalDialogRuntime, NavigationRuntime, OemBrandingRuntime, ThemeCssRuntime, TimeRangeRuntime, TokenRuntime, CurrentUserRuntime, CurrentUserPreferenceRuntime, UserSubscriptionRuntime, WidgetConfigRuntime } from '@trackunit/iris-app-runtime-core';
|
|
3
3
|
import { FeatureFlagContextProvider, ToastProvider, AnalyticsContextProvider, AssetSortingProvider, ConfirmationDialogProvider, EnvironmentContextProvider, ErrorHandlingContextProvider, ExportDataContext, FilterBarProvider, GeolocationProvider, ModalDialogContextProvider, NavigationContextProvider, OemBrandingContextProvider, TimeRangeProvider, TokenProvider, CurrentUserProvider, CurrentUserPreferenceProvider, UserSubscriptionProvider, WidgetConfigProvider } from '@trackunit/react-core-contexts-api';
|
|
4
|
-
import { useState, useEffect,
|
|
4
|
+
import { useState, useEffect, useMemo, useCallback, useReducer, Suspense } from 'react';
|
|
5
5
|
import { ApolloLink, Observable, createHttpLink, from, split, ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
|
|
6
|
-
import { useEnvironment, useToken, useErrorHandler
|
|
6
|
+
import { useEnvironment, useToken, useErrorHandler } from '@trackunit/react-core-hooks';
|
|
7
7
|
import { setContext } from '@apollo/client/link/context';
|
|
8
8
|
import { removeTypenameFromVariables } from '@apollo/client/link/remove-typename';
|
|
9
9
|
import { getMainDefinition, Observable as Observable$1 } from '@apollo/client/utilities';
|
|
@@ -38,25 +38,10 @@ const FeatureFlagProviderIrisApp = ({ children }) => {
|
|
|
38
38
|
return jsx(FeatureFlagContextProvider, { value: featureFlags, children: children });
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
/**
|
|
42
|
-
* Picks out the messages that report a missing authorization scope.
|
|
43
|
-
*
|
|
44
|
-
* The server offers no structured signal to key off: `verifyScopes`
|
|
45
|
-
* (libs/graphql/common/src/schemabuilder/AuthorizationDirective.ts) throws a plain
|
|
46
|
-
* `ForbiddenError` whose message is assembled as "Missing " + the allowed scopes joined with
|
|
47
|
-
* " or " + " scope". Matching on that shape is therefore the only option available, and it
|
|
48
|
-
* lives here so the HTTP and subscription links cannot drift apart on what counts as a
|
|
49
|
-
* missing scope.
|
|
50
|
-
*
|
|
51
|
-
* Filtering rather than inspecting the first error matters in both links: a response can
|
|
52
|
-
* carry several errors in any order, since the directive throws per resolved field.
|
|
53
|
-
*/
|
|
54
|
-
const getMissingScopeMessages = (errors) => errors.map(error => error.message).filter(message => message.startsWith("Missing ") && message.endsWith(" scope"));
|
|
55
|
-
|
|
56
41
|
/**
|
|
57
42
|
* This error link is used to capture error information, i. e. traceId, graphQL errors, network errors, etc.
|
|
58
43
|
*/
|
|
59
|
-
const createErrorLink = ({ errorHandler, getToken,
|
|
44
|
+
const createErrorLink = ({ errorHandler, getToken, }) => {
|
|
60
45
|
return onError(({ graphQLErrors, networkError, operation, response, forward }) => {
|
|
61
46
|
if (networkError) {
|
|
62
47
|
// We skip the error logging if the error is an AbortError
|
|
@@ -83,10 +68,6 @@ const createErrorLink = ({ errorHandler, getToken, showErrorToast, }) => {
|
|
|
83
68
|
}
|
|
84
69
|
// eslint-disable-next-line no-console
|
|
85
70
|
console.error(`Error calling: '${operation.getContext().clientAwareness.name}' fetching Data for: ${operation.operationName}`, graphQLErrors);
|
|
86
|
-
const missingScopeMessages = getMissingScopeMessages(graphQLErrors);
|
|
87
|
-
if (missingScopeMessages.length > 0) {
|
|
88
|
-
showErrorToast(new Error(missingScopeMessages.join(", ")));
|
|
89
|
-
}
|
|
90
71
|
/**
|
|
91
72
|
* We want to see the full graphQL error since
|
|
92
73
|
* it contains extra details like the query/mutation
|
|
@@ -137,20 +118,18 @@ const DEFAULT_AUTH_GRACE_MS = 30000;
|
|
|
137
118
|
/**
|
|
138
119
|
* Wraps an SSE subscription link and provides the same error monitoring as the
|
|
139
120
|
* HTTP error link — capturing GraphQL errors, traceIds, FORCE_RELOAD_BROWSER,
|
|
140
|
-
* and UNAUTHENTICATED codes
|
|
141
|
-
* long-lived subscription Observables.
|
|
121
|
+
* and UNAUTHENTICATED codes — for long-lived subscription Observables.
|
|
142
122
|
*
|
|
143
123
|
* UNAUTHENTICATED errors are deferred by a grace period (default 30s) to allow
|
|
144
124
|
* Okta's autoRenew to refresh the token. If a successful response arrives
|
|
145
125
|
* within the window the capture is cancelled; if the timer fires, a single
|
|
146
126
|
* captureException is sent — an actionable signal that token refresh failed.
|
|
147
127
|
*/
|
|
148
|
-
const createSubscriptionErrorLink = ({ errorHandler, getToken,
|
|
128
|
+
const createSubscriptionErrorLink = ({ errorHandler, getToken, graceMs = DEFAULT_AUTH_GRACE_MS, }) => {
|
|
149
129
|
return new ApolloLink((operation, forward) => {
|
|
150
130
|
return new Observable(observer => {
|
|
151
131
|
let authGraceTimer = null;
|
|
152
132
|
let authErrorReported = false;
|
|
153
|
-
let missingScopeReported = false;
|
|
154
133
|
const clearGraceTimer = () => {
|
|
155
134
|
if (authGraceTimer !== null) {
|
|
156
135
|
clearTimeout(authGraceTimer);
|
|
@@ -180,16 +159,6 @@ const createSubscriptionErrorLink = ({ errorHandler, getToken, showErrorToast, g
|
|
|
180
159
|
level: "error",
|
|
181
160
|
data: { log: JSON.stringify(errors) },
|
|
182
161
|
});
|
|
183
|
-
// A subscription is long-lived and a scope failure is static for the operation, so
|
|
184
|
-
// the same error can arrive on every payload. Report it once per subscription
|
|
185
|
-
// rather than re-raising the toast, which the provider's de-dup would otherwise
|
|
186
|
-
// make visibly disappear and re-enter. Reset when a clean payload arrives, as the
|
|
187
|
-
// auth grace handling below does.
|
|
188
|
-
const missingScopeMessages = getMissingScopeMessages(errors);
|
|
189
|
-
if (missingScopeMessages.length > 0 && !missingScopeReported) {
|
|
190
|
-
missingScopeReported = true;
|
|
191
|
-
showErrorToast(new Error(missingScopeMessages.join(", ")));
|
|
192
|
-
}
|
|
193
162
|
const invalidToken = errors.some(x => x.extensions?.code === "UNAUTHENTICATED" ||
|
|
194
163
|
x.message.includes("Invalid token specified") ||
|
|
195
164
|
x.message.includes("Access denied! You need to be authorized to perform this action!"));
|
|
@@ -209,7 +178,6 @@ const createSubscriptionErrorLink = ({ errorHandler, getToken, showErrorToast, g
|
|
|
209
178
|
else {
|
|
210
179
|
clearGraceTimer();
|
|
211
180
|
authErrorReported = false;
|
|
212
|
-
missingScopeReported = false;
|
|
213
181
|
}
|
|
214
182
|
observer.next(response);
|
|
215
183
|
},
|
|
@@ -311,7 +279,7 @@ const isInternalGqlContext = () => {
|
|
|
311
279
|
/**
|
|
312
280
|
* @internal
|
|
313
281
|
*/
|
|
314
|
-
const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, isDev, tracingHeaders: initialTracingHeaders, firstToken, errorHandler,
|
|
282
|
+
const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, isDev, tracingHeaders: initialTracingHeaders, firstToken, errorHandler, }) => {
|
|
315
283
|
let token = firstToken;
|
|
316
284
|
let tracingHeaders = initialTracingHeaders;
|
|
317
285
|
const publicGraphQLLink = createHttpLink({
|
|
@@ -331,7 +299,7 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
331
299
|
},
|
|
332
300
|
};
|
|
333
301
|
});
|
|
334
|
-
const errorLink = createErrorLink({ errorHandler, getToken: () => token
|
|
302
|
+
const errorLink = createErrorLink({ errorHandler, getToken: () => token });
|
|
335
303
|
const defaultOptions = {
|
|
336
304
|
watchQuery: {
|
|
337
305
|
fetchPolicy: "no-cache",
|
|
@@ -369,7 +337,7 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
369
337
|
headers: () => generateHeaders(token, tracingHeaders),
|
|
370
338
|
});
|
|
371
339
|
// Split links based on operation type
|
|
372
|
-
const subscriptionErrorLink = createSubscriptionErrorLink({ errorHandler, getToken: () => token
|
|
340
|
+
const subscriptionErrorLink = createSubscriptionErrorLink({ errorHandler, getToken: () => token });
|
|
373
341
|
const splitLink = from([
|
|
374
342
|
authLink,
|
|
375
343
|
split(({ query }) => {
|
|
@@ -424,34 +392,6 @@ const useApolloClient = () => {
|
|
|
424
392
|
const { graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, environment, tracingHeaders } = useEnvironment();
|
|
425
393
|
const { token: currentToken } = useToken();
|
|
426
394
|
const errorHandler = useErrorHandler();
|
|
427
|
-
const { addToast } = useToast();
|
|
428
|
-
const { permissions } = useCurrentUser();
|
|
429
|
-
const showErrorToast = useCallback((error) => {
|
|
430
|
-
// Only a developer running an Iris App locally can act on a missing scope, since only
|
|
431
|
-
// they can edit the manifest. The server throws the same message for real customer
|
|
432
|
-
// sessions and for token clients that have no manifest at all, so surfacing it outside
|
|
433
|
-
// local mode tells users to fix something they cannot reach.
|
|
434
|
-
//
|
|
435
|
-
// Read from storage rather than through useIrisAppsSDK().developerMode: that context is
|
|
436
|
-
// created below this provider (it queries GraphQL, so it needs this Apollo client) and
|
|
437
|
-
// lives in a visibility:host library this one cannot import.
|
|
438
|
-
const isDeveloper = permissions?.some(permission => permission === "FAKE_PERMISSION");
|
|
439
|
-
if (!isDeveloper) {
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
void addToast({
|
|
443
|
-
// ToastProviderHost de-dups on id *or* title, so both must be constant and
|
|
444
|
-
// specific: a message-derived id churned on every scope combination, and the
|
|
445
|
-
// generic "Error" title evicted unrelated toasts (and was evicted by them).
|
|
446
|
-
id: "apollo-missing-scope",
|
|
447
|
-
intent: "danger",
|
|
448
|
-
title: "Missing scope",
|
|
449
|
-
// A missing scope leaves the view permanently empty, so the only explanation
|
|
450
|
-
// the user gets must not time out.
|
|
451
|
-
behavior: "persistent",
|
|
452
|
-
description: `${error.message} - you must add the scope to your iris app manifest`,
|
|
453
|
-
});
|
|
454
|
-
}, [addToast, permissions]);
|
|
455
395
|
const [client] = useState(() => {
|
|
456
396
|
return createApolloClient({
|
|
457
397
|
/* DON'T CHANGE THIS ! its there to ensure we don't recreate apollo client just because the token changes,
|
|
@@ -464,7 +404,6 @@ const useApolloClient = () => {
|
|
|
464
404
|
tracingHeaders,
|
|
465
405
|
firstToken: currentToken,
|
|
466
406
|
errorHandler,
|
|
467
|
-
showErrorToast,
|
|
468
407
|
});
|
|
469
408
|
});
|
|
470
409
|
// Synchronously propagate the token into the Apollo client closure during render
|
|
@@ -1032,7 +971,7 @@ const TrackunitProviders = ({ translations, children, errorHandler }) => {
|
|
|
1032
971
|
registerTranslations(translations); // Register the apps translations if passed.
|
|
1033
972
|
}
|
|
1034
973
|
initializeTranslationsForApp(); // Initialize all registered translations
|
|
1035
|
-
return (jsx(EnvironmentProviderIrisApp, { children: jsx(ErrorHandlingProviderIrisApp, { errorHandler: errorHandler, children: jsx(ThemeCssProviderIrisApp, { children: jsx(TokenProviderIrisApp, { children: jsx(CurrentUserPreferenceProviderIrisApp, { children: jsx(CurrentUserProviderIrisApp, { children: jsx(UserSubscriptionProviderIrisApp, { children: jsx(AnalyticsProviderIrisApp, { children: jsx(OemBrandingProviderIrisApp, { children: jsx(AssetSortingProviderIrisApp, { children: jsx(
|
|
974
|
+
return (jsx(EnvironmentProviderIrisApp, { children: jsx(ErrorHandlingProviderIrisApp, { errorHandler: errorHandler, children: jsx(ThemeCssProviderIrisApp, { children: jsx(TokenProviderIrisApp, { children: jsx(CurrentUserPreferenceProviderIrisApp, { children: jsx(CurrentUserProviderIrisApp, { children: jsx(UserSubscriptionProviderIrisApp, { children: jsx(AnalyticsProviderIrisApp, { children: jsx(OemBrandingProviderIrisApp, { children: jsx(AssetSortingProviderIrisApp, { children: jsx(ManagerApolloProvider, { children: jsx(NavigationProviderIrisApp, { children: jsx(ToastProviderIrisApp, { children: jsx(ModalDialogContextProviderIrisApp, { children: jsx(ConfirmationDialogProviderIrisApp, { children: jsx(FilterBarProviderIrisApp, { children: jsx(ExportDataProviderIrisApp, { children: jsx(TimeRangeProviderIrisApp, { children: jsx(WidgetConfigProviderIrisApp, { children: jsx(GeolocationProviderIrisApp, { children: jsx(Suspense, { fallback: jsx(Spinner, { centering: "centered", "data-testid": "trackunit-providers" }), children: jsx(FeatureFlagProviderIrisApp, { children: children }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }));
|
|
1036
975
|
};
|
|
1037
976
|
|
|
1038
977
|
export { FeatureFlagProviderIrisApp, ManagerApolloProvider, ToastProviderIrisApp, TrackunitProviders };
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-core-contexts",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.5-alpha-3e7014314a8.0",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=24.x"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@trackunit/iris-app-api": "2.4.
|
|
11
|
-
"@trackunit/iris-app-runtime-core-api": "1.17.
|
|
12
|
-
"@trackunit/react-core-hooks": "1.21.
|
|
13
|
-
"@trackunit/i18n-library-translation": "2.4.
|
|
14
|
-
"@trackunit/react-components": "2.10.
|
|
15
|
-
"@trackunit/iris-app-runtime-core": "1.18.
|
|
10
|
+
"@trackunit/iris-app-api": "2.4.14-alpha-3e7014314a8.0",
|
|
11
|
+
"@trackunit/iris-app-runtime-core-api": "1.17.28-alpha-3e7014314a8.0",
|
|
12
|
+
"@trackunit/react-core-hooks": "1.21.22-alpha-3e7014314a8.0",
|
|
13
|
+
"@trackunit/i18n-library-translation": "2.4.22-alpha-3e7014314a8.0",
|
|
14
|
+
"@trackunit/react-components": "2.10.16-alpha-3e7014314a8.0",
|
|
15
|
+
"@trackunit/iris-app-runtime-core": "1.18.28-alpha-3e7014314a8.0",
|
|
16
16
|
"graphql-sse": "^2.5.4",
|
|
17
|
-
"@trackunit/react-core-contexts-api": "1.18.
|
|
17
|
+
"@trackunit/react-core-contexts-api": "1.18.28-alpha-3e7014314a8.0"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@apollo/client": "^3.13.8",
|
|
@@ -3,7 +3,7 @@ import { ErrorHandlingContextValue, TracingHeaders } from "@trackunit/iris-app-r
|
|
|
3
3
|
/**
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
6
|
-
export declare const createApolloClient: ({ graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, isDev, tracingHeaders: initialTracingHeaders, firstToken, errorHandler,
|
|
6
|
+
export declare const createApolloClient: ({ graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, isDev, tracingHeaders: initialTracingHeaders, firstToken, errorHandler, }: {
|
|
7
7
|
graphqlPublicUrl: string;
|
|
8
8
|
graphqlInternalUrl: string;
|
|
9
9
|
graphqlReportUrl: string;
|
|
@@ -11,7 +11,6 @@ export declare const createApolloClient: ({ graphqlPublicUrl, graphqlInternalUrl
|
|
|
11
11
|
isDev: boolean;
|
|
12
12
|
firstToken?: string;
|
|
13
13
|
errorHandler: ErrorHandlingContextValue;
|
|
14
|
-
showErrorToast: (error: Error) => void;
|
|
15
14
|
}) => {
|
|
16
15
|
client: ApolloClient<import("@apollo/client").NormalizedCacheObject>;
|
|
17
16
|
setToken: (newToken: string | undefined) => void;
|
|
@@ -3,8 +3,7 @@ import { ErrorHandlingContextValue } from "@trackunit/iris-app-runtime-core-api"
|
|
|
3
3
|
/**
|
|
4
4
|
* This error link is used to capture error information, i. e. traceId, graphQL errors, network errors, etc.
|
|
5
5
|
*/
|
|
6
|
-
export declare const createErrorLink: ({ errorHandler, getToken,
|
|
6
|
+
export declare const createErrorLink: ({ errorHandler, getToken, }: {
|
|
7
7
|
errorHandler: ErrorHandlingContextValue;
|
|
8
8
|
getToken: () => string | undefined;
|
|
9
|
-
showErrorToast: (error: Error) => void;
|
|
10
9
|
}) => ApolloLink;
|
|
@@ -3,17 +3,15 @@ import { ErrorHandlingContextValue } from "@trackunit/iris-app-runtime-core-api"
|
|
|
3
3
|
/**
|
|
4
4
|
* Wraps an SSE subscription link and provides the same error monitoring as the
|
|
5
5
|
* HTTP error link — capturing GraphQL errors, traceIds, FORCE_RELOAD_BROWSER,
|
|
6
|
-
* and UNAUTHENTICATED codes
|
|
7
|
-
* long-lived subscription Observables.
|
|
6
|
+
* and UNAUTHENTICATED codes — for long-lived subscription Observables.
|
|
8
7
|
*
|
|
9
8
|
* UNAUTHENTICATED errors are deferred by a grace period (default 30s) to allow
|
|
10
9
|
* Okta's autoRenew to refresh the token. If a successful response arrives
|
|
11
10
|
* within the window the capture is cancelled; if the timer fires, a single
|
|
12
11
|
* captureException is sent — an actionable signal that token refresh failed.
|
|
13
12
|
*/
|
|
14
|
-
export declare const createSubscriptionErrorLink: ({ errorHandler, getToken,
|
|
13
|
+
export declare const createSubscriptionErrorLink: ({ errorHandler, getToken, graceMs, }: {
|
|
15
14
|
errorHandler: ErrorHandlingContextValue;
|
|
16
15
|
getToken: () => string | undefined;
|
|
17
|
-
showErrorToast: (error: Error) => void;
|
|
18
16
|
graceMs?: number;
|
|
19
17
|
}) => ApolloLink;
|
package/migrations/entry.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../../../libs/react/core-contexts/migrations/entry.ts"],"names":[],"mappings":"","sourcesContent":["export {};\n"]}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Picks out the messages that report a missing authorization scope.
|
|
3
|
-
*
|
|
4
|
-
* The server offers no structured signal to key off: `verifyScopes`
|
|
5
|
-
* (libs/graphql/common/src/schemabuilder/AuthorizationDirective.ts) throws a plain
|
|
6
|
-
* `ForbiddenError` whose message is assembled as "Missing " + the allowed scopes joined with
|
|
7
|
-
* " or " + " scope". Matching on that shape is therefore the only option available, and it
|
|
8
|
-
* lives here so the HTTP and subscription links cannot drift apart on what counts as a
|
|
9
|
-
* missing scope.
|
|
10
|
-
*
|
|
11
|
-
* Filtering rather than inspecting the first error matters in both links: a response can
|
|
12
|
-
* carry several errors in any order, since the directive throws per resolved field.
|
|
13
|
-
*/
|
|
14
|
-
export declare const getMissingScopeMessages: (errors: ReadonlyArray<{
|
|
15
|
-
message: string;
|
|
16
|
-
}>) => Array<string>;
|