@contiva/sap-integration-suite-client
Version:
SAP Cloud Platform Integration API Client
523 lines (522 loc) • 20 kB
TypeScript
/**
* SAP Cloud Platform Integration API Client
*
* This module provides a client for interacting with the SAP Cloud Platform Integration API.
* It handles authentication, request management, and provides type-safe access to all SAP API endpoints.
*
* @module sap-integration-suite-client
* @packageDocumentation
*/
import { IntegrationContentClient } from '../wrapper/integration-content-client';
import { IntegrationContentAdvancedClient } from '../wrapper/custom/integration-content-advanced-client';
import { MessageProcessingLogsClient } from '../wrapper/message-processing-logs-client';
import { LogFilesClient } from '../wrapper/log-files-client';
import { MessageStoreClient } from '../wrapper/message-store-client';
import { SecurityContentClient } from '../wrapper/security-content-client';
import { B2BScenariosClient } from '../wrapper/b2b-scenarios-client';
import { PartnerDirectoryClient } from '../wrapper/partner-directory-client';
import { BaseCustomClient, CustomClientFactory } from '../wrapper/custom/base-custom-client';
import { CacheManager } from '../core/cache-manager';
/**
* Configuration interface for SapClient
*
* @interface SapClientConfig
* @example
* // Create a client with explicit configuration
* const client = new SapClient({
* baseUrl: 'https://my-tenant.sap-api.com/api/v1',
* oauthClientId: 'my-client-id',
* oauthClientSecret: 'my-client-secret',
* oauthTokenUrl: 'https://my-tenant.authentication.sap.hana.ondemand.com/oauth/token'
* });
*/
export interface SapClientConfig {
/**
* Base URL of the SAP API (e.g. https://tenant.sap-api.com/api/v1)
* If not provided, the SAP_BASE_URL environment variable will be used
*/
baseUrl?: string;
/**
* OAuth client ID for authentication
* If not provided, the SAP_OAUTH_CLIENT_ID environment variable will be used
*/
oauthClientId?: string;
/**
* OAuth client secret for authentication
* If not provided, the SAP_OAUTH_CLIENT_SECRET environment variable will be used
*/
oauthClientSecret?: string;
/**
* OAuth token URL for retrieving tokens
* If not provided, the SAP_OAUTH_TOKEN_URL environment variable will be used
*/
oauthTokenUrl?: string;
/**
* Enable response format normalization
* If true, the client will attempt to normalize different SAP API response formats
* Default: true
*/
normalizeResponses?: boolean;
/**
* Maximum number of retries for failed requests
* Default: 0 (no retries)
*/
maxRetries?: number;
/**
* Delay between retries in milliseconds
* Default: 1000 (1 second)
*/
retryDelay?: number;
/**
* Aktiviere oder deaktiviere benutzerdefinierte Client-Erweiterungen
* Default: true
*/
enableCustomClients?: boolean;
/**
* Redis connection string for caching
* Format: host:port,password=xxx,ssl=True,abortConnect=False
* If not provided, the REDIS_CONNECTION_STRING environment variable will be used
*/
redisConnectionString?: string;
/**
* Enable Redis caching
* Default: false (or REDIS_ENABLED environment variable)
*/
redisEnabled?: boolean;
/**
* Force cache revalidation on every request
* Uses stale-while-revalidate pattern (returns cached data while updating in background)
* Default: false
*/
forceRefreshCache?: boolean;
/**
* Disable caching completely for this client instance
* No cache reads or writes will be performed
* Default: false
*/
noCache?: boolean;
/**
* Custom logger function for cache events
* If provided, cache status logs will be sent to this function instead of console.log
*/
cacheLogger?: (message: string) => void;
/**
* External CacheManager instance (optional)
* If provided, this instance will be used instead of creating a new one
* Useful for sharing a single Redis connection across multiple SapClient instances
*/
cacheManager?: CacheManager;
}
/**
* Main SAP Client class that provides access to all SAP API endpoints
*
* This client handles:
* - Authentication via OAuth 2.0 client credentials flow
* - Token management (expiration and renewal)
* - CSRF token handling for write operations
* - Type-safe API access through generated API clients
* - Error handling
* - Response format normalization
*
* @example
* // Basic usage with environment variables
* import SapClient from 'sap-integration-suite-client';
* const client = new SapClient();
*
* // With explicit configuration
* const client = new SapClient({
* baseUrl: 'https://tenant.sap-api.com/api/v1',
* oauthClientId: 'client-id',
* oauthClientSecret: 'client-secret',
* oauthTokenUrl: 'https://tenant.authentication.sap.hana.ondemand.com/oauth/token'
* });
*
* // Making API calls
* async function getPackages() {
* const response = await client.integrationContent.integrationPackages.integrationPackagesList();
* return response.data;
* }
*/
declare class SapClient {
/** Axios instance used for HTTP requests */
private axiosInstance;
/** Base URL of the SAP API */
private baseUrl;
/** CSRF token for write operations */
private csrfToken;
/** Promise for ongoing CSRF token fetch to prevent race conditions */
private csrfTokenPromise;
/** OAuth token with expiration information */
private oauthToken;
/** Promise for ongoing token refresh to prevent race conditions */
private tokenRefreshPromise;
/** OAuth client ID */
private oauthClientId;
/** OAuth client secret */
private oauthClientSecret;
/** OAuth token URL */
private oauthTokenUrl;
/** Whether to normalize API responses */
private normalizeResponses;
/** Maximum number of retries for failed requests */
private maxRetries;
/** Delay between retries in milliseconds */
private retryDelay;
/** Minimum delay between consecutive requests (rate limiting prevention) */
private minRequestDelay;
/** Timestamp of last request */
private lastRequestTime;
/** Whether to enable custom client extensions */
private enableCustomClients;
/** Registry für benutzerdefinierte Client-Erweiterungen */
private customClientRegistry;
/** Map der erstellten benutzerdefinierten Clients */
private customClients;
/** Cache manager for Redis-based caching */
private _cacheManager;
/** Flag to track if cache manager is externally provided (don't disconnect on cleanup) */
private isExternalCacheManager;
/** Hostname for cache key generation */
private hostname;
/** Force cache revalidation on every request */
private forceRefreshCache;
/** Disable caching completely */
private noCache;
/** Custom logger for cache events */
private cacheLogger?;
/** Cached debug mode flag for performance */
private readonly debugMode;
/**
* Integration Content API client
* Provides access to integration packages, artifacts, and related operations
*/
integrationContent: IntegrationContentClient;
/**
* Integration Content Advanced API client
* Provides extended functionality for integration content operations
*/
integrationContentAdvanced: IntegrationContentAdvancedClient;
/**
* Log Files API client
* Access system logs and log archives
*/
logFiles: LogFilesClient;
/**
* Message Processing Logs API client
* Access and query message processing logs
*/
messageProcessingLogs: MessageProcessingLogsClient;
/**
* Message Store API client
* Access stored messages and attachments
*/
messageStore: MessageStoreClient;
/**
* Security Content API client
* Manage security artifacts like credentials and certificates
*/
securityContent: SecurityContentClient;
/**
* B2B Scenarios API client
* Access business documents, orphaned interchanges, and acknowledgements
*/
b2bScenarios: B2BScenariosClient;
/**
* Partner Directory API client
* Manage partner information, parameters, and authorized users
*/
partnerDirectory: PartnerDirectoryClient;
/**
* Gets the cache manager instance
* Returns null if caching is disabled or not configured
*
* @returns The CacheManager instance or null
*/
get cacheManager(): CacheManager | null;
/**
* Creates a new SAP Client instance
*
* @param {SapClientConfig} [config] - Configuration options for the client
* @throws {Error} If required configuration is missing
*
* @example
* // Using environment variables
* const client = new SapClient();
*
* @example
* // Using explicit configuration
* const client = new SapClient({
* baseUrl: 'https://tenant.sap-api.com/api/v1',
* oauthClientId: 'client-id',
* oauthClientSecret: 'client-secret',
* oauthTokenUrl: 'https://tenant.authentication.sap.hana.ondemand.com/oauth/token'
* });
*/
constructor(config?: SapClientConfig);
/**
* Validates that the required configuration is provided
*
* @private
* @throws {Error} If baseUrl is missing
* @throws {Error} If any OAuth configuration is incomplete
*/
private validateConfig;
/**
* Gets or refreshes the OAuth token
* - Returns existing token if it's still valid
* - Fetches a new token if the current one is expired or doesn't exist
* - Adds expiration timestamp to the token
*
* @private
* @returns {Promise<OAuthToken>} Promise resolving to the OAuth token
* @throws {Error} If token cannot be obtained
*/
private getOAuthToken;
/**
* Normalizes different SAP API response formats into a consistent structure
*
* @private
* @param {any} data - The response data to normalize
* @returns {any} Normalized response data
*/
private normalizeResponseFormat;
/**
* Enhances error objects with more detailed information
*
* @private
* @param {any} error - The error object
* @param {string} [message] - Optional message to prefix the error
* @returns {any} Enhanced error object
*/
private enhanceError;
/**
* Performs an HTTP request with retry logic for transient errors
*
* @private
* @param {Function} requestFn - Function that performs the request
* @param {number} retries - Number of retries left
* @returns {Promise<any>} Promise resolving to the response
*/
private requestWithRetry;
/**
* Checks if a URL represents a collection endpoint (without specific ID)
* Collection endpoints return arrays of items and are candidates for differential updates
*
* @private
* @param url - The URL to check
* @returns true if URL is a collection endpoint, false otherwise
*
* @example
* isCollectionEndpoint('https://api.com/IntegrationRuntimeArtifacts') // true
* isCollectionEndpoint('https://api.com/IntegrationRuntimeArtifacts(\'id\')') // false
* isCollectionEndpoint('https://api.com/IntegrationPackages') // true
*/
private isCollectionEndpoint;
/**
* Extracts the base endpoint pattern for cache invalidation
*
* This method determines which cache pattern should be invalidated based on the endpoint.
* For example:
* - /IntegrationPackages('MyPackage')/IntegrationDesigntimeArtifacts → /IntegrationPackages
* - /IntegrationRuntimeArtifacts('MyArtifact') → /IntegrationRuntimeArtifacts
* - /MessageMappingDesigntimeArtifacts → /MessageMappingDesigntimeArtifacts
*
* @private
* @param endpoint - The full endpoint path (without base URL)
* @returns The base endpoint pattern for invalidation
*/
private extractBaseEndpointForInvalidation;
/**
* Custom fetch implementation that uses axios to make HTTP requests
* - Handles CSRF token for write operations
* - Adds OAuth token to requests
* - Transforms axios responses to fetch API Response objects
* - Implements retry logic
* - Normalizes response formats
* - Implements Redis-based caching with stale-while-revalidate pattern
*
* @private
* @param {string | URL | Request} input - URL or Request object
* @param {RequestInit} [init] - Request initialization options
* @returns {Promise<Response>} Promise resolving to a Response object
* @throws {Error} If the request fails
*/
private customFetch;
/**
* Performs the actual HTTP request to SAP
*
* @private
* @param url - The URL to request
* @param method - HTTP method
* @param headers - Request headers
* @param body - Request body
* @returns The response data
*/
private performRequest;
/**
* Ensures a CSRF token is available for write operations
* - Fetches a new token if one doesn't exist
* - Uses OAuth token for authentication
* - Prevents race conditions with csrfTokenPromise
*
* @private
* @param {boolean} forceRefresh - Force fetching a new token even if one exists
* @returns {Promise<void>}
* @throws {Error} If the CSRF token cannot be fetched
*/
private ensureCsrfToken;
/**
* Initialisiert alle benutzerdefinierten Client-Erweiterungen
*/
private initializeCustomClients;
/**
* Gibt einen vorhandenen benutzerdefinierten Client zurück oder erstellt einen neuen
*
* @param type - Der Typ des benutzerdefinierten Clients
* @param baseClient - Der zugrundeliegende Standard-Client
* @returns Eine Instanz des benutzerdefinierten Clients
*/
private getOrCreateCustomClient;
/**
* Gibt einen benutzerdefinierten Client nach Typ zurück
*
* @param type - Der Typ des benutzerdefinierten Clients
* @returns Der benutzerdefinierte Client oder undefined, wenn nicht gefunden
*/
getCustomClient<C extends BaseCustomClient<any>>(type: string): C | undefined;
/**
* Registriert einen benutzerdefinierten Client-Factory
*
* Diese Methode erlaubt es, zur Laufzeit neue benutzerdefinierte Client-Factories
* zu registrieren, was die dynamische Erweiterung des Clients ermöglicht.
*
* @param type - Der Typ des benutzerdefinierten Clients
* @param factory - Die Factory für die Erstellung des Clients
*/
registerCustomClientFactory<T, C extends BaseCustomClient<T>>(type: string, factory: CustomClientFactory<T, C>): void;
/**
* Invalidates cache entries matching a pattern
*
* This is a convenience wrapper around CacheManager.deleteByPattern that
* automatically generates the hostname prefix for the cache key pattern.
*
* @param pattern - The pattern to match (without hostname prefix, e.g. 'GET:/IntegrationRuntimeArtifacts*')
* @returns The number of cache keys deleted
*
* @example
* // Invalidate all cache entries for a specific artifact
* const deleted = await client.invalidateCache(`GET:/IntegrationRuntimeArtifacts('artifactId')*`);
*
* // Invalidate all runtime artifacts
* const deleted = await client.invalidateCache('GET:/IntegrationRuntimeArtifacts*');
*/
invalidateCache(pattern: string): Promise<number>;
/**
* Invalidates all cache entries for a specific artifact
*
* This is a convenience method that invalidates both the runtime artifact cache
* and optionally the package cache containing the artifact.
*
* **Important**: This method also invalidates the corresponding collection caches
* (e.g., `GET:/IntegrationRuntimeArtifacts*` and `GET:/IntegrationPackages*`) to ensure
* consistency between single-item and collection caches.
*
* @param artifactId - The ID of the artifact to invalidate
* @param packageId - Optional package ID to also invalidate the package cache
* @returns The total number of cache keys deleted
*
* @example
* // Invalidate cache for an artifact (includes collection cache)
* const deleted = await client.invalidateArtifactCache('MyArtifact');
*
* // Invalidate cache for an artifact and its package (includes all collection caches)
* const deleted = await client.invalidateArtifactCache('MyArtifact', 'MyPackage');
*/
invalidateArtifactCache(artifactId: string, packageId?: string): Promise<number>;
/**
* Invalidates ALL designtime-related cache entries for this client's hostname
*
* This method clears all caches related to designtime artifacts, which includes:
* - /IntegrationPackages (collection and individual packages)
* - /IntegrationDesigntimeArtifacts (all designtime artifacts)
* - /MessageMappingDesigntimeArtifacts (all message mappings)
* - /ValueMappingDesigntimeArtifacts (all value mappings)
* - /ScriptCollectionDesigntimeArtifacts (all script collections)
*
* Use this method before fetching fresh designtime data to ensure
* deleted artifacts are properly removed from the cache.
*
* @returns The total number of cache keys deleted
*
* @example
* // Clear all designtime caches before refresh
* const deleted = await client.invalidateDesigntimeCache();
* console.log(`Cleared ${deleted} cache entries`);
*
* // Then fetch fresh data
* const packages = await client.integrationContent.getIntegrationPackages();
*/
invalidateDesigntimeCache(): Promise<number>;
/**
* Invalidates ALL runtime-related cache entries for this client's hostname
*
* This method clears all caches related to runtime/deployed artifacts:
* - /IntegrationRuntimeArtifacts (collection and individual deployed artifacts)
*
* Use this method before fetching fresh runtime data to ensure
* undeployed artifacts are properly removed from the cache.
*
* @returns The total number of cache keys deleted
*
* @example
* // Clear all runtime caches before refresh
* const deleted = await client.invalidateRuntimeCache();
* console.log(`Cleared ${deleted} cache entries`);
*
* // Then fetch fresh data
* const deployedArtifacts = await client.integrationContent.getDeployedArtifacts();
*/
invalidateRuntimeCache(): Promise<number>;
/**
* Invalidates ALL cache entries for this client's hostname
*
* This method clears the entire cache for the current SAP system,
* including both designtime and runtime data.
*
* **Warning**: This is a destructive operation that will clear ALL cached data
* for this hostname. Use with caution as it will cause increased load on SAP
* while the cache is repopulated.
*
* @returns The total number of cache keys deleted
*
* @example
* // Clear ALL caches for this system
* const deleted = await client.invalidateAllCache();
*/
invalidateAllCache(): Promise<number>;
/**
* Gets the hostname used for cache key generation
*
* @returns The hostname extracted from the base URL
*/
getHostname(): string;
/**
* Closes all connections and cleans up resources
*
* This method should be called when the SapClient is no longer needed
* to prevent memory leaks and ensure proper cleanup of resources like
* Redis connections.
*
* @returns Promise that resolves when cleanup is complete
*
* @example
* const client = new SapClient();
* // ... use client ...
* await client.disconnect();
*/
disconnect(): Promise<void>;
}
/**
* Export the SapClient class as default export
*/
export default SapClient;