UNPKG

@contiva/sap-integration-suite-client

Version:
1,020 lines 136 kB
"use strict"; /** * SAP Integration Content Client * * Diese Datei enthält eine vereinfachte Client-Klasse für die Interaktion mit * den Integration Content APIs von SAP Cloud Integration. * * @module sap-integration-suite-client/integration-content */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.IntegrationContentClient = void 0; const response_normalizer_1 = require("../utils/response-normalizer"); const integration_content_advanced_client_1 = require("./custom/integration-content-advanced-client"); const cache_update_helper_1 = require("../utils/cache-update-helper"); const hostname_extractor_1 = require("../utils/hostname-extractor"); const cache_collections_config_1 = require("../utils/cache-collections-config"); const cache_logger_1 = require("../utils/cache-logger"); const cache_key_generator_1 = require("../utils/cache-key-generator"); const cache_config_1 = require("../core/cache-config"); /** * Erweiterter SAP Integration Content Client * * Diese Klasse stellt eine vereinfachte API für die Interaktion * mit den SAP Integration Content APIs bereit. */ class IntegrationContentClient { /** * Erstellt einen neuen IntegrationContentClient * * @param {IntegrationContentApi<unknown>} api - Die zugrunde liegende API-Instanz * @param {CacheManager} [cacheManager] - Optional: CacheManager für Cache-Updates * @param {string} [hostname] - Optional: Hostname für Cache-Key-Generierung */ constructor(api, cacheManager, hostname) { this.cacheManager = null; this.hostname = ''; this.api = api; this.normalizer = new response_normalizer_1.ResponseNormalizer(); this.advancedClient = new integration_content_advanced_client_1.IntegrationContentAdvancedClient(this); this.cacheManager = cacheManager || null; this.hostname = hostname || ''; } /** * Gibt alle Integrationspakete zurück * * @param {Object} options Optionale Parameter für die Anfrage * @param {number} [options.top] Maximale Anzahl der zurückzugebenden Pakete * @param {number} [options.skip] Anzahl der zu überspringenden Pakete * @param {string} [options.author] Filtern nach Autor (Custom Tag) * @param {string} [options.lob] Filtern nach Line of Business (Custom Tag) * @returns {Promise<ComSapHciApiIntegrationPackage[]>} Promise mit einer Liste von Integrationspaketen * * @example * // Alle Integrationspakete abrufen * const packages = await client.getIntegrationPackages(); * * @example * // Pakete mit Paginierung abrufen * const packages = await client.getIntegrationPackages({ top: 10, skip: 20 }); */ async getIntegrationPackages(options = {}) { const response = await this.api.integrationPackages.integrationPackagesList({ $top: options.top, $skip: options.skip, Author: options.author, LoB: options.lob }); return this.normalizer.normalizeArrayResponse(response.data, 'getIntegrationPackages', 'IntegrationPackages'); } /** * Gibt alle Integrationspakete zurück (mit Caching) * * Diese Methode implementiert das Stale-While-Revalidate Pattern: * - Frische Daten aus Cache werden sofort zurückgegeben * - Veraltete Daten werden sofort zurückgegeben und im Hintergrund aktualisiert * - Bei Cache-Miss werden frische Daten von SAP geholt und gecacht * * @param {Object} options Optionale Parameter für die Anfrage * @param {number} [options.top] Maximale Anzahl der zurückzugebenden Pakete * @param {number} [options.skip] Anzahl der zu überspringenden Pakete * @param {string} [options.author] Filtern nach Autor (Custom Tag) * @param {string} [options.lob] Filtern nach Line of Business (Custom Tag) * @param {boolean} [forceRefresh=false] Erzwingt das Laden frischer Daten von SAP * @returns {Promise<{data: ComSapHciApiIntegrationPackage[], cacheInfo: CacheInfo}>} Pakete mit Cache-Informationen * * @example * const { data, cacheInfo } = await client.getIntegrationPackagesWithCache(); * console.log(cacheInfo.hit); // true/false * console.log(cacheInfo.status); // 'HIT', 'HIT-STALE', 'MISS', or 'DISABLED' */ async getIntegrationPackagesWithCache(options = {}, forceRefresh = false) { const endpoint = '/IntegrationPackages'; const queryParams = { top: options.top, skip: options.skip, author: options.author, lob: options.lob }; // If no cacheManager or hostname, fall back to direct API call if (!this.cacheManager || !this.hostname) { const data = await this.getIntegrationPackages(options); return { data, cacheInfo: { hit: false, age: null, status: 'DISABLED', source: 'sap-api-direct', }, }; } const cacheKey = (0, cache_key_generator_1.generateCacheKey)(this.hostname, 'GET', endpoint, queryParams); const cacheOptions = { ttl: cache_config_1.CACHE_TTL.STANDARD, revalidateAfter: cache_config_1.REVALIDATE_AFTER.STANDARD }; const fetchFn = async () => { return this.getIntegrationPackages(options); }; const result = await this.cacheManager.getOrFetch(cacheKey, fetchFn, cacheOptions, forceRefresh); return { data: result.data, cacheInfo: { ...result.cacheInfo, key: cacheKey, source: 'npm-package-cache', }, }; } /** * Gibt ein Integrationspaket anhand seiner ID zurück * * @param {string} packageId ID des Integrationspakets * @returns {Promise<ComSapHciApiIntegrationPackage>} Promise mit dem Integrationspaket * * @example * const package = await client.getIntegrationPackageById('MyPackageId'); */ async getIntegrationPackageById(packageId) { const response = await this.api.integrationPackagesId.integrationPackagesList(packageId); return response.data; } /** * Erstellt ein neues Integrationspaket * * @param {ComSapHciApiIntegrationPackageCreate} packageData Daten des Integrationspakets * @param {boolean} [overwrite=false] Ob ein existierendes Paket mit der gleichen ID überschrieben werden soll * @returns {Promise<ComSapHciApiIntegrationPackage>} Promise mit dem erstellten Integrationspaket * * @example * const newPackage = await client.createIntegrationPackage({ * Id: 'MyNewPackage', * Name: 'My New Integration Package', * Description: 'This is a new package' * }); */ async createIntegrationPackage(packageData, overwrite = false) { const response = await this.api.integrationPackages.integrationPackagesCreate(packageData, overwrite ? { Overwrite: ["true"] } : undefined); return response.data; } /** * Löscht ein Integrationspaket anhand seiner ID * * @param {string} packageId ID des zu löschenden Integrationspakets * @returns {Promise<void>} Promise, der aufgelöst wird, wenn das Paket gelöscht wurde * * @example * await client.deleteIntegrationPackage('MyPackageId'); */ async deleteIntegrationPackage(packageId) { await this.api.integrationPackagesId.integrationPackagesDelete(packageId); } /** * Gibt alle Integrationsflows für ein bestimmtes Paket zurück * * @param {string} packageId ID des Integrationspakets * @returns {Promise<ComSapHciApiIntegrationDesigntimeArtifact[]>} Promise mit einer Liste von Integrationsflows * * @example * const flows = await client.getIntegrationFlows('MyPackageId'); */ async getIntegrationFlows(packageId) { try { const response = await this.api.integrationPackagesId.integrationDesigntimeArtifactsList(packageId); return this.normalizer.normalizeArrayResponse(response.data, 'getIntegrationFlows', 'IntegrationDesigntimeArtifacts'); } catch (error) { // Check for 500 Internal Server Error - skip these packages gracefully if ((error === null || error === void 0 ? void 0 : error.statusCode) === 500 && (error === null || error === void 0 ? void 0 : error.errorCode) === 'Internal Server Error') { console.warn(`Failed to fetch Integration Flows for package ${packageId}. Skipping and returning empty array.`); return []; } // For other errors, rethrow throw error; } } /** * Gibt einen bestimmten Integrationsflow anhand seiner ID und Version zurück * * @param {string} flowId ID des Integrationsflows * @param {string} version Version des Integrationsflows (Standard: 'active') * @returns {Promise<ComSapHciApiIntegrationDesigntimeArtifact>} Promise mit dem Integrationsflow * * @example * const flow = await client.getIntegrationFlowById('MyFlowId', '1.0.0'); */ async getIntegrationFlowById(flowId, version = 'active') { const response = await this.api.integrationDesigntimeArtifactsIdIdVersionVersion.integrationDesigntimeArtifactsIdVersionList(flowId, version); return this.normalizer.normalizeEntityResponse(response.data, 'getIntegrationFlowById'); } /** * Erstellt oder lädt einen neuen Integrationsflow hoch. * * @param {ComSapHciApiIntegrationDesigntimeArtifactCreate} flowData Daten des zu erstellenden Flows * @returns {Promise<ComSapHciApiIntegrationDesigntimeArtifact>} Promise mit dem erstellten Integrationsflow * * @example * const newFlow = await client.createIntegrationFlow({ * Id: 'MyNewFlow', * Name: 'My New Flow', * PackageId: 'MyPackageId', * ArtifactContent: 'base64encodedZipContent' // Base64 kodierter ZIP-Inhalt * }); */ async createIntegrationFlow(flowData) { const response = await this.api.integrationDesigntimeArtifacts.integrationDesigntimeArtifactsCreate(flowData); return this.normalizer.normalizeEntityResponse(response.data, 'createIntegrationFlow'); } /** * Aktualisiert einen vorhandenen Integrationsflow. * * @param {string} flowId ID des zu aktualisierenden Flows * @param {string} version Version des zu aktualisierenden Flows * @param {ComSapHciApiIntegrationDesigntimeArtifactUpdate} flowData Die zu aktualisierenden Daten * @returns {Promise<void>} Promise, der aufgelöst wird, wenn der Flow aktualisiert wurde * * @example * await client.updateIntegrationFlow('MyFlowId', '1.0.0', { * Name: 'Updated Flow Name', * ArtifactContent: 'newBase64encodedZipContent' // Neuer Base64 kodierter ZIP-Inhalt * }); */ async updateIntegrationFlow(flowId, version, flowData) { await this.api.integrationDesigntimeArtifactsIdIdVersionVersion.integrationDesigntimeArtifactsIdVersionUpdate(flowId, version, flowData); } /** * Löscht einen Integrationsflow anhand seiner ID und Version. * * @param {string} flowId ID des zu löschenden Integrationsflows * @param {string} version Version des zu löschenden Integrationsflows * @returns {Promise<void>} Promise, der aufgelöst wird, wenn der Flow gelöscht wurde * * @example * await client.deleteIntegrationFlow('MyFlowId', '1.0.0'); */ async deleteIntegrationFlow(flowId, version) { await this.api.integrationDesigntimeArtifactsIdIdVersionVersion.integrationDesigntimeArtifactsIdVersionDelete(flowId, version); } /** * Lädt einen Integrationsflow als ZIP-Datei herunter. * * Hinweis: Download von Flows aus Configure-Only-Paketen ist nicht möglich. * Die Methode gibt in Node.js-Umgebungen einen Buffer zurück und * in Browser-Umgebungen einen Blob/File. * * @param {string} flowId ID des herunterzuladenden Flows * @param {string} [version='active'] Version des herunterzuladenden Flows * @returns {Promise<Buffer | Blob>} Promise mit den Binärdaten des Flows * * @example * // In Node.js: * try { * const buffer = await client.downloadIntegrationFlow('MyFlowId', '1.0.1'); * fs.writeFileSync('MyFlowId.zip', buffer); * } catch (error) { * console.error('Download failed:', error); * } */ async downloadIntegrationFlow(flowId, version = 'active') { try { // Für diesen API-Call verwenden wir direkt axios, da wir einen binären Response benötigen // und der generierte HTTP-Client nicht optimal mit arraybuffer umgeht const axios = (await Promise.resolve().then(() => __importStar(require('axios')))).default; // Erhalte das Security-Token (wird von der API-Client-Instanz verwaltet) const securityWorker = this.api['securityWorker']; const securityData = this.api['securityData']; // Debug-Logging für die Sicherheitsinformationen if (process.env.DEBUG === 'true') { console.debug('[IntegrationContentClient] Preparing download for flow:', flowId, version); console.debug('[IntegrationContentClient] Security worker available:', !!securityWorker); console.debug('[IntegrationContentClient] Security data available:', !!securityData); } // Sicherheitsparameter abrufen und sicherstellen, dass wir ein Objekt haben let securityParams = {}; if (securityWorker) { try { const params = await securityWorker(securityData); if (params && typeof params === 'object') { securityParams = params; } } catch (securityError) { console.error('[IntegrationContentClient] Error getting security params:', securityError); } } if (process.env.DEBUG === 'true') { console.debug('[IntegrationContentClient] Security params:', securityParams && typeof securityParams === 'object' ? Object.keys(securityParams).length : 'none'); } // URL aus dem API-Client extrahieren const baseUrl = this.api['baseUrl']; const url = `${baseUrl}/IntegrationDesigntimeArtifacts(Id='${flowId}',Version='${version}')/$value`; // Wenn keine Sicherheitsparameter vorhanden sind oder die Headers fehlen, // verwenden wir einen Fallback const hasSecurityHeaders = securityParams && typeof securityParams === 'object' && 'headers' in securityParams && securityParams.headers && typeof securityParams.headers === 'object' && 'Authorization' in securityParams.headers; if (!hasSecurityHeaders) { if (process.env.SAP_OAUTH_CLIENT_ID && process.env.SAP_OAUTH_CLIENT_SECRET && process.env.SAP_OAUTH_TOKEN_URL) { // Direktes Token-Holen als Fallback if (process.env.DEBUG === 'true') { console.debug('[IntegrationContentClient] Using direct token acquisition as fallback'); } // Token direkt holen (ähnlich wie in getArtifacts.js) const tokenResponse = await axios({ method: 'post', url: process.env.SAP_OAUTH_TOKEN_URL, params: { grant_type: 'client_credentials', client_id: process.env.SAP_OAUTH_CLIENT_ID, client_secret: process.env.SAP_OAUTH_CLIENT_SECRET }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); if (process.env.DEBUG === 'true') { console.debug('[IntegrationContentClient] Token acquired successfully, making download request'); } // API-Aufruf mit dem direkt geholten Token durchführen const response = await axios({ method: 'get', url, headers: { 'Authorization': `Bearer ${tokenResponse.data.access_token}`, 'Accept': 'application/zip' }, responseType: 'arraybuffer' }); // Prüfen, ob wir Daten erhalten haben if (response && response.data) { if (process.env.DEBUG === 'true') { console.debug('[IntegrationContentClient] Download successful, data length:', response.data.length || (response.data.byteLength || 'unknown')); } // In Node.js-Umgebung if (typeof Buffer !== 'undefined') { return Buffer.from(response.data); } // In Browser-Umgebung if (typeof Blob !== 'undefined' && typeof window !== 'undefined') { return new Blob([response.data], { type: 'application/zip' }); } } throw new Error(`Unexpected response format or environment when downloading flow ${flowId}`); } else { if (process.env.DEBUG === 'true') { console.debug('[IntegrationContentClient] No security parameters and no environment variables for fallback'); } } } // Nur hier ankommen, wenn wir gültige securityParams haben oder keine Fallback-Variablen vorhanden sind if (process.env.DEBUG === 'true') { console.debug('[IntegrationContentClient] Using security parameters from API client'); } // API-Aufruf mit axios durchführen const response = await axios({ method: 'get', url, headers: { ...((securityParams && typeof securityParams === 'object' && 'headers' in securityParams) ? securityParams.headers : {}), 'Accept': 'application/zip' }, responseType: 'arraybuffer' }); // Prüfen, ob wir Daten erhalten haben if (response && response.data) { if (process.env.DEBUG === 'true') { console.debug('[IntegrationContentClient] Download successful, data length:', response.data.length || (response.data.byteLength || 'unknown')); } // In Node.js-Umgebung if (typeof Buffer !== 'undefined') { return Buffer.from(response.data); } // In Browser-Umgebung if (typeof Blob !== 'undefined' && typeof window !== 'undefined') { return new Blob([response.data], { type: 'application/zip' }); } } throw new Error(`Unexpected response format or environment when downloading flow ${flowId}`); } catch (error) { // Verbesserte Fehlerbehandlung mit mehr Informationen console.error(`[IntegrationContentClient] Error downloading flow ${flowId}:`, error.message || 'Unknown error'); if (error.response) { console.error(`Status: ${error.response.status}, Status text: ${error.response.statusText}`); if (error.response.data) { // Wenn die Fehlerdaten ein Buffer sind, versuchen wir, sie als Text zu decodieren const errorData = Buffer.isBuffer(error.response.data) ? Buffer.from(error.response.data).toString('utf8') : error.response.data; console.error('Error details:', errorData); } } throw error; } } /** * Deployed einen Integrationsflow * * SAP erwartet Single Quotes in der URL, die NICHT URL-encoded sein dürfen. * Deshalb wird hier ein direkter HTTP-Request gemacht, der die URL manuell baut. * * @param {string} flowId ID des zu deployenden Integrationsflows * @param {string} [version='active'] Version des Integrationsflows * @returns {Promise<void>} Promise, der aufgelöst wird, wenn das Deployment gestartet wurde * * @example * await client.deployIntegrationFlow('MyFlowId'); */ async deployIntegrationFlow(flowId, version = 'active') { // SAP erwartet Single Quotes in der URL, die NICHT URL-encoded sein dürfen. // Die standard API-Methode würde die Quotes als %27 encodieren, deshalb verwenden wir axios direkt. // Wir nutzen aber customFetch aus dem SapClient für OAuth und CSRF Token Handling. const axios = (await Promise.resolve().then(() => __importStar(require('axios')))).default; const baseUrl = this.api['baseUrl']; // Validierung dass baseUrl vollständig ist if (!baseUrl || (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://'))) { throw new Error(`Invalid baseUrl for deploy operation: ${baseUrl}`); } // URL mit Single Quotes konstruieren (nicht URL-encoded) const url = `${baseUrl}/DeployIntegrationDesigntimeArtifact?Id='${flowId}'&Version='${version}'`; // Security-Daten für OAuth holen const securityWorker = this.api['securityWorker']; const securityData = this.api['securityData']; const authHeaders = {}; if (securityWorker) { try { const params = await securityWorker(securityData); if (params && typeof params === 'object' && 'headers' in params) { const headers = params.headers; if (headers && typeof headers === 'object' && !Array.isArray(headers)) { for (const [key, value] of Object.entries(headers)) { if (typeof value === 'string') { authHeaders[key] = value; } } } } } catch (securityError) { console.error('[IntegrationContentClient] Error getting security params:', securityError); } } if (!authHeaders['Authorization']) { throw new Error('No authorization headers available for deploy request'); } // CSRF-Token URL konstruieren // SAP erwartet CSRF-Token von GET /api/v1/ (root der API, nicht root des Servers) const csrfUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`; // CSRF-Token holen const csrfResponse = await axios.get(csrfUrl, { headers: { 'X-CSRF-Token': 'Fetch', ...authHeaders } }); const csrfToken = csrfResponse.headers['x-csrf-token']; if (!csrfToken) { throw new Error('CSRF token not found in response headers'); } // Deploy-Request ausführen const response = await axios.post(url, null, { headers: { 'X-CSRF-Token': csrfToken, 'Content-Type': 'application/json', ...authHeaders } }); // 202 Accepted ist die erwartete Antwort für erfolgreiches Deployment if (response.status !== 202 && response.status !== 200) { throw new Error(`Unexpected response status: ${response.status}`); } } /** * Undeployed einen Integrationsflow * * SAP erwartet Single Quotes im URL-Pfad, die NICHT URL-encoded sein dürfen. * Die standard API-Methode würde die Quotes als %27 encodieren, deshalb verwenden wir axios direkt. * * @param {string} flowId ID des zu undeployenden Integrationsflows * @returns {Promise<void>} Promise, der aufgelöst wird, wenn der Flow undeployed wurde * * @example * await client.undeployIntegrationFlow('MyFlowId'); */ async undeployIntegrationFlow(flowId) { const axios = (await Promise.resolve().then(() => __importStar(require('axios')))).default; const baseUrl = this.api['baseUrl']; // Validierung dass baseUrl vollständig ist if (!baseUrl || (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://'))) { throw new Error(`Invalid baseUrl for undeploy operation: ${baseUrl}`); } // URL mit Single Quotes im Pfad konstruieren (nicht URL-encoded) const url = `${baseUrl}/IntegrationRuntimeArtifacts('${flowId}')`; // Security-Daten für OAuth holen const securityWorker = this.api['securityWorker']; const securityData = this.api['securityData']; const authHeaders = {}; if (securityWorker) { try { const params = await securityWorker(securityData); if (params && typeof params === 'object' && 'headers' in params) { const headers = params.headers; if (headers && typeof headers === 'object' && !Array.isArray(headers)) { for (const [key, value] of Object.entries(headers)) { if (typeof value === 'string') { authHeaders[key] = value; } } } } } catch (securityError) { console.error('[IntegrationContentClient] Error getting security params:', securityError); } } if (!authHeaders['Authorization']) { throw new Error('No authorization headers available for undeploy request'); } // CSRF-Token URL konstruieren const csrfUrl = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`; // CSRF-Token holen const csrfResponse = await axios.get(csrfUrl, { headers: { 'X-CSRF-Token': 'Fetch', ...authHeaders } }); const csrfToken = csrfResponse.headers['x-csrf-token']; if (!csrfToken) { throw new Error('CSRF token not found in response headers'); } // Undeploy-Request ausführen const response = await axios.delete(url, { headers: { 'X-CSRF-Token': csrfToken, 'Content-Type': 'application/json', ...authHeaders } }); // 202 Accepted ist die erwartete Antwort für erfolgreiches Undeploy if (response.status !== 202 && response.status !== 200) { throw new Error(`Unexpected response status: ${response.status}`); } } /** * Gibt alle deployten Integrationsartefakte zurück * * @param {Object} options Optionale Parameter für die Anfrage * @param {number} [options.top] Maximale Anzahl der zurückzugebenden Artefakte * @param {number} [options.skip] Anzahl der zu überspringenden Artefakte * @param {string} [options.filter] OData-Filterausdruck * @returns {Promise<ComSapHciApiIntegrationRuntimeArtifact[]>} Promise mit einer Liste von Runtime-Artefakten * * @example * // Alle deployten Artefakte abrufen * const runtimeArtifacts = await client.getDeployedArtifacts(); * * @example * // Deployten Artefakte mit Filter abrufen * const errorArtifacts = await client.getDeployedArtifacts({ * filter: "Status eq 'ERROR'" * }); */ async getDeployedArtifacts(options = {}) { const response = await this.api.integrationRuntimeArtifacts.integrationRuntimeArtifactsList({ $top: options.top, $skip: options.skip, $filter: options.filter }); return this.normalizer.normalizeArrayResponse(response.data, 'getDeployedArtifacts'); } /** * Gibt deployed Artefakte zurück (mit Caching) * * Diese Methode implementiert das Stale-While-Revalidate Pattern: * - Frische Daten aus Cache werden sofort zurückgegeben * - Veraltete Daten werden sofort zurückgegeben und im Hintergrund aktualisiert * - Bei Cache-Miss werden frische Daten von SAP geholt und gecacht * * @param {Object} options Optionale Parameter für die Anfrage * @param {number} [options.top] Maximale Anzahl der zurückzugebenden Artefakte * @param {number} [options.skip] Anzahl der zu überspringenden Artefakte * @param {string} [options.filter] OData-Filterausdruck * @param {boolean} [forceRefresh=false] Erzwingt das Laden frischer Daten von SAP * @returns {Promise<{data: ComSapHciApiIntegrationRuntimeArtifact[], cacheInfo: CacheInfo}>} Artefakte mit Cache-Informationen * * @example * const { data, cacheInfo } = await client.getDeployedArtifactsWithCache(); * console.log(cacheInfo.hit); // true/false * console.log(cacheInfo.status); // 'HIT', 'HIT-STALE', 'MISS', or 'DISABLED' */ async getDeployedArtifactsWithCache(options = {}, forceRefresh = false) { const endpoint = '/IntegrationRuntimeArtifacts'; const queryParams = { top: options.top, skip: options.skip, filter: options.filter }; // If no cacheManager or hostname, fall back to direct API call if (!this.cacheManager || !this.hostname) { const data = await this.getDeployedArtifacts(options); return { data, cacheInfo: { hit: false, age: null, status: 'DISABLED', source: 'sap-api-direct', }, }; } const cacheKey = (0, cache_key_generator_1.generateCacheKey)(this.hostname, 'GET', endpoint, queryParams); // Runtime artifacts use shorter revalidation time (5 minutes) const cacheOptions = { ttl: cache_config_1.CACHE_TTL.STANDARD, revalidateAfter: cache_config_1.REVALIDATE_AFTER.RUNTIME }; const fetchFn = async () => { return this.getDeployedArtifacts(options); }; const result = await this.cacheManager.getOrFetch(cacheKey, fetchFn, cacheOptions, forceRefresh); return { data: result.data, cacheInfo: { ...result.cacheInfo, key: cacheKey, source: 'npm-package-cache', }, }; } /** * Gibt Konfigurationsparameter für einen Integrationsflow zurück * * @param {string} flowId ID des Integrationsflows * @param {string} [version='active'] Version des Integrationsflows * @param {string} [filter] Optionaler OData-Filterausdruck * @returns {Promise<ComSapHciApiConfiguration[]>} Promise mit einer Liste von Konfigurationen * * @example * const configs = await client.getIntegrationFlowConfigurations('MyFlowId'); */ async getIntegrationFlowConfigurations(flowId, version = 'active', filter) { const response = await this.api.integrationDesigntimeArtifactsIdIdVersionVersion.configurationsList(flowId, version, { $filter: filter }); return this.normalizer.normalizeArrayResponse(response.data, 'getIntegrationFlowConfigurations'); } /** * Gibt Konfigurationsparameter für einen Integrationsflow zurück (mit Caching) * * Diese Methode implementiert das Stale-While-Revalidate Pattern. * * @param {string} flowId ID des Integrationsflows * @param {string} [version='active'] Version des Integrationsflows * @param {string} [filter] Optionaler OData-Filterausdruck * @param {boolean} [forceRefresh=false] Erzwingt das Laden frischer Daten von SAP * @returns {Promise<{data: ComSapHciApiConfiguration[], cacheInfo: CacheInfo}>} Konfigurationen mit Cache-Informationen * * @example * const { data, cacheInfo } = await client.getIntegrationFlowConfigurationsWithCache('MyFlowId'); */ async getIntegrationFlowConfigurationsWithCache(flowId, version = 'active', filter, forceRefresh = false) { const endpoint = `/IntegrationDesigntimeArtifacts(Id='${flowId}',Version='${version}')/Configurations`; const queryParams = filter ? { filter } : undefined; // If no cacheManager or hostname, fall back to direct API call if (!this.cacheManager || !this.hostname) { const data = await this.getIntegrationFlowConfigurations(flowId, version, filter); return { data, cacheInfo: { hit: false, age: null, status: 'DISABLED', source: 'sap-api-direct', }, }; } const cacheKey = (0, cache_key_generator_1.generateCacheKey)(this.hostname, 'GET', endpoint, queryParams); const cacheOptions = { ttl: cache_config_1.CACHE_TTL.STANDARD, revalidateAfter: cache_config_1.REVALIDATE_AFTER.STANDARD }; const fetchFn = async () => { return this.getIntegrationFlowConfigurations(flowId, version, filter); }; const result = await this.cacheManager.getOrFetch(cacheKey, fetchFn, cacheOptions, forceRefresh); return { data: result.data, cacheInfo: { ...result.cacheInfo, key: cacheKey, source: 'npm-package-cache', }, }; } /** * Gibt die Anzahl der Konfigurationsparameter für einen Integrationsflow zurück. * * @param {string} flowId ID des Integrationsflows * @param {string} [version='active'] Version des Integrationsflows * @param {string} [filter] Optionaler OData-Filterausdruck * @returns {Promise<number>} Promise mit der Anzahl der Konfigurationen * * @example * const count = await client.getIntegrationFlowConfigurationCount('MyFlowId'); * console.log(`Flow has ${count} configurations.`); */ async getIntegrationFlowConfigurationCount(flowId, version = 'active', filter) { // Die API gibt keinen direkten Zähler zurück, daher holen wir alle und zählen sie. // Beachte: Das ist bei sehr vielen Konfigurationen ineffizient. // Eine bessere Implementierung würde die $count-Option nutzen, falls die generierte API sie unterstützt. // Da der generierte Client `configurationsCountList` hat, verwenden wir diesen. const response = await this.api.integrationDesigntimeArtifactsIdIdVersionVersion.configurationsCountList(flowId, version, { $filter: filter }); // Die API gibt die Anzahl als String im Body zurück, wenn $count verwendet wird. // Wir parsen diesen String zu einer Zahl. const countString = response.data; return parseInt(countString || '0', 10); } /** * Aktualisiert einen Konfigurationsparameter für einen Integrationsflow * * @param {string} flowId ID des Integrationsflows * @param {string} parameterKey Schlüssel des zu aktualisierenden Parameters * @param {string} parameterValue Neuer Wert für den Parameter * @param {string} [dataType] Datentyp des Parameters (z.B., 'xsd:string') * @param {string} [version='active'] Version des Integrationsflows * @returns {Promise<void>} Promise, der aufgelöst wird, wenn der Parameter aktualisiert wurde * * @example * await client.updateIntegrationFlowConfiguration( * 'MyFlowId', * 'Receiver_Host', * 'api.example.com', * 'xsd:string' * ); */ async updateIntegrationFlowConfiguration(flowId, parameterKey, parameterValue, dataType, version = 'active') { await this.api.integrationDesigntimeArtifactsIdIdVersionVersion.linksConfigurationsUpdate(flowId, version, parameterKey, { ParameterValue: parameterValue, DataType: dataType }); } /** * Gibt alle Service-Endpoints von deployten Integrationsflows zurück * * @param {Object} options Optionale Parameter für die Anfrage * @param {number} [options.top] Maximale Anzahl der zurückzugebenden Endpoints * @param {number} [options.skip] Anzahl der zu überspringenden Endpoints * @param {string} [options.filter] OData-Filterausdruck * @returns {Promise<ComSapHciApiServiceEndpoint[]>} Promise mit einer Liste von Service-Endpoints * * @example * const endpoints = await client.getServiceEndpoints(); */ async getServiceEndpoints(options = {}) { // Korrektur: OData-konforme Parameter für $expand // Bei OData werden mehrere expand-Werte durch Kommas getrennt in einem Parameter übergeben const response = await this.api.serviceEndpoints.serviceEndpointsList({ $top: options.top, $skip: options.skip, $filter: options.filter, $expand: "EntryPoints,ApiDefinitions" // Type Assertion, damit der TypeScript-Compiler dies akzeptiert }); return this.normalizer.normalizeArrayResponse(response.data, 'getServiceEndpoints'); } /** * Gibt Fehlerinformationen für ein deployten Integrationsartefakt zurück * * @param {string} artifactId ID des deployten Integrationsartefakts * @returns {Promise<ComSapHciApiRuntimeArtifactErrorInformation | null>} Promise mit den Fehlerinformationen oder null bei Fehlern * * @example * const errorInfo = await client.getArtifactErrorInformation('MyFailedFlow'); * if (errorInfo && errorInfo.Id) { * console.log(`Fehler-ID: ${errorInfo.Id}`); * } */ async getArtifactErrorInformation(artifactId) { try { // Verwende die generierte API, um das Artefakt mit Fehlerinformationen zu holen const response = await this.api.integrationRuntimeArtifactsId.integrationRuntimeArtifactsList(artifactId); // Hole das RuntimeArtifact aus der Antwort const runtimeArtifact = this.normalizer.normalizeEntityResponse(response.data, 'getDeployedArtifactById'); // Wenn es keine Fehlerinformationen gibt, gib null zurück if (!(runtimeArtifact === null || runtimeArtifact === void 0 ? void 0 : runtimeArtifact.ErrorInformation)) { return null; } // Da die ErrorInformation bereits im Artefakt enthalten ist, können wir sie direkt zurückgeben return runtimeArtifact.ErrorInformation; } catch (error) { console.error('Error fetching error information:', error); return null; } } /** * Gibt Fehlerinformationen für ein Artefakt zurück (mit Caching) * * Diese Methode implementiert das Stale-While-Revalidate Pattern. * Nutzt kürzere Revalidierungszeit da Fehlerinformationen sich häufig ändern können. * * @param {string} artifactId ID des Artefakts * @param {boolean} [forceRefresh=false] Erzwingt das Laden frischer Daten von SAP * @returns {Promise<{data: ComSapHciApiRuntimeArtifactErrorInformation | null, cacheInfo: CacheInfo}>} * * @example * const { data, cacheInfo } = await client.getArtifactErrorInformationWithCache('MyArtifactId'); */ async getArtifactErrorInformationWithCache(artifactId, forceRefresh = false) { const endpoint = `/IntegrationRuntimeArtifacts('${artifactId}')`; // If no cacheManager or hostname, fall back to direct API call if (!this.cacheManager || !this.hostname) { const data = await this.getArtifactErrorInformation(artifactId); return { data, cacheInfo: { hit: false, age: null, status: 'DISABLED', source: 'sap-api-direct', }, }; } const cacheKey = (0, cache_key_generator_1.generateCacheKey)(this.hostname, 'GET', endpoint); // Error information uses shorter revalidation time (5 minutes) const cacheOptions = { ttl: cache_config_1.CACHE_TTL.STANDARD, revalidateAfter: cache_config_1.REVALIDATE_AFTER.RUNTIME }; const fetchFn = async () => { return this.getArtifactErrorInformation(artifactId); }; const result = await this.cacheManager.getOrFetch(cacheKey, fetchFn, cacheOptions, forceRefresh); return { data: result.data, cacheInfo: { ...result.cacheInfo, key: cacheKey, source: 'npm-package-cache', }, }; } /** * Gibt detaillierte Fehlerinformationen für ein deployten Integrationsartefakt zurück * Diese Methode ruft den spezifischen $value-Endpunkt auf, der mehr Details enthält * * @param {string} artifactId ID des deployten Integrationsartefakts * @returns {Promise<DetailedErrorInformation | null>} Promise mit den detaillierten Fehlerinformationen oder null bei Fehlern * * @example * const detailedError = await client.getDetailedArtifactErrorInformation('MyFailedFlow'); * if (detailedError && detailedError.message) { * console.log(`Fehlertyp: ${detailedError.message.messageId}`); * if (detailedError.parameter && detailedError.parameter.length > 0) { * try { * const paramJson = JSON.parse(detailedError.parameter[0]); * console.log(`Fehlermeldung: ${paramJson.message}`); * } catch (e) { * console.log(`Parameter: ${detailedError.parameter[0]}`); * } * } * } * * @deprecated Diese Methode wird in zukünftigen Versionen ausgelagert. Bitte verwenden Sie die entsprechenden Methoden im IntegrationContentAdvancedClient. */ async getDetailedArtifactErrorInformation(artifactId) { return this.advancedClient.getDetailedArtifactErrorInformation(artifactId); } /** * Parst die Fehlerdetails aus den Parametern einer DetailedErrorInformation * * @param {DetailedErrorInformation} errorInfo Die detaillierten Fehlerinformationen * @returns {ParsedErrorDetails | null} Die geparsten Fehlerdetails oder null, wenn keine Parameter vorhanden oder das Parsing fehlschlägt * * @example * const detailedError = await client.getDetailedArtifactErrorInformation('MyFailedFlow'); * if (detailedError) { * const errorDetails = client.parseErrorDetails(detailedError); * if (errorDetails && errorDetails.message) { * console.log(`Fehlermeldung: ${errorDetails.message}`); * * if (errorDetails.childMessageInstances && errorDetails.childMessageInstances.length > 0) { * console.log(`Hauptursache: ${errorDetails.childMessageInstances[0].message}`); * } * } * } * * @deprecated Diese Methode wird in zukünftigen Versionen ausgelagert. Bitte verwenden Sie die entsprechenden Methoden im IntegrationContentAdvancedClient. */ parseErrorDetails(errorInfo) { return this.advancedClient.parseErrorDetails(errorInfo); } /** * Gibt alle Value Mappings für ein bestimmtes Paket zurück * * @param {string} packageId ID des Integrationspakets * @returns {Promise<ComSapHciApiValueMappingDesigntimeArtifact[]>} Promise mit einer Liste von Value Mappings * * @example * const valueMappings = await client.getValueMappings('MyPackageId'); */ async getValueMappings(packageId) { try { const response = await this.api.integrationPackagesId.valueMappingDesigntimeArtifactsList(packageId); return this.normalizer.normalizeArrayResponse(response.data, 'getValueMappings'); } catch (error) { // Check for the specific 500 Internal Server Error case if ((error === null || error === void 0 ? void 0 : error.statusCode) === 500 && (error === null || error === void 0 ? void 0 : error.errorCode) === 'Internal Server Error') { console.warn(`Failed to fetch Value Mappings for package ${packageId}. Skipping and returning empty array.`); return []; } // For other errors, rethrow throw error; } } /** * Gibt alle Message Mappings für ein bestimmtes Paket zurück * * @param {string} packageId ID des Integrationspakets * @returns {Promise<ComSapHciApiMessageMappingDesigntimeArtifact[]>} Promise mit einer Liste von Message Mappings * * @example * const messageMappings = await client.getMessageMappings('MyPackageId'); */ async getMessageMappings(packageId) { try { const response = await this.api.integrationPackagesId.messageMappingDesigntimeArtifactsList(packageId); return this.normalizer.normalizeArrayResponse(response.data, 'getMessageMappings'); } catch (error) { // Check for the specific 500 Internal Server Error case if ((error === null || error === void 0 ? void 0 : error.statusCode) === 500 && (error === null || error === void 0 ? void 0 : error.errorCode) === 'Internal Server Error') { console.warn(`Failed to fetch Message Mappings for package ${packageId}. Skipping and returning empty array.`); return []; } // For other errors, rethrow throw error; } } /** * Ruft alle Integrationspakete mit ihren zugehörigen Artefakten ab * * Diese Methode holt alle Integrationspakete und sammelt für jedes Paket: * - Integrationsflows * - Message Mappings * - Value Mappings * - Script Collections * * @param {Object} options Optionale Parameter für die Anfrage * @param {number} [options.top] Maximale Anzahl der zurückzugebenden Pakete * @param {number} [options.skip] Anzahl der zu überspringenden Pakete * @param {boolean} [options.includeEmpty=false] Ob leere Pakete ohne Artefakte inkludiert werden sollen * @param {boolean} [options.parallel=false] Ob die Artefakte parallel abgerufen werden sollen (schneller, aber möglicherweise API-Limits) * @returns {Promise<PackageWithArtifacts[]>} Liste von Paketen mit ihren Artefakten * * @example * // Alle Pakete mit ihren Artefakten abrufen * const packagesWithArtifacts = await client.getPackagesWithArtifacts(); * * // Nur die ersten 5 Pakete mit Artefakten abrufen * const packagesWithArtifacts = await client.getPackagesWithArtifacts({ top: 5 }); * * // Alle Pakete mit parallelen API-Aufrufen abrufen (schneller) * const packagesWithArtifacts = await client.getPackagesWithArtifacts({ parallel: true }); * * @deprecated Diese Methode wird in zukünftigen Versionen ausgelagert. Bitte verwenden Sie die entsprechenden Methoden im IntegrationContentAdvancedClient. */ async getPackagesWithArtifacts(options = {}) { return this.advancedClient.getPackagesWithArtifacts(options); } /** * Ruft alle Integrationspakete mit ihren zugehörigen Artefakten ab (mit Caching) * * Diese Methode implementiert das Stale-While-Revalidate Pattern: * - Frische Daten aus Cache werden sofort zurückgegeben * - Veraltete Daten werden sofort zurückgegeben und im Hintergrund aktualisiert * - Bei Cache-Miss werden frische Daten von SAP geholt und gecacht * * @param {Object} options Optionale Parameter für die Anfrage * @param {number} [options.top] Maximale Anzahl der zurückzugebenden Pakete * @param {number} [options.skip] Anzahl der zu überspringenden Pakete * @param {boolean} [options.includeEmpty=false] Ob leere Pakete ohne Artefakte inkludiert werden sollen * @param {boolean} [options.parallel=false] Ob die Artefakte parallel abgerufen werden solle