UNPKG

@commercetools-frontend/application-shell-connectors

Version:
955 lines (907 loc) • 53.9 kB
import _Object$keys from '@babel/runtime-corejs3/core-js-stable/object/keys'; import _Object$getOwnPropertySymbols from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols'; import _filterInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/filter'; import _Object$getOwnPropertyDescriptor from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor'; import _forEachInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/for-each'; import _Object$getOwnPropertyDescriptors from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors'; import _Object$defineProperties from '@babel/runtime-corejs3/core-js-stable/object/define-properties'; import _Object$defineProperty from '@babel/runtime-corejs3/core-js-stable/object/define-property'; import _defineProperty from '@babel/runtime-corejs3/helpers/esm/defineProperty'; import _pt from 'prop-types'; import { createContext, useContext, useMemo, useEffect } from 'react'; import moment from 'moment-timezone'; import { reportErrorToSentry } from '@commercetools-frontend/sentry'; import _URL from '@babel/runtime-corejs3/core-js-stable/url'; import _slicedToArray from '@babel/runtime-corejs3/helpers/esm/slicedToArray'; import _reduceInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/reduce'; import _Object$entries from '@babel/runtime-corejs3/core-js-stable/object/entries'; import _valuesInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/values'; import { jsx } from '@emotion/react/jsx-runtime'; import { useQuery, useLazyQuery, useMutation } from '@apollo/client/react'; import warning from 'tiny-warning'; import { GRAPHQL_TARGETS, SUPPORTED_HEADERS, STATUS_CODES, LOGOUT_REASONS, STORAGE_KEYS, LOGIN_STRATEGY_OIDC } from '@commercetools-frontend/constants'; import _findInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/find'; import { ApolloLink, ApolloClient, InMemoryCache, createHttpLink, from } from '@apollo/client'; import loggerLink from 'apollo-link-logger'; import { onError } from '@apollo/client/link/error'; import history from '@commercetools-frontend/browser-history'; import _includesInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/includes'; import _Array$isArray from '@babel/runtime-corejs3/core-js-stable/array/is-array'; import _someInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/some'; import _Object$values from '@babel/runtime-corejs3/core-js-stable/object/values'; import _mapInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/map'; import createHttpUserAgent from '@commercetools/http-user-agent'; import omitEmpty from 'omit-empty-es'; import { v4 } from 'uuid'; import _Reflect$construct from '@babel/runtime-corejs3/core-js-stable/reflect/construct'; import _createClass from '@babel/runtime-corejs3/helpers/esm/createClass'; import _classCallCheck from '@babel/runtime-corejs3/helpers/esm/classCallCheck'; import _assertThisInitialized from '@babel/runtime-corejs3/helpers/esm/assertThisInitialized'; import _inherits from '@babel/runtime-corejs3/helpers/esm/inherits'; import _possibleConstructorReturn from '@babel/runtime-corejs3/helpers/esm/possibleConstructorReturn'; import _getPrototypeOf from '@babel/runtime-corejs3/helpers/esm/getPrototypeOf'; import _wrapNativeSuper from '@babel/runtime-corejs3/helpers/esm/wrapNativeSuper'; import _JSON$stringify from '@babel/runtime-corejs3/core-js-stable/json/stringify'; import { RetryLink } from '@apollo/client/link/retry'; // NOTE: This string will be replaced on build time with the package version. var version = "24.2.0"; // From https://github.com/acdlite/recompose/blob/master/src/packages/recompose/getDisplayName.js const getDisplayName = Component => { if (typeof Component === 'string') { return Component; } if (!Component) { return undefined; } return Component.displayName || Component.name || 'Component'; }; const mcHostnameRegex = /^mc(-(\d){4,})?\.(.*)$/; const mcPreviewHostnameRegex = /^.*\.mc-preview\.(.*)$/; const getMcOriginTld = host => { if (host.match(mcPreviewHostnameRegex)) { return host.replace(mcPreviewHostnameRegex, '$1'); } return host.replace(mcHostnameRegex, '$3'); }; const getMcApiUrlFromOrigin = origin => { const url = new _URL(origin); const originTld = getMcOriginTld(url.host); return `${url.protocol}//mc-api.${originTld}`; }; const parseAsBoolean = value => value === true || value === 'true'; function getMcApiUrl() { let environment = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.app; let origin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : window.origin; const isServedByProxy = parseAsBoolean(environment.servedByProxy); /** * Prefer using the origin URL for the MC API based on the origin value * of the browser's `window.location`. * This ensures that the application always uses the correct URL associated * with that environment, instead of relying on the config value. */ if (isServedByProxy) { return getMcApiUrlFromOrigin(origin); } return environment.mcApiUrl; } function ownKeys$7(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$7(e) { for (var r = 1; r < arguments.length; r++) { var _context2, _context3; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context2 = ownKeys$7(Object(t), !0)).call(_context2, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context3 = ownKeys$7(Object(t))).call(_context3, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } // Menu visibilities // Permissions // Action rights // Data fences /** * NOTE: * * Permissions and menu visibilities are being fetched though the `allAppliedPermissions` * and the `allAppliedMenuVisibilities` which both return an array of `{ name: string, value: boolean }`. * This gives more flexibility to introduce new values to apps without having to release * the merchant-center-app-kit by adding/exposing them from the mc-be (our proxy service). * * The application below however expects both permissions an menu visibilities to be of the shape * `[name: string]: boolean` which is what the shape above is mapped into here. This object shape is easier * to work with in application level code (while be a non breaking change to other packages) as you can just * do `canViewProducts`. * * This function considering its concern belongs into the `permissions` package. However, * for now it doesn't have to be shared and as a result can be co-located with * the fetching logic. Given this mapping needs to be used elsewere feel free * to move this over to `permissions` and export it there. */ const normalizeAllAppliedPermissions = allAppliedPermissions => { if (!allAppliedPermissions || allAppliedPermissions.length === 0) { return null; } return _reduceInstanceProperty(allAppliedPermissions).call(allAppliedPermissions, (transformedAllApplied, allApplied) => { if (!allApplied) return transformedAllApplied; return _objectSpread$7(_objectSpread$7({}, transformedAllApplied), {}, { [allApplied.name]: allApplied.value }); }, {}); }; const normalizeAllAppliedMenuVisibilities = allAppliedMenuVisibilities => { if (!allAppliedMenuVisibilities || allAppliedMenuVisibilities.length === 0) { return null; } return _reduceInstanceProperty(allAppliedMenuVisibilities).call(allAppliedMenuVisibilities, (transformedAllApplied, allApplied) => { if (!allApplied) return transformedAllApplied; return _objectSpread$7(_objectSpread$7({}, transformedAllApplied), {}, { [allApplied.name]: allApplied.value }); }, {}); }; const normalizeAllAppliedActionRights = allAppliedActionRights => { if (!allAppliedActionRights || allAppliedActionRights.length === 0) { return null; } return _reduceInstanceProperty(allAppliedActionRights).call(allAppliedActionRights, (transformedAllApplied, allApplied) => { if (!allApplied) return transformedAllApplied; const previousAllAppliedGroup = transformedAllApplied[allApplied.group]; return _objectSpread$7(_objectSpread$7({}, transformedAllApplied), {}, { [allApplied.group]: _objectSpread$7(_objectSpread$7({}, previousAllAppliedGroup), {}, { [allApplied.name]: allApplied.value }) }); }, {}); }; const normalizeAppliedDataFencesForStoresByResourceType = dataFences => { var _context; const groupedByResourceType = _reduceInstanceProperty(dataFences).call(dataFences, (previousGroupsOfSameType, appliedDataFence) => { if (!appliedDataFence) return previousGroupsOfSameType; const previousGroup = previousGroupsOfSameType[appliedDataFence.group]; return _objectSpread$7(_objectSpread$7({}, previousGroupsOfSameType), {}, { [appliedDataFence.group]: [...(previousGroup || []), appliedDataFence] }); }, {}); return _reduceInstanceProperty(_context = _Object$entries(groupedByResourceType)).call(_context, (previousGroupedByResourceType, _ref) => { let _ref2 = _slicedToArray(_ref, 2), resourceType = _ref2[0], dataFences = _ref2[1]; const groupByDataFenceName = _reduceInstanceProperty(dataFences).call(dataFences, (nextDataFenceValues, dataFence) => { const dataFenceByName = nextDataFenceValues[dataFence.name] || { values: [] }; return _objectSpread$7(_objectSpread$7({}, nextDataFenceValues), {}, { [dataFence.name]: _objectSpread$7(_objectSpread$7({}, dataFenceByName), {}, { values: [..._valuesInstanceProperty(dataFenceByName), dataFence.value] }) }); }, {}); return _objectSpread$7(_objectSpread$7({}, previousGroupedByResourceType), {}, { [resourceType]: groupByDataFenceName }); }, {}); }; // input: // [ // { // type: 'store', // name: 'canManageOrders', // value: 'usa', // group: 'orders', // }, // { // type: 'store', // name: 'canManageOrders', // value: 'germany', // group: 'orders', // }, // { // type: 'store', // name: 'canViewOrders', // value: 'canada', // group: 'orders', // }, // ] // output: // { // store: { // orders: { // canManageOrders: { values: ['usa', 'germany'] }, // canViewOrders: { values: ['canada'] }, // } // } // } const normalizeAllAppliedDataFences = allAppliedDataFences => { if (!allAppliedDataFences || allAppliedDataFences.length === 0) { return null; } const groupedByType = _reduceInstanceProperty(allAppliedDataFences).call(allAppliedDataFences, (previousGroupsOfSameType, appliedDataFence) => { if (!appliedDataFence) return previousGroupsOfSameType; const previousGroup = previousGroupsOfSameType[appliedDataFence.type]; return _objectSpread$7(_objectSpread$7({}, previousGroupsOfSameType), {}, { [appliedDataFence.type]: [...(previousGroup || []), appliedDataFence] }); }, {}); const normalizedDataFences = _objectSpread$7({}, 'store' in groupedByType ? { store: normalizeAppliedDataFencesForStoresByResourceType(groupedByType.store) } : {}); if (_Object$keys(normalizedDataFences).length > 0) return normalizedDataFences; return null; }; function ownKeys$6(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$6(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context = ownKeys$6(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context2 = ownKeys$6(Object(t))).call(_context2, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } const Context$2 = /*#__PURE__*/createContext({}); const defaultTimeZone = moment.tz.guess() || 'Etc/UTC'; // Expose only certain fields as some of them are only meant to // be used internally in the AppShell const mapUserToApplicationContextUser = user => { if (!user) return null; let applicationContextUser = { id: user.id, email: user.email, createdAt: user.createdAt, firstName: user.firstName, lastName: user.lastName, businessRole: user.businessRole, // NOTE: this is an alias for the original field `user.language` but it's actually // a locale (language + country). locale: user.language, timeZone: user.timeZone || defaultTimeZone, projects: user.projects }; // This property will only be populated when user has logged in using SSO if (user.idTokenUserInfo) { let additionalClaims = {}; try { additionalClaims = JSON.parse(user.idTokenUserInfo.additionalClaims || '{}'); } catch (error) { reportErrorToSentry(new Error('@commercetools-frontend/application-shell-connectors: Could not parse received user sso token additional claims from server.'), { extra: { receivedAdditionalClaims: user.idTokenUserInfo.additionalClaims } }); } applicationContextUser.idTokenUserInfo = _objectSpread$6(_objectSpread$6({}, user.idTokenUserInfo), {}, { additionalClaims }); } return applicationContextUser; }; // Adjust certain fields which depend e.g. on the origin const mapEnvironmentToApplicationContextEnvironment = (environment, origin) => _objectSpread$6(_objectSpread$6({}, environment), {}, { // NOTE: The `mcApiUrl` depends on `servedByProxy` mcApiUrl: getMcApiUrl(environment, origin) }); // Expose only certain fields as some of them are only meant to // be used internally in the AppShell const mapProjectToApplicationContextProject = project => { if (!project) return null; return { key: project.key, version: project.version, name: project.name, countries: project.countries, currencies: project.currencies, languages: project.languages, ownerId: project.owner.id, ownerName: project.owner.name, sampleDataImportDataset: project.sampleDataImportDataset, isUserAdminOfCurrentProject: project.isUserAdminOfCurrentProject, isProductionProject: project.isProductionProject }; }; const createApplicationContext = (environment, user, project, projectDataLocale) => ({ environment: mapEnvironmentToApplicationContextEnvironment(environment), user: mapUserToApplicationContextUser(user), project: mapProjectToApplicationContextProject(project), permissions: normalizeAllAppliedPermissions(project?.allAppliedPermissions), actionRights: normalizeAllAppliedActionRights(project?.allAppliedActionRights), dataFences: normalizeAllAppliedDataFences(project?.allAppliedDataFences), dataLocale: projectDataLocale || null }); const ApplicationContextProvider = props => jsx(Context$2.Provider, { value: createApplicationContext(props.environment, props.user, props.project, props.projectDataLocale), children: props.children }); ApplicationContextProvider.propTypes = process.env.NODE_ENV !== "production" ? { projectDataLocale: _pt.string, children: _pt.node.isRequired } : {}; ApplicationContextProvider.displayName = 'ApplicationContextProvider'; const ApplicationContext = props => jsx(Context$2.Consumer, { children: context => { // Because of the way the ApplicationShell configures the Context.Provider, // we ensure that, when we read from the context, we always get actual // context object and not the initial value. // Therefore, we can safely cast the value to be out `TApplicationContext` type. const applicationContext = context; return props.render(applicationContext); } }); ApplicationContext.propTypes = process.env.NODE_ENV !== "production" ? { render: _pt.func.isRequired } : {}; ApplicationContext.displayName = 'ApplicationContext'; function withApplicationContext(mapApplicationContextToProps) { return Component => { const WrappedComponent = props => jsx(ApplicationContext, { render: applicationContext => { const mappedProps = mapApplicationContextToProps ? mapApplicationContextToProps(applicationContext) : { applicationContext }; // @ts-ignore: relates to https://github.com/emotion-js/emotion/issues/3245 return jsx(Component, _objectSpread$6(_objectSpread$6({}, props), mappedProps)); } }); WrappedComponent.displayName = `withApplicationContext(${getDisplayName(Component)})`; return WrappedComponent; }; } // Use function overloading to declare two possible signatures with two // distict return types, based on the selector function argument. // Then implement the function. Typescript will pick the appropriate signature // based on the function arguments. function useApplicationContextHook(selector) { const context = useContext(Context$2); // Because of the way the ApplicationShell configures the Context.Provider, // we ensure that, when we read from the context, we always get actual // context object and not the initial value. // Therefore, we can safely cast the value to be out `TApplicationContext` type. const applicationContext = context; return selector ? selector(applicationContext) : applicationContext; } // This is a workaround to trick babel/rollup to correctly export the function. // Most likely the problem arises with the use of overloading. // See related issue: https://github.com/babel/babel/issues/8361 const useApplicationContext = useApplicationContextHook; function ownKeys$5(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$5(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context = ownKeys$5(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context2 = ownKeys$5(Object(t))).call(_context2, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } const Context$1 = /*#__PURE__*/createContext({}); const CustomViewContextProvider = props => { const contextValue = useMemo(() => ({ hostUrl: props.hostUrl, customViewConfig: props.customViewConfig }), [props.hostUrl, props.customViewConfig]); return jsx(Context$1.Provider, { value: contextValue, children: props.children }); }; // Use function overloading to declare two possible signatures with two // distict return types, based on the selector function argument. CustomViewContextProvider.propTypes = process.env.NODE_ENV !== "production" ? { hostUrl: _pt.string.isRequired, customViewConfig: _pt.any.isRequired, children: _pt.node.isRequired } : {}; // Then implement the function. Typescript will pick the appropriate signature // based on the function arguments. function useCustomViewContextHook(selector) { // Because of the way the CustomViewShell configures the Context.Provider, // we ensure that, when we read from the context, we always get actual // context object and not the initial value. // Therefore, we can safely cast the value to be out `TCustomViewContext` type. const customViewContext = useContext(Context$1); const applicationContext = useApplicationContext(); const mergedContext = _objectSpread$5(_objectSpread$5({}, applicationContext), customViewContext); return selector ? selector(mergedContext) : mergedContext; } // This is a workaround to trick babel/rollup to correctly export the function. // Most likely the problem arises with the use of overloading. // See related issue: https://github.com/babel/babel/issues/8361 const useCustomViewContext = useCustomViewContextHook; function ownKeys$4(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$4(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context = ownKeys$4(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context2 = ownKeys$4(Object(t))).call(_context2, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } var FetchProjectExtensionImageRegex = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "query", name: { kind: "Name", value: "FetchProjectExtensionImageRegex" }, variableDefinitions: [], directives: [], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "projectExtension" }, arguments: [], directives: [], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" }, arguments: [], directives: [] }, { kind: "Field", name: { kind: "Name", value: "imageRegex" }, arguments: [], directives: [], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "thumb" }, arguments: [], directives: [], selectionSet: { kind: "SelectionSet", selections: [{ kind: "FragmentSpread", name: { kind: "Name", value: "ImageRegex" }, directives: [] }] } }, { kind: "Field", name: { kind: "Name", value: "small" }, arguments: [], directives: [], selectionSet: { kind: "SelectionSet", selections: [{ kind: "FragmentSpread", name: { kind: "Name", value: "ImageRegex" }, directives: [] }] } }] } }] } }] } }, { kind: "FragmentDefinition", name: { kind: "Name", value: "ImageRegex" }, typeCondition: { kind: "NamedType", name: { kind: "Name", value: "ImageRegexOptions" } }, directives: [], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "flag" }, arguments: [], directives: [] }, { kind: "Field", name: { kind: "Name", value: "search" }, arguments: [], directives: [] }, { kind: "Field", name: { kind: "Name", value: "replace" }, arguments: [], directives: [] }] } }], loc: { start: 0, end: 256, source: { body: "query FetchProjectExtensionImageRegex {\n projectExtension {\n id\n imageRegex {\n thumb {\n ...ImageRegex\n }\n small {\n ...ImageRegex\n }\n }\n }\n}\nfragment ImageRegex on ImageRegexOptions {\n flag\n search\n replace\n}\n", name: "GraphQL request", locationOffset: { line: 1, column: 1 } } } }; const useWarning = (condition, message) => { useEffect(() => { process.env.NODE_ENV !== "production" ? warning(condition, message) : void 0; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); }; const Context = /*#__PURE__*/createContext({ isLoading: false }); const useProjectExtensionImageRegex = () => { const _useContext = useContext(Context), isLoading = _useContext.isLoading, imageRegex = _useContext.imageRegex; return { isLoading, imageRegex }; }; const ProjectExtensionProviderForImageRegex = props => { const _useQuery = useQuery(FetchProjectExtensionImageRegex, { skip: props.skip, context: { target: GRAPHQL_TARGETS.SETTINGS_SERVICE } }), loading = _useQuery.loading, data = _useQuery.data; return jsx(Context.Provider, { value: { isLoading: loading, imageRegex: data && data.projectExtension && data.projectExtension.imageRegex }, children: props.children }); }; ProjectExtensionProviderForImageRegex.propTypes = process.env.NODE_ENV !== "production" ? { skip: _pt.bool, children: _pt.node.isRequired } : {}; ProjectExtensionProviderForImageRegex.displayName = 'ProjectExtensionProviderForImageRegex'; const GetProjectExtensionImageRegex = props => { useWarning(false, `@commercetools-frontend/application-shell-connectors: It is not recommended to use the 'GetProjectExtensionImageRegex' anymore. Please use the 'useProjectExtensionImageRegex' hook instead.`); return jsx(Context.Consumer, { children: imageRegexContext => props.render(imageRegexContext) }); }; GetProjectExtensionImageRegex.propTypes = process.env.NODE_ENV !== "production" ? { render: _pt.func.isRequired } : {}; GetProjectExtensionImageRegex.displayName = 'GetProjectExtensionImageRegex'; function withProjectExtensionImageRegex() { let propKey = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'imageRegexData'; return Component => { const WrappedComponent = props => { useWarning(false, `@commercetools-frontend/application-shell-connectors: It is not recommended to use the 'withProjectExtensionImageRegex' high order component anymore. Please use the 'useProjectExtensionImageRegex' hook instead.`); const imageregexContext = useProjectExtensionImageRegex(); return jsx(GetProjectExtensionImageRegex, { render: () => // @ts-ignore: relates to https://github.com/emotion-js/emotion/issues/3245 jsx(Component, _objectSpread$4(_objectSpread$4({}, props), {}, { [propKey]: imageregexContext })) }); }; WrappedComponent.displayName = `withProjectExtensionImageRegex(${getDisplayName(Component)})`; return WrappedComponent; }; } function ownKeys$3(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$3(e) { for (var r = 1; r < arguments.length; r++) { var _context2, _context3; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context2 = ownKeys$3(Object(t), !0)).call(_context2, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context3 = ownKeys$3(Object(t))).call(_context3, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } const getSkipTokenRetry = context => { const skipTokenRetry = Boolean(context.skipTokenRetry); return skipTokenRetry; }; const forwardTokenRetryHeader = headers => _objectSpread$3(_objectSpread$3({}, headers), {}, { [SUPPORTED_HEADERS.X_TOKEN_RETRY]: 'true' }); // This link retries requests to the CTP API that have been rejected // because of an invalid/expired oauth token. // To do so, we resend the request with the header "X-Force-Token: true" // so that the MC BE can issue a new token. // NOTE: the retry is not meant to work for the MC access token. const getDoesGraphQLTargetSupportTokenRetry = context => { var _context; const graphQLTarget = context.headers?.[SUPPORTED_HEADERS.X_GRAPHQL_TARGET] || context.headers?.[SUPPORTED_HEADERS.X_GRAPHQL_TARGET.toLowerCase()]; return Boolean(graphQLTarget && _includesInstanceProperty(_context = [GRAPHQL_TARGETS.COMMERCETOOLS_PLATFORM, GRAPHQL_TARGETS.ADMINISTRATION_SERVICE, GRAPHQL_TARGETS.SETTINGS_SERVICE, GRAPHQL_TARGETS.MERCHANT_CENTER_BACKEND]).call(_context, graphQLTarget)); }; const isHttpError = error => error.statusCode !== undefined || error.statusCode !== undefined; const isGraphQLError = error => _Array$isArray(error) && _someInstanceProperty(error).call(error, err => err.message !== undefined || err.extensions !== undefined); // Checks response from GraphQL in order to scan 401 errors and redirect the // user to the login page resetting the store and showing the proper message const errorLink = onError(_ref => { let graphQLErrors = _ref.graphQLErrors, networkError = _ref.networkError, operation = _ref.operation, forward = _ref.forward; if (networkError && isHttpError(networkError) && networkError.statusCode === STATUS_CODES.UNAUTHORIZED) { history.push(`/logout?reason=${LOGOUT_REASONS.UNAUTHORIZED}`); return; } // In case of graphql errors, we want to retry unauthenticated requests by // forcing our API to fetch a new token, using the `X-Force-Token` header. // https://www.apollographql.com/docs/link/links/error/#retrying-failed-requests // We need to do this as the `token-retry-link` only works for network errors. // https://www.apollographql.com/docs/link/links/retry/ const context = operation.getContext(); if (graphQLErrors && isGraphQLError(graphQLErrors)) { for (const err of graphQLErrors) { const isNonAuthenticatedViaExtensionCode = err?.extensions?.code === 'UNAUTHENTICATED'; /** * NOTE: * Not not all GraphQL APIs expose an `extensions` field in * each error. For those we have to use the `message` * property until they introduced support for the `extensions` * field. */ const isNonAuthenticatedViaCode = err?.message === 'invalid_token'; if ((isNonAuthenticatedViaExtensionCode || isNonAuthenticatedViaCode) && getDoesGraphQLTargetSupportTokenRetry(context) && !getSkipTokenRetry(context)) { operation.setContext(_ref2 => { let headers = _ref2.headers; return { headers: forwardTokenRetryHeader(headers) }; }); // retry the request, returning the new observable return forward(operation); } } } // Report unhandled errors to Sentry if (context.enableSentryErrorReporting && networkError) { reportErrorToSentry(networkError, { extra: { operationName: operation.operationName } }); } if (context.enableSentryErrorReporting && graphQLErrors) { for (const err of graphQLErrors) { reportErrorToSentry(err, { extra: { operationName: operation.operationName } }); } } return; }); // Attempt to load the `teamId` from sessionStorage function selectTeamIdFromStorage() { return window.sessionStorage.getItem(STORAGE_KEYS.ACTIVE_TEAM_ID) || window.app.__DEVELOPMENT__?.oidc?.teamId; } const staticUrlPathsInPositionOfProjectKey = ['login', 'logout', 'account']; // Attempt to extract the `:projectKey` from the URL. // If the value matches one of the values in the list above, // return `undefined` as we're not within a project context. function selectProjectKeyFromUrl() { let locationPath = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.location.pathname; let possibleProjectKey = ''; const pathParts = locationPath.split('/'); if (pathParts[1] === 'custom-views') { // Custom Views paths: /custom-views/:customViewId/projects/:projectKey possibleProjectKey = pathParts[4]; } else { // Application paths: /:projectKey/:applicationId possibleProjectKey = pathParts[1]; } return _includesInstanceProperty(staticUrlPathsInPositionOfProjectKey).call(staticUrlPathsInPositionOfProjectKey, possibleProjectKey) ? undefined : possibleProjectKey; } // Keep an in-memory reference to the apollo client that would be initialized // by the application. This is useful to retrieve on runtime the reference // to the apollo client in other static modules. let cachedApolloClient; const setCachedApolloClient = apolloClient => { cachedApolloClient = apolloClient; }; const getCachedApolloClient = () => cachedApolloClient; var FetchUserId = { kind: "Document", definitions: [{ kind: "OperationDefinition", operation: "query", name: { kind: "Name", value: "FetchUserId" }, variableDefinitions: [], directives: [], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", alias: { kind: "Name", value: "user" }, name: { kind: "Name", value: "me" }, arguments: [], directives: [], selectionSet: { kind: "SelectionSet", selections: [{ kind: "Field", name: { kind: "Name", value: "id" }, arguments: [], directives: [] }] } }] } }], loc: { start: 0, end: 46, source: { body: "query FetchUserId {\n user: me {\n id\n }\n}\n", name: "GraphQL request", locationOffset: { line: 1, column: 1 } } } }; function selectUserId() { const apolloClient = getCachedApolloClient(); if (!apolloClient) { return null; } try { const queryResult = apolloClient.readQuery({ query: FetchUserId }); if (queryResult && queryResult.user) { return queryResult.user.id; } } catch (e) { return null; } return null; } const VALID_ID_PART_FORMAT = /^[\w-/]+$/; const skipMalformedPart = part => part && VALID_ID_PART_FORMAT.test(part); function getCorrelationId() { var _context; let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, userId = _ref.userId; return _filterInstanceProperty(_context = ['mc', selectProjectKeyFromUrl(), userId, v4()]).call(_context, skipMalformedPart).join('/'); } const createApolloContextForProxyForwardTo = proxyForwardTocontext => ({ // Send the request to the forward-to endpoint. uri: `${getMcApiUrl()}/proxy/forward-to`, // Custom properties to be used by the "header-link". forwardToConfig: proxyForwardTocontext, skipGraphQlTargetCheck: true }); /* eslint-disable no-console */ const isLoggerEnabled = () => { if (process.env.DEBUG === 'true') return true; if (process.env.NODE_ENV === 'development') return true; const queryParams = new _URL(window.location.href); if (process.env.NODE_ENV === 'production' && queryParams.searchParams.get('debug') === 'true') return true; return false; }; const logger = { groupCollapsed: function () { return isLoggerEnabled() && console.groupCollapsed(...arguments); }, groupEnd: () => isLoggerEnabled() && console.groupEnd(), info: function () { return isLoggerEnabled() && console.info(...arguments); }, log: function () { return isLoggerEnabled() && console.log(...arguments); }, error: function () { return isLoggerEnabled() && console.error(...arguments); }, warn: function () { return isLoggerEnabled() && console.warn(...arguments); } }; const getSessionToken = () => window.sessionStorage.getItem(STORAGE_KEYS.SESSION_TOKEN); const setActiveSession = sessionToken => { if (!sessionToken) return; window.sessionStorage.setItem(STORAGE_KEYS.SESSION_TOKEN, sessionToken ?? ''); window.localStorage.setItem(STORAGE_KEYS.LOGIN_STRATEGY, LOGIN_STRATEGY_OIDC); // Remove flag for original workflow window.localStorage.removeItem(STORAGE_KEYS.IS_AUTHENTICATED); }; const clearSession = () => { window.sessionStorage.removeItem(STORAGE_KEYS.SESSION_TOKEN); window.sessionStorage.removeItem(STORAGE_KEYS.SESSION_SCOPE); window.sessionStorage.removeItem(STORAGE_KEYS.IS_AUTHENTICATED); }; const getActiveProjectKey = () => window.localStorage.getItem(STORAGE_KEYS.ACTIVE_PROJECT_KEY); const removeActiveProjectKey = () => { window.localStorage.removeItem(STORAGE_KEYS.ACTIVE_PROJECT_KEY); }; const setActiveProjectKey = projectKey => { window.localStorage.setItem(STORAGE_KEYS.ACTIVE_PROJECT_KEY, projectKey); }; const getSessionScope = () => window.sessionStorage.getItem(STORAGE_KEYS.SESSION_SCOPE); const setSessionScope = scope => { window.sessionStorage.setItem(STORAGE_KEYS.SESSION_SCOPE, scope); }; const getSessionState = stateId => { const sessionStateKey = `${STORAGE_KEYS.NONCE}_${stateId}`; const unparsedSessionState = window.sessionStorage.getItem(sessionStateKey); if (unparsedSessionState) { try { const parsedSessionState = JSON.parse(unparsedSessionState); return parsedSessionState; } catch (error) { if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line no-console console.warn(`Cannot parse session state for "${sessionStateKey}".\n${unparsedSessionState}`); } } } return null; }; const setSessionState = (stateId, state) => { const sessionStateKey = `${STORAGE_KEYS.NONCE}_${stateId}`; window.sessionStorage.setItem(sessionStateKey, _JSON$stringify(state)); }; const removeSessionState = stateId => { const sessionStateKey = `${STORAGE_KEYS.NONCE}_${stateId}`; window.sessionStorage.removeItem(sessionStateKey); }; var oidcStorage = /*#__PURE__*/Object.freeze({ __proto__: null, getSessionToken: getSessionToken, setActiveSession: setActiveSession, clearSession: clearSession, getSessionState: getSessionState, setSessionState: setSessionState, removeSessionState: removeSessionState, getActiveProjectKey: getActiveProjectKey, setActiveProjectKey: setActiveProjectKey, removeActiveProjectKey: removeActiveProjectKey, getSessionScope: getSessionScope, setSessionScope: setSessionScope }); function _createSuper(t) { var r = _isNativeReflectConstruct(); return function () { var e, o = _getPrototypeOf(t); if (r) { var s = _getPrototypeOf(this).constructor; e = _Reflect$construct(o, arguments, s); } else e = o.apply(this, arguments); return _possibleConstructorReturn(this, e); }; } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function () { return !!t; })(); } function ownKeys$2(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$2(e) { for (var r = 1; r < arguments.length; r++) { var _context2, _context3; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context2 = ownKeys$2(Object(t), !0)).call(_context2, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context3 = ownKeys$2(Object(t))).call(_context3, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } let _userAgent$1; const getUserAgent$1 = () => { if (!_userAgent$1) { _userAgent$1 = createHttpUserAgent({ name: 'unknown-http-client', libraryName: typeof window !== 'undefined' ? window.app?.applicationName ?? 'unknown-application-name' : undefined }); } return _userAgent$1; }; const defaultForwardToVersion = 'v2'; const defaultForwardToAudiencePolicy = 'forward-url-full-path'; function buildApiUrl(endpoint) { const apiUrl = getMcApiUrl().replace(/\/$/, ''); return `${apiUrl}${endpoint}`; } const getAppliedForwardToHeaders = forwardToConfig => { var _context; if (!forwardToConfig) { return {}; } if (!forwardToConfig.uri) { throw new Error(`Missing required "uri" option.`); } const exchangeTokenClaims = []; if (forwardToConfig.includeUserPermissions) { exchangeTokenClaims.push('permissions'); } return _objectSpread$2(_objectSpread$2({}, _reduceInstanceProperty(_context = _Object$entries(forwardToConfig.headers ?? {})).call(_context, (customForwardHeaders, _ref) => { let _ref2 = _slicedToArray(_ref, 2), headerName = _ref2[0], headerValue = _ref2[1]; return _objectSpread$2(_objectSpread$2({}, customForwardHeaders), {}, { // Prefix headers so that the MC API can allow and forward them. [`x-forward-header-${headerName}`]: headerValue }); }, {})), {}, { [SUPPORTED_HEADERS.ACCEPT_VERSION]: forwardToConfig.version ?? defaultForwardToVersion, [SUPPORTED_HEADERS.X_FORWARD_TO]: forwardToConfig.uri, [SUPPORTED_HEADERS.X_FORWARD_TO_AUDIENCE_POLICY]: forwardToConfig.audiencePolicy ?? defaultForwardToAudiencePolicy, [SUPPORTED_HEADERS.X_FORWARD_TO_CLAIMS]: exchangeTokenClaims.join(' ') }); }; function createHttpClientOptions() { let config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; const sessionToken = getSessionToken(); const projectKey = config.projectKey ?? selectProjectKeyFromUrl(); const userId = selectUserId(); const userAgent = config?.userAgent || getUserAgent$1(); return { credentials: 'include', headers: omitEmpty(_objectSpread$2(_objectSpread$2({}, config.headers), {}, { // Required headers [SUPPORTED_HEADERS.ACCEPT]: 'application/json', [SUPPORTED_HEADERS.AUTHORIZATION]: sessionToken ? `Bearer ${sessionToken}` : undefined, [SUPPORTED_HEADERS.X_APPLICATION_ID]: window.app.applicationIdentifier, [SUPPORTED_HEADERS.X_CUSTOM_VIEW_ID]: window.app.customViewId, [SUPPORTED_HEADERS.X_CORRELATION_ID]: getCorrelationId({ userId }), [SUPPORTED_HEADERS.X_PROJECT_KEY]: projectKey, [SUPPORTED_HEADERS.X_USER_AGENT]: userAgent }, getAppliedForwardToHeaders(config.forwardToConfig))) }; } let RenewTokenError = /*#__PURE__*/function (_Error) { _inherits(RenewTokenError, _Error); var _super = _createSuper(RenewTokenError); function RenewTokenError(message) { var _this; _classCallCheck(this, RenewTokenError); _this = _super.call(this, message); _Object$defineProperty(_assertThisInitialized(_this), 'name', { value: 'RenewTokenError' }); return _this; } return _createClass(RenewTokenError); }(/*#__PURE__*/_wrapNativeSuper(Error)); async function executeHttpClientRequest(fetcher) { let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; // Wrapper function to be called again (once) on retry. async function sendRequest() { let _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, shouldRenewToken = _ref3.shouldRenewToken; const requestOptions = createHttpClientOptions(config); const response = await fetcher(_objectSpread$2(_objectSpread$2({}, requestOptions), {}, { headers: omitEmpty(_objectSpread$2(_objectSpread$2({}, requestOptions.headers), {}, { // Passing this header forces a token renewal. [SUPPORTED_HEADERS.X_TOKEN_RETRY]: shouldRenewToken })) })); if (response.statusCode === STATUS_CODES.UNAUTHORIZED) { throw new RenewTokenError(`Unauthorized response, attempting retry.`); } // In case a new session token is returned from the server, save it. const refreshedSessionToken = response.getHeader('x-refreshed-session-token'); if (refreshedSessionToken) { setActiveSession(refreshedSessionToken); } return response.data; } // Attempt to send the request. return sendRequest().catch(error => { if (error instanceof RenewTokenError) { // Retry the request and ask to renew the token. return sendRequest({ shouldRenewToken: true }); } throw error; }); } function ownKeys$1(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread$1(e) { for (var r = 1; r < arguments.length; r++) { var _context4, _context5; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context4 = ownKeys$1(Object(t), !0)).call(_context4, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context5 = ownKeys$1(Object(t))).call(_context5, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } let _userAgent; const getUserAgent = () => { if (!_userAgent) { var _context; _userAgent = createHttpUserAgent({ name: 'apollo-client', // version: apolloVersion, libraryName: _filterInstanceProperty(_context = [typeof window !== 'undefined' ? window.app?.applicationName ?? 'unknown-application-name' : undefined, 'application-shell']).call(_context, Boolean).join('/'), libraryVersion: version, contactUrl: 'https://git.io/fjuyC', // points to the appkit repo issues contactEmail: 'support@commercetools.com' }); } return _userAgent; }; const isKnownGraphQlTarget = target => { var _context2; return target ? _includesInstanceProperty(_context2 = _Object$values(GRAPHQL_TARGETS)).call(_context2, target) : false; }; const extractSessionTokenFromResponse = context => { const refreshedSessionToken = context.response?.headers?.get('x-refreshed-session-token'); if (refreshedSessionToken) { return refreshedSessionToken ?? null; } const restResponseWithRefreshTokenHeader = context.restResponses?.find(response => response.headers?.has('x-refreshed-session-token')); if (restResponseWithRefreshTokenHeader) { return restResponseWithRefreshTokenHeader.headers.get('x-refreshed-session-token') ?? null; } return null; }; /* eslint-disable import/prefer-default-export */ // Use a middleware to update the request headers with the correct params. const headerLink = new ApolloLink((operation, forward) => { var _context3; const apolloContext = operation.getContext(); const variables = operation.variables; const graphQlTarget = apolloContext.target || variables.target; if (!apolloContext.skipGraphQlTargetCheck && !isKnownGraphQlTarget(graphQlTarget)) throw new Error(`GraphQL target "${graphQlTarget}" is missing (or is not supported) in operation "${operation.operationName}"`); /** * NOTE: * The project key is read from the url in a project related application context. * This holds for most applications like `application-categories`, `application-discounts` etc. * However, the `application-account` does not run with the project key being part of the url. * As a result we allow passing the project key as a variable on the operation allowing * it to be the fallback. */ const projectKey = apolloContext.projectKey || variables.projectKey || selectProjectKeyFromUrl(); const teamId = apolloContext.teamId || variables.teamId || selectTeamIdFromStorage(); const featureFlag = apolloContext.featureFlag || variables.featureFlag; operation.setContext(createHttpClientOptions({ userAgent: getUserAgent(), headers: omitEmpty(_objectSpread$1(_objectSpread$1({}, apolloContext.headers), {}, { // Required headers for GraphQL API. [SUPPORTED_HEADERS.X_GRAPHQL_TARGET]: graphQlTarget, // For logging/debugging purposes. [SUPPORTED_HEADERS.X_GRAPHQL_OPERATION_NAME]: operation.operationName, // Experimental features, use with caution. [SUPPORTED_HEADERS.X_TEAM_ID]: teamId, [SUPPORTED_HEADERS.X_FEATURE_FLAG]: featureFlag })), forwardToConfig: apolloContext.forwardToConfig, projectKey })); return _mapInstanceProperty(_context3 = forward(operation)).call(_context3, response => { const context = operation.getContext(); const refreshedSessionToken = extractSessionTokenFromResponse(context); if (refreshedSessionToken) { setActiveSession(refreshedSessionToken); } return response; }); }); const tokenRetryLink = new RetryLink({ attempts: (count, operation, error) => { const context = operation.getContext(); if (error?.statusCode === STATUS_CODES.UNAUTHORIZED && count === 1 && getDoesGraphQLTargetSupportTokenRetry(context) && !getSkipTokenRetry(context)) { operation.setContext(_ref => { let headers = _ref.headers; return { headers: forwardTokenRetryHeader(headers) }; }); return true; } return false; } }); function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context = ownKeys(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context2 = ownKeys(Object(t))).call(_context2, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } const createApolloLink = function () { let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; const httpLink = createHttpLink({ uri: `${getMcApiUrl()}/graphql`, fetch }); // The order of links is IMPORTANT! // In the request-phase they are executed top to bottom. // In the response/phase they are executed bottom to top. // The `httpLink` is terminating so it must be last, while `tokenRetryLink` and `errorLink` // wrap the links the their right. return from([headerLink, // See https://www.apollographql.com/docs/react/api/link/apollo-link-rest/#link-order /