Untitled
unknown
plain_text
10 months ago
7.9 kB
7
Indexable
import RestClient from '@/util/api';
import '@/core/api';
import { RestClientError } from '@/core/RestClientError';
import ScId from '@/onCommerce/sc-common/ScId';
import { Trans, useTranslation } from 'react-i18next';
import useGlobalNotification from '@/core/GlobalNotification/useGlobalNotification';
import { QueryKey, useQueryClient } from 'react-query';
import { TFunction } from 'i18next';
import { Client, Message, Subscription } from 'webstomp-client';
import { useClient } from '@/portal/views/ClientProvider';
import { useEffect } from 'react';
import TextLink from '@/core/components/TextLink/TextLink';
import { GlobalNotificationContextState } from '@/core/GlobalNotification/GlobalNotificationContext';
/**
* Convenience wrapper around the RestClient in order to simplify read and write operations in screen components.
*/
export default class ScBackend {
/**
* Wrapper so send a read operation in screen components. The base part for the URL is already set here 'api/uib/onCommerce/'
* @param url The url of the operation without slash at the beginning e.g. 'basket/data'
* @param params Any request params to append to the URL (optional; disallows url-embedding; converts via Object.entries() with basic support for non-nested arrays).
* @param scId The screen component ID.
*/
public static read(url: string, scId: ScId, params?: object): any {
if (params != null) {
url += `?${this.buildQueryParams(params)}`;
}
return RestClient.get(`./api/uib/onCommerce/${url}`, null, {
headers: {
'Component-Id': scId.toRequestHeaderValue(),
},
}).catch(err => {
throw new RestClientError(err, err.status);
});
}
private static buildQueryParams(parameterObject: object): URLSearchParams {
const params = new URLSearchParams();
Object.entries(parameterObject).forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach(v => params.append(key, this.encodeQueryParam(v)));
} else if (value != null) {
params.append(key, this.encodeQueryParam(value));
}
});
return params;
}
private static encodeQueryParam(param: unknown): string {
if (
typeof param === 'string' ||
typeof param === 'number' ||
typeof param === 'boolean'
) {
return encodeURIComponent(param);
} else {
return encodeURIComponent(String(param));
}
}
/**
* Wrapper so send a write operation in screen components. The base part for the URL is already set here 'api/uib/onCommerce/'
* @param url The url of the operation without slash at the beginning e.g. 'basket/data'
* @param data The data of the operation.
* @param scId The screen component ID.
*/
public static write(url: string, data: any, scId: ScId): any {
return RestClient.post(`./api/uib/onCommerce/${url}`, data, {
headers: {
'Component-Id': scId.toRequestHeaderValue(),
},
}).catch((error: any) => {
throw new RestClientError(error);
});
}
}
/**
* Error handler displaying a given error or default fallback message
* @param eShopErrorPrefix Error message to be attached to displayed when no specific backend message
*/
export const handleErrorWithFallback = (
t: TFunction,
notification: GlobalNotificationContextState,
eShopErrorPrefix?: string
) => {
return (error: RestClientError) => {
let message;
const translationKey = error?.responseJSON?.translationKey;
const translationData = error?.responseJSON?.translationData;
if (
(translationKey && !translationData) ||
(translationKey &&
translationData &&
!Object.keys(translationData).length)
) {
message = t(translationKey);
} else if (
translationKey &&
translationData &&
Object.keys(translationData).length
) {
message = t(translationKey, {
...translationData,
});
} else if (eShopErrorPrefix) {
message = (
<>
{`${eShopErrorPrefix} `}
<Trans i18nKey="errorHandling:contactEshopSupport" t={t}>
{' '}
<TextLink
text={'support-omnipluseshop-hq@daimlertruck.com'}
url={`mailto:support-omnipluseshop-hq@daimlertruck.com`}
/>{' '}
</Trans>
</>
);
} else {
message = t('errorHandling:system.failure', {
systemName: 'Backend',
});
}
notification.showNotification({
message,
severity: 'error',
});
};
};
/**
* Error handler displaying a global notification for a given error
* @param eShopErrorPrefix Error message to be attached to displayed when no specific backend message
*/
export const useHandleErrorWithFallback = (eShopErrorPrefix?: string) => {
const { t } = useTranslation(['common', 'errorHandling', 'htmlMail']);
const notification = useGlobalNotification();
return handleErrorWithFallback(t, notification, eShopErrorPrefix);
};
/**
* Default mutation handler overwriting the query data for the given QueryKey
* with the new data received on success and displaying a success notification.
*/
export const useDefaultMutationOptions = <T,>(queryKey: QueryKey) => {
const { t } = useTranslation(['common', 'errorHandling']);
const notification = useGlobalNotification();
const queryClient = useQueryClient();
return {
onError: handleErrorWithFallback(t, notification),
onSuccess: (result: T) => {
queryClient.setQueryData(queryKey, result);
const message = t('common:editSuccess');
notification.showNotification({
message,
severity: 'success',
});
},
};
};
/**
* Mutation handler displaying a notification only in case of an error.
*/
export const useSilentActionMutationOptions = () => {
const { t } = useTranslation(['errorHandling']);
const notification = useGlobalNotification();
return {
onError: handleErrorWithFallback(t, notification),
};
};
/**
* Mutation handler displaying a notification on error and success but not
* overwriting the query cache.
*/
export const useActionMutationOptions = <T,>() => {
const { t } = useTranslation(['common', 'errorHandling']);
const notification = useGlobalNotification();
return {
onError: handleErrorWithFallback(t, notification),
onSuccess: () => {
const message = t('common:editSuccess');
notification.showNotification({
message,
severity: 'success',
});
},
};
};
interface WebsocketSignal {
signal: string;
}
/**
* Hook for subscribing to the standard refetch signal for an SC topic.
*/
export const useRefetchSubscription = (
topic: SubscriptionTopic,
action: () => void
) => {
return useSubscription(topic, (message?: WebsocketSignal) => {
if (message?.signal === 'refetchData') {
action();
}
});
};
/**
* Hook for creating a subscription on a given topic using the default websocket client.
*/
export const useSubscription = (
topic: SubscriptionTopic,
messageHandler: (message: any) => void
) => {
const client = useClient();
useEffect(() => {
const subscription =
client && createSubscription(client, topic, messageHandler);
return () => {
if (subscription) {
subscription.unsubscribe();
}
};
}, [client]);
};
/**
* Helper method creating a subscription on a given client and topic
*/
function createSubscription(
client: Client,
topic: string,
callback: (messageBody: any) => void
): Subscription {
const fullTopic = `/user/queue/oncommerce/sc/${topic}`;
return client.subscribe(fullTopic, (message: Message) => {
callback(JSON.parse(message.body));
});
}
export enum SubscriptionTopic {
Basket = 'basket',
CrossSellingProducts = 'crossSellingProducts',
EShopHeader = 'eShopHeader',
OrderHistoryDetails = 'orderHistoryDetails',
TrackRemoteEvent = 'trackRemoteEvent',
}
Editor is loading...
Leave a Comment