@contiva/sap-integration-suite-client
Version:
SAP Cloud Platform Integration API Client
1,020 lines • 136 kB
JavaScript
"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