Handling custom OCC endpoints
It is a common task to add support for a custom (non-standard) SAP OCC API endpoint not covered by the Alokai intergration. This guide will show you how can do it using Alokai.
Prerequisites
Before we start make sure that you are familiar with Adding New API Methods guide. With that guide, you would be able to communicate with OCC API but it would require manual retrieval of context parameters (baseSiteId, userId, language, and currency) and preparation of authorization headers. Read on to see how to streamline that process.
Communicating with OCC API effectively
OCC endpoints have a given structure
{baseUrl}/{baseSiteId}/{resource_path}?fields={fields}&lang={language}&curr={currency}
or for user-specific data:
{baseUrl}/{baseSiteId}/user/{userId}/{resource_path}?fields={fields}&lang={language}&curr={currency}
These parameters are:
baseUrl
- the URL to the OCC APIbaseSiteId
- most endpoints require this due to multisite capabilities. eg. powertools-spauserId
- some endpoints return user-specific data. This parameter is either “current” for an authorized user, “anonymous” for an anonymous user, or a specific user id when we use ASM (link).resource_path
- what data we want to retrieve from the API. e.g. products/{productCode}/reviewsfields
- what fieldset should be returned. e.g BASIC, DEFAULT, FULL, or list of fieldslanguage
- in what language the data should be returnedcurrency
- in which currency the data should be returned
Additionally, for a logged-in user, the request should contain authorization headers.
Here's where to find the parameters:
baseUrl
- is configured in .env file asSAPCC_API_URI
. The api client already knows it and prepends each URL with it.baseSiteId
- is defined in the middleware configuration. That configuration is exposed to api method via context.userId
- can be found in the request cookies underAUTH_USER_COOKIE_NAME
.language
- can be found in the request cookies underVSF_LOCALE_COOKIE
.currency
- can be found in the request cookies underVSF_CURRENCY_COOKIE
.- authorization token - can be found in the request cookies under
AUTH_USER_TOKEN_COOKIE_NAME
You don't have to parse the cookies yourself. Alokai provides helper methods for that. Here’s a code example of how to do it:
import {
SapccIntegrationContext,
TokenModes,
createRequestOptions,
getUserIdFromRequest,
} from "@vsf-enterprise/sapcc-api";
import { BaseProps, BaseUserId } from "@vsf-enterprise/sapcc-types";
export interface CustomEndpointProps extends BaseProps, BaseUserId {
customField: any;
}
export interface CustomResponse {
whatever: any;
}
const callCustomEndpoint = async (
context: SapccIntegrationContext,
props: CustomEndpointProps
): Promise<CustomResponse> => {
const { config, req, client } = context;
const userId = getUserIdFromRequest({ req, props } as any); // retrieves userID from props or cookies
const res = await client.get(
`/${config.api.baseSiteId}/users/${userId}/customEndpoint/${props.customField}`,
createRequestOptions({
// adds authorization headers and language & currency parameters
context,
props,
tokenMode: TokenModes.CUSTOMERORAPPLICATION,
})
);
return res.data;
};
Read more about the helper methods:
Real life example
Here's an example implementation of the product interest feature. This feature is available in OCC API, but not in the middleware and SDK integration. Let's add support for it.
First, you need to add a new API method in the middleware. (For simplicity, this guide shows how to do it in one file, but we recommend splitting it into multiple files to maintain cleaner code.)
import {
SapccIntegrationContext,
TokenModes,
createRequestOptions,
getUserIdFromRequest,
} from "@vsf-enterprise/sapcc-api";
import { BaseProps, BaseUserId, Product } from "@vsf-enterprise/sapcc-types";
export interface GetProductInterestsProps extends BaseProps, BaseUserId {
productCode?: string;
}
export interface ProductInterestEntry {
interestType?: string;
dateAdded?: string;
expirationDate?: string;
}
export interface ProductInterestRelation {
product?: Product;
productInterestEntry?: Array<ProductInterestEntry>;
}
export interface UserInterestsResponse {
results: Array<ProductInterestRelation>;
}
const getProductInterests = async (
context: SapccIntegrationContext,
props: GetProductInterestsProps
): Promise<UserInterestsResponse> => {
const { config, req, client } = context;
const userId = getUserIdFromRequest({ req, props } as any);
const requestOptions = createRequestOptions({
context,
props,
tokenMode: TokenModes.CUSTOMERORAPPLICATION,
});
const res = await client.get(
`/${config.api.baseSiteId}/users/${userId}/productinterests`,
{
...requestOptions,
params: {
...requestOptions.params,
productCode: props.productCode,
},
}
);
return res.data;
};
const apiMethods = methods<typeof normalizers>();
const unifiedApiExtension = createUnifiedExtension<Context, Config>()({
normalizers,
apiMethods: {
...apiMethods,
getProductInterests,
},
config: {
/* ... */
},
});
Then, in your frontend application, you need to add a custom hook to retrieve the product interests on the front end.
import { useQuery } from "@tanstack/react-query";
import { InferSdkArgs, useSdk } from "~/sdk";
export type GetProductInterestsArgs = InferSdkArgs<"getProductInterests">;
export function useProductInterests({ productCode }: GetProductInterestsArgs) {
const sdk = useSdk();
return useQuery({
queryKey: ["product interests", productCode],
queryFn: () =>
sdk.unified.getProductInterests({
productCode,
}),
refetchOnMount: false,
refetchOnWindowFocus: false,
});
}