UNPKG

@commercetools-frontend/sdk

Version:
575 lines (550 loc) • 25.7 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 _slicedToArray from '@babel/runtime-corejs3/helpers/esm/slicedToArray'; import _defineProperty from '@babel/runtime-corejs3/helpers/esm/defineProperty'; import _reduceInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/reduce'; import _Object$entries from '@babel/runtime-corejs3/core-js-stable/object/entries'; import omitEmpty from 'omit-empty-es'; import _Reflect$construct from '@babel/runtime-corejs3/core-js-stable/reflect/construct'; import _classCallCheck from '@babel/runtime-corejs3/helpers/esm/classCallCheck'; import _createClass from '@babel/runtime-corejs3/helpers/esm/createClass'; import _possibleConstructorReturn from '@babel/runtime-corejs3/helpers/esm/possibleConstructorReturn'; import _getPrototypeOf from '@babel/runtime-corejs3/helpers/esm/getPrototypeOf'; import _inherits from '@babel/runtime-corejs3/helpers/esm/inherits'; import { Component } from 'react'; import { deepEqual } from 'fast-equals'; import { connect, useDispatch } from 'react-redux'; import _concatInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/concat'; import { createRequestBuilder } from '@commercetools/api-request-builder'; import { SHOW_LOADING, STATUS_CODES, HIDE_LOADING } from '@commercetools-frontend/constants'; import _URL from '@babel/runtime-corejs3/core-js-stable/url'; import { decode } from 'qss'; import _globalThis from '@babel/runtime-corejs3/core-js/global-this'; import { Buffer } from 'buffer'; import createHttpUserAgent from '@commercetools/http-user-agent'; import { createClient as createClient$1 } from '@commercetools/sdk-client'; import { createCorrelationIdMiddleware as createCorrelationIdMiddleware$1 } from '@commercetools/sdk-middleware-correlation-id'; import { createHttpMiddleware } from '@commercetools/sdk-middleware-http'; 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; } function get(payload) { return { type: 'SDK', payload: _objectSpread$3(_objectSpread$3({}, payload), {}, { method: 'GET' }) }; } // contrary to the other methods this does not bear the exact name of the HTTP-verb // because `delete` is a reserved keyword in ECMAScript function del(payload) { return { type: 'SDK', payload: _objectSpread$3(_objectSpread$3({}, payload), {}, { method: 'DELETE' }) }; } function head(payload) { return { type: 'SDK', payload: _objectSpread$3(_objectSpread$3({}, payload), {}, { method: 'HEAD' }) }; } function post(payload) { return { type: 'SDK', payload: _objectSpread$3(_objectSpread$3({}, payload), {}, { method: 'POST' }) }; } const enhancePayloadForForwardToProxy = payload => { var _payload$headers, _context; const headers = (_payload$headers = payload.headers) !== null && _payload$headers !== void 0 ? _payload$headers : {}; const exchangeTokenClaims = []; if (payload.includeUserPermissions) { exchangeTokenClaims.push('permissions'); } return { uri: '/proxy/forward-to', mcApiProxyTarget: undefined, headers: omitEmpty(_objectSpread$3(_objectSpread$3({}, _reduceInstanceProperty(_context = _Object$entries(headers)).call(_context, (customForwardHeaders, _ref) => { let _ref2 = _slicedToArray(_ref, 2), headerName = _ref2[0], headerValue = _ref2[1]; return _objectSpread$3(_objectSpread$3({}, customForwardHeaders), {}, { // Prefix headers so that the MC API can allow and forward them. ["x-forward-header-".concat(headerName)]: headerValue }); }, {})), {}, { 'Accept-version': 'v2', 'X-Forward-To': payload.uri, 'X-Forward-To-Audience-Policy': payload.audiencePolicy || 'forward-url-full-path', 'X-Forward-To-Claims': exchangeTokenClaims.join(' ') })) }; }; const forwardTo = { get(payload) { return { type: 'SDK', payload: _objectSpread$3(_objectSpread$3({}, payload), {}, { method: 'GET' }, enhancePayloadForForwardToProxy(payload)) }; }, del(payload) { return { type: 'SDK', payload: _objectSpread$3(_objectSpread$3({}, payload), {}, { method: 'DELETE' }, enhancePayloadForForwardToProxy(payload)) }; }, head(payload) { return { type: 'SDK', payload: _objectSpread$3(_objectSpread$3({}, payload), {}, { method: 'HEAD' }, enhancePayloadForForwardToProxy(payload)) }; }, post(payload) { return { type: 'SDK', payload: _objectSpread$3(_objectSpread$3({}, payload), {}, { method: 'POST' }, enhancePayloadForForwardToProxy(payload)) }; } }; var index = /*#__PURE__*/Object.freeze({ __proto__: null, get: get, del: del, head: head, post: post, forwardTo: forwardTo }); function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? _Reflect$construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(_Reflect$construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function () { return !!t; })(); } const defaultProps = { actionCreatorArgs: [], shouldRefetch: (prevArgs, nextArgs) => !deepEqual(prevArgs, nextArgs) }; let SdkGet = /*#__PURE__*/function (_Component) { function SdkGet() { var _this; _classCallCheck(this, SdkGet); for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _callSuper(this, SdkGet, [...args]); _this.state = { // We want the component to have a loading state by default, so we // keep track of whether the first request has completed. // We can't use requestsInFlight only as that would lead to a flash of // the loading state until the first request starts in componentDidMount. isWaitingForCompletionOfFirstRequest: true, requestsInFlight: 0, result: undefined, error: undefined }; _this.isComponentMounted = false; _this.changeRequestsInFlight = delta => { if (_this.isComponentMounted) _this.setState(prevState => ({ requestsInFlight: prevState.requestsInFlight + delta })); }; _this.fetch = _ref => { let dispatch = _ref.dispatch, actionCreator = _ref.actionCreator, actionCreatorArgs = _ref.actionCreatorArgs, onSuccess = _ref.onSuccess, onError = _ref.onError; _this.changeRequestsInFlight(1); return dispatch(actionCreator(...(actionCreatorArgs !== null && actionCreatorArgs !== void 0 ? actionCreatorArgs : defaultProps.actionCreatorArgs))).then(result => { _this.changeRequestsInFlight(-1); if (_this.isComponentMounted) _this.setState({ error: undefined, result }); if (onSuccess) onSuccess(result); return result; }, error => { _this.changeRequestsInFlight(-1); if (_this.isComponentMounted) _this.setState({ error, result: undefined }); if (onError) onError(error);else SdkGet.errorHandler(error); }); }; _this.refresh = () => _this.fetch({ dispatch: _this.props.dispatch, actionCreator: _this.props.actionCreator, actionCreatorArgs: _this.props.actionCreatorArgs, onSuccess: _this.props.onSuccess, onError: _this.props.onError }); return _this; } _inherits(SdkGet, _Component); return _createClass(SdkGet, [{ key: "componentDidMount", value: function componentDidMount() { this.isComponentMounted = true; this.fetch({ dispatch: this.props.dispatch, actionCreator: this.props.actionCreator, actionCreatorArgs: this.props.actionCreatorArgs, onSuccess: this.props.onSuccess, onError: this.props.onError }).then(result => { if (this.isComponentMounted) this.setState({ isWaitingForCompletionOfFirstRequest: false }); return result; }, error => { if (this.isComponentMounted) this.setState({ isWaitingForCompletionOfFirstRequest: false }); throw error; }); } }, { key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { var _this$props$shouldRef, _prevProps$actionCrea, _this$props$actionCre; const shouldRefetch = (_this$props$shouldRef = this.props.shouldRefetch) !== null && _this$props$shouldRef !== void 0 ? _this$props$shouldRef : defaultProps.shouldRefetch; if (shouldRefetch((_prevProps$actionCrea = prevProps.actionCreatorArgs) !== null && _prevProps$actionCrea !== void 0 ? _prevProps$actionCrea : defaultProps.actionCreatorArgs, (_this$props$actionCre = this.props.actionCreatorArgs) !== null && _this$props$actionCre !== void 0 ? _this$props$actionCre : defaultProps.actionCreatorArgs)) { this.fetch({ dispatch: this.props.dispatch, actionCreator: this.props.actionCreator, actionCreatorArgs: this.props.actionCreatorArgs, onSuccess: this.props.onSuccess, onError: this.props.onError }); } } }, { key: "componentWillUnmount", value: function componentWillUnmount() { this.isComponentMounted = false; } }, { key: "render", value: function render() { return this.props.render({ isLoading: this.state.requestsInFlight > 0 || this.state.isWaitingForCompletionOfFirstRequest, refresh: this.refresh, result: this.state.result, error: this.state.error }); } }]); }(Component); SdkGet.displayName = 'SdkGet'; SdkGet.errorHandler = error => { throw error; }; const mapDispatchToProps = dispatch => ({ dispatch }); var SdkGet$1 = connect(null, mapDispatchToProps)(SdkGet); // NOTE: This string will be replaced on build time with the package version. var version = "25.0.0"; /* 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); } }; 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 _context3, _context4; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context3 = ownKeys$2(Object(t), !0)).call(_context3, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context4 = ownKeys$2(Object(t))).call(_context4, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } const parseUri = uri => { const parser = document.createElement('a'); parser.href = uri; return { pathname: parser.pathname, search: decode(parser.search.substring(1)) }; }; const isPostAction = action => action.payload.method === 'POST'; const logRequest = _ref => { var _context; let method = _ref.method, request = _ref.request, response = _ref.response, error = _ref.error, action = _ref.action; const uriParts = parseUri(request.uri); const groupName = _concatInstanceProperty(_context = "%c".concat(method, " %c")).call(_context, uriParts.pathname); logger.groupCollapsed(groupName, "color: ".concat(error ? 'red' : 'black', "; font-weight: bold;"), 'color: gray; font-weight: lighter;'); logger.log('%caction', 'color: cadetblue; font-weight: bold;', action); logger.log('%crequest', "color: cornflowerblue; font-weight: bold;", _objectSpread$2({ headers: request.headers, uri: request.uri, params: uriParts.search }, isPostAction(action) ? { body: action.payload.payload } : {})); if (response) logger.log('%cresponse', "color: green; font-weight: bold;", response); if (error) logger.log('%cerror', "color: red; font-weight: bold;", error); logger.groupEnd(); }; 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 = actualWindow => { var _context2; const url = new _URL(actualWindow.origin); const originTld = getMcOriginTld(url.host); return _concatInstanceProperty(_context2 = "".concat(url.protocol, "//mc-api.")).call(_context2, originTld); }; const parseAsBoolean = value => value === true || value === 'true'; function getMcApiUrl() { let actualWindow = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window; const isServedByProxy = parseAsBoolean(actualWindow.app.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(actualWindow); } return actualWindow.app.mcApiUrl; } 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 _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context = ownKeys$1(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context2 = ownKeys$1(Object(t))).call(_context2, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } // This is currently required by @commercetools/sdk-middleware-http package _globalThis.Buffer = Buffer; const userAgent = createHttpUserAgent({ name: '@commercetools/sdk-client', libraryName: [typeof window !== 'undefined' ? window.app.applicationName : 'unknown-application-name', 'sdk'].join('/'), libraryVersion: version, contactUrl: 'https://git.io/fjuyC', // points to the appkit repo issues contactEmail: 'mc@commercetools.com' }); const customUserAgentMiddleware = next => (request, response) => { const requestWithCustomUserAgent = _objectSpread$1(_objectSpread$1({}, request), {}, { headers: _objectSpread$1(_objectSpread$1({}, request.headers), {}, { 'X-User-Agent': userAgent }) }); next(requestWithCustomUserAgent, response); }; // NOTE we should not use these directly but rather have them passed in from // the application const httpMiddleware = createHttpMiddleware({ host: getMcApiUrl(), includeResponseHeaders: true, credentialsMode: 'include', fetch }); const createCorrelationIdMiddleware = _ref => { let getCorrelationId = _ref.getCorrelationId; return createCorrelationIdMiddleware$1({ generate: getCorrelationId }); }; const createClient = _ref2 => { let getCorrelationId = _ref2.getCorrelationId; return createClient$1({ middlewares: [createCorrelationIdMiddleware({ getCorrelationId }), customUserAgentMiddleware, httpMiddleware] }); }; 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 _context4, _context5; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context4 = ownKeys(Object(t), !0)).call(_context4, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context5 = ownKeys(Object(t))).call(_context5, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; } const isSdkActionForUri = actionPayload => actionPayload.uri !== undefined; // https://github.com/commercetools/nodejs/blob/master/packages/api-request-builder/src/default-services.js#L200:L200 const ORDER_EDIT_SERVICE = 'orderEdits'; const actionToUri = (action, projectKey) => { var _action$payload$optio; if (isSdkActionForUri(action.payload)) return action.payload.uri; // Validate that `projectKey` exists if (!projectKey) { var _context; throw new Error(_concatInstanceProperty(_context = "Expected projectKey to be defined for action service \"".concat(action.payload.service, "\" (method \"")).call(_context, action.payload.method, "\")")); } const requestBuilder = createRequestBuilder({ projectKey }); // NOTE it's weird that we have to access this from the request builder. // Shouldn't it just be a part of the object we parse? // NOTE shouldn't requestBuilder be called requestUriBuilder instead? const service = requestBuilder[action.payload.service]; if (action.payload.options) service.parse(action.payload.options); return service.build({ // given `service=orderEdits` and given `applyOrderEditTo`, we build an apply endpoint // given `service=orderEdits` and no `applyOrderEditTo`, we build an update endpoint // https://docs.commercetools.com/api/projects/order-edits applyOrderEdit: action.payload.service === ORDER_EDIT_SERVICE && typeof ((_action$payload$optio = action.payload.options) === null || _action$payload$optio === void 0 ? void 0 : _action$payload$optio.applyOrderEditTo) === 'string', // at this stage, the `projectKey` should be available already. withProjectKey: true }); }; // Force TS cast of generic action to TNotificationAction const isSdkAction = action => action.type === 'SDK'; const isSdkError = error => error.statusCode !== undefined; function createSdkMiddleware(_ref) { let getCorrelationId = _ref.getCorrelationId, getProjectKey = _ref.getProjectKey, getAdditionalHeaders = _ref.getAdditionalHeaders; const client = createClient({ getCorrelationId }); const middleware = _ref2 => { let dispatch = _ref2.dispatch; return next => action => { var _context2, _context3; if (!isSdkAction(action)) { return next(action); } const projectKey = getProjectKey(); const uri = _filterInstanceProperty(_context2 = [action.payload.mcApiProxyTarget && "/proxy/".concat(action.payload.mcApiProxyTarget), actionToUri(action, projectKey)]).call(_context2, Boolean).join(''); // This `requestName` is never really used. // // We keep track of requests which are in progress in the `loading` state of // the application. The `loading` state is an array of strings // (which are correlation Ids, action types or request names). // This is just done so that debugging is easier. // // It's easier to debug // loading: ['PRODUCTS_FETCHED', 'sdk.fetch(/product-projection-search)'] // than to debug // loading: 2 const requestName = _concatInstanceProperty(_context3 = "sdk.".concat(action.payload.method.toLowerCase(), "(")).call(_context3, uri, ")"); // NOTE here the middleware is aware of the application // Instead we should probably convert to a middleware factory // and provide hooks for `onFetch`, `onResult` and `onError dispatch({ type: SHOW_LOADING, payload: requestName }); // NOTE the promise returned by the client resolves to a custom format // it will contain { statusCode, headers, body } // NOTE This retry logic could be moved to an sdk client middleware, // but the client's middleware system is not capable of that right now // https://github.com/commercetools/merchant-center-frontend/pull/3304 // https://github.com/commercetools/nodejs/issues/390 const sendRequest = function () { let _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, shouldRenewToken = _ref3.shouldRenewToken; const additionalHeaders = getAdditionalHeaders(); const headers = _objectSpread(_objectSpread(_objectSpread(_objectSpread({ Accept: 'application/json' }, action.payload.headers || {}), shouldRenewToken ? { 'X-Force-Token': 'true' } : {}), projectKey && { 'X-Project-Key': projectKey }), additionalHeaders !== null && additionalHeaders !== void 0 ? additionalHeaders : {}); const body = action.payload.method === 'POST' ? action.payload.payload : undefined; return client.execute({ uri, method: action.payload.method, headers, body }).then(result => { if (process.env.NODE_ENV === 'development') logRequest({ method: action.payload.method, request: { headers, uri }, response: result.body, action }); return result; }, error => { if (process.env.NODE_ENV === 'development') logRequest({ method: action.payload.method, request: { headers, uri }, error, action }); throw error; }); }; return sendRequest().catch(error => { // in case of 401 error, try again with a new token // https://github.com/commercetools/merchant-center-backend/blob/master/docs/AUTHENTICATION.md#problems-due-to-oauth-token-caching if (isSdkError(error) && error.statusCode === STATUS_CODES.UNAUTHORIZED) { return sendRequest({ shouldRenewToken: true }); } throw error; }).then(result => { dispatch({ type: HIDE_LOADING, payload: requestName }); // The promise returned by "fetch" will reject when the request fails, // but only in certain cases. See "Checking that the fetch was successful" // in https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch // The SDK already handles this case for us. return result.body; }, error => { dispatch({ type: HIDE_LOADING, payload: requestName }); throw error; }); }; }; return middleware; } // Wraps `dispatch` and cast the return type to a Promise, as the middleware // returns a Promise. function useAsyncDispatch() { const dispatch = useDispatch(); return dispatch; } const Sdk = { Get: SdkGet$1 }; export { Sdk, index as actions, createSdkMiddleware as createMiddleware, useAsyncDispatch, version };