@apptus/esales-api
Version:
Library for making requests to Elevate 4 API v3
155 lines (133 loc) • 5.18 kB
text/typescript
import { assert, assertString } from './util/mod.ts';
/** Used to determine server settings to use. */
type Touchpoint = 'desktop' | 'mobile';
/** Session is used to handle session information for a specific visitor. */
interface Session {
/**
* Method for retrieving session information, synchronously
* or asynchronously. Returns a `SessionMetadata` object that
* contains `customerKey` and `sessionKey`.
*
* @example
* ```ts
* declare const session: Session;
*
* const { customerKey, sessionKey } = await session();
* ```
*/
(): SessionMetadata | Promise<SessionMetadata>;
}
/** Metadata information regarding a visitor, used during requests to the Elevate Storefront API. */
interface SessionMetadata {
/**
* A key that uniquely identifies the current visitor. Using UUIDs as keys are recommended.
* @see https://docs.elevate.voyado.cloud/elevate/4/integration/api/common/query-parameters/#mandatory-parameters
*/
customerKey: string;
/**
* A unique key, identifying the session. Using UUIDs as keys are recommended.
* @see https://docs.elevate.voyado.cloud/elevate/4/integration/api/common/query-parameters/#mandatory-parameters
*/
sessionKey: string;
}
/**
* Configuration required during initialization for the Elevate Storefront API.
*
* @example
* ```ts
* const config: Config = { ... };
* const api = elevate(config);
* ```
*/
interface Config {
/**
* The ID for the Elevate cluster for all subsequent API requests.
* Production/Test/Development clusters each have their own unique ID.
* The value for `clusterId` can be found in credentials tab in
* the Voyado Elevate app,
*
* The value should be:
* - An identifier in a format similar to `'w00000000'`
* - A URL with the origin to the Elevate cluster, e.g.
* `'https://w00000000.api.esales.apptus.cloud/'`
*
* @example
* ```ts
* const api = elevate({ clusterId: 'w00000000' });
* ```
*/
clusterId: string;
/** Required for loading correct products and behavior data. */
market: string;
/** Required for loading localized product and facet data. */
locale: string;
/** Used to determine various server settings. */
touchpoint: Touchpoint;
/** Configures how session metadata (customerKey & sessionKey) is retrieved before requests to the API */
session: Session;
}
/**
* Subset of Config for interacting with the Notifications API.
* @see Config
*/
type NotificationConfig = Pick<Config, 'clusterId' | 'market' | 'session'>;
/**
* Created from a validated `Config`. Used internally.
* @internal
*/
interface BaseOptions extends Omit<NotificationConfig, 'clusterId'> {
clusterUrl: string;
}
/**
* Created from a validated `Config`. Used internally.
* @internal
*/
interface FullOptions extends Omit<Config, 'clusterId'> {
clusterUrl: string;
}
function validateBaseConfig(config: unknown): asserts config is NotificationConfig {
assert(typeof config === 'object' && config !== null, 'API config must be an "object"');
const c: Partial<NotificationConfig> = config;
assertString(c.clusterId, 'Property "clusterId" must be a non-empty string');
assertString(c.market, 'Property "market" must be a non-empty string');
assert(typeof c.session === 'function', 'Property "session" must be a method.');
}
/**
* Validates that user supplied config is in a correct format. `unknown` is used as parameter
* value to indicate that we do not yet trust the user input.
*
* @param config raw configuration for creating the API library to be validated
*/
export function validateConfig(config: unknown): asserts config is Config {
validateBaseConfig(config);
const c: Partial<Config> = config;
const touchpoints: Touchpoint[] = ['desktop', 'mobile'];
assertString(c.locale, 'Property "locale" must be a string and valid locale, e.g. "en-US".');
assert(touchpoints.includes(c.touchpoint!), `Property "touchpoint" must be one of: ${touchpoints.join(', ')}`);
}
function resolveClusterUrl<T extends NotificationConfig>(config: T) {
const { clusterId, ...rest } = config;
const clusterUrl = (() => {
/** Ensures consistent casing and trailing slash of URL's */
const normalizeUrl = (url: string) => new URL(url).href;
try {
// Will throw for invalid URL, and normalize trailing slash
return normalizeUrl(clusterId);
} catch { /* empty */ }
return normalizeUrl(`https://${clusterId}.api.esales.apptus.cloud/`);
})();
return { ...rest, clusterUrl };
}
/** Prepares user-supplied configuration for internal use. */
export function resolveConfig(config: Config): FullOptions {
// TODO(csv): Allow this code to be removed in a production build?
validateConfig(config);
return resolveClusterUrl(config);
}
/** Prepares user-supplied configuration for internal use. */
export function resolveNotificationConfig(config: NotificationConfig): BaseOptions {
// TODO(csv): Allow this code to be removed in a production build?
validateBaseConfig(config);
return resolveClusterUrl(config);
}
export type { Config, NotificationConfig, BaseOptions, FullOptions, Session, SessionMetadata, Touchpoint };