@contiva/sap-integration-suite-client
Version:
SAP Cloud Platform Integration API Client
1,157 lines • 55.6 kB
JavaScript
"use strict";
/**
* Helper functions for updating cache entries
*
* @module cache-update-helper
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.updateArtifactInCache = updateArtifactInCache;
exports.updateArtifactInPackageCache = updateArtifactInPackageCache;
exports.getNestedValue = getNestedValue;
exports.setNestedValue = setNestedValue;
exports.addArtifactToCacheArray = addArtifactToCacheArray;
exports.compareArtifacts = compareArtifacts;
exports.mergeArtifactsDifferentially = mergeArtifactsDifferentially;
exports.removeArtifactFromCacheArray = removeArtifactFromCacheArray;
/**
* Updates an artifact's status in a cache entry
* Handles various cache formats (Array, OData-Format, etc.)
*
* @param cachedData - The cached data to update
* @param artifactId - The ID of the artifact to update
* @param statusData - The status data to apply
* @returns Updated cached data, or null if artifact not found
*
* @example
* const updated = updateArtifactInCache(cachedData, 'MyArtifact', {
* Status: 'STARTED',
* DeployedBy: 'user@example.com',
* DeployedOn: '2024-01-01T00:00:00Z'
* });
*/
function updateArtifactInCache(cachedData, artifactId, statusData) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
try {
const data = cachedData.data;
// Handle different cache formats
let artifacts = null;
// Format 1: Direct array
if (Array.isArray(data)) {
artifacts = data;
}
// Format 2: OData v2 format (d.results)
else if (((_a = data === null || data === void 0 ? void 0 : data.d) === null || _a === void 0 ? void 0 : _a.results) && Array.isArray(data.d.results)) {
artifacts = data.d.results;
}
// Format 3: OData v4 format (value array)
else if ((data === null || data === void 0 ? void 0 : data.value) && Array.isArray(data.value)) {
artifacts = data.value;
}
// Format 4: IntegrationPackages format
else if ((data === null || data === void 0 ? void 0 : data.IntegrationPackages) && Array.isArray(data.IntegrationPackages)) {
artifacts = data.IntegrationPackages;
}
// Format 5: IntegrationRuntimeArtifacts format
else if ((data === null || data === void 0 ? void 0 : data.IntegrationRuntimeArtifacts) && Array.isArray(data.IntegrationRuntimeArtifacts)) {
artifacts = data.IntegrationRuntimeArtifacts;
}
// Format 6: Single object (not an array)
else if (data && typeof data === 'object' && !Array.isArray(data)) {
// Check if this is a single artifact object
if (data.Id === artifactId || data.id === artifactId) {
// If Status is null, remove the artifact (set data to null or empty)
if (statusData.Status === null) {
// For single artifact objects, we can't really "remove" it,
// but we can set it to null or an empty object
// However, this might break the cache structure, so we return null
// to indicate the cache entry should be invalidated
return null;
}
// Update the single artifact
const updated = { ...data };
if (statusData.Status !== undefined) {
updated.Status = statusData.Status;
}
if (statusData.DeployedBy !== undefined) {
updated.DeployedBy = statusData.DeployedBy;
}
if (statusData.DeployedOn !== undefined) {
updated.DeployedOn = statusData.DeployedOn;
}
return {
...cachedData,
data: updated,
};
}
// Heuristik: Struktur nicht erkannt oder semantisch andere Ressource
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Cache structure not recognized or semantically different resource. ' +
'Expected artifact array or single artifact object, but got:', {
hasData: !!data,
dataType: typeof data,
isArray: Array.isArray(data),
keys: data ? Object.keys(data).slice(0, 10) : [],
artifactId
});
}
return null;
}
// If we have an array of artifacts, find and update the matching one
if (artifacts) {
// Heuristik: Prüfe, ob Array die erwartete Struktur hat (z.B. Artefakte mit Id/id)
const hasExpectedStructure = artifacts.some((item) => item && typeof item === 'object' && (item.Id || item.id));
if (!hasExpectedStructure && artifacts.length > 0) {
// Array hat nicht die erwartete Struktur (semantisch andere Ressource)
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Array structure does not match expected artifact format. ' +
'Expected artifacts with Id/id property, but got:', {
arrayLength: artifacts.length,
firstItemType: artifacts[0] ? typeof artifacts[0] : 'null',
firstItemKeys: artifacts[0] && typeof artifacts[0] === 'object' ? Object.keys(artifacts[0]).slice(0, 10) : [],
artifactId
});
}
return null; // No-op: Struktur nicht erkannt
}
const artifactIndex = artifacts.findIndex((artifact) => artifact.Id === artifactId || artifact.id === artifactId);
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Searching for artifact in array:', {
artifactId,
arrayLength: artifacts.length,
artifactIndex,
statusData,
sampleIds: artifacts.slice(0, 3).map((a) => ({ Id: a === null || a === void 0 ? void 0 : a.Id, id: a === null || a === void 0 ? void 0 : a.id })),
});
}
if (artifactIndex === -1) {
// Artifact not found in array
// If Status is null, the artifact should be removed from the collection (already removed)
if (statusData.Status === null) {
// Artifact already removed from collection - this is expected for undeploy
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Artifact not found in array (expected for undeploy):', {
artifactId,
arrayLength: artifacts.length
});
}
// Return null to indicate no update needed (artifact already removed)
return null;
}
// For deploy/status updates: If artifact is not in collection, add it
// This can happen if the artifact was just deployed and not yet in the cached collection
// Check if Status is defined and not null (including empty string check)
const shouldAddArtifact = statusData.Status !== undefined && statusData.Status !== null && statusData.Status !== '';
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Artifact not found in array, checking if should add:', {
artifactId,
arrayLength: artifacts.length,
statusData,
shouldAddArtifact,
statusValue: statusData.Status,
statusType: typeof statusData.Status,
});
}
if (shouldAddArtifact) {
// Create a new artifact object with the status data
const newArtifact = {
Id: artifactId,
id: artifactId,
...statusData,
};
// Add the artifact to the array
const updatedArtifacts = [...artifacts, newArtifact];
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Adding artifact to collection:', {
artifactId,
originalArrayLength: artifacts.length,
newArrayLength: updatedArtifacts.length,
newArtifact,
});
}
// Reconstruct the data structure with the new artifact added
let updatedData;
if (Array.isArray(data)) {
updatedData = updatedArtifacts;
}
else if ((_b = data === null || data === void 0 ? void 0 : data.d) === null || _b === void 0 ? void 0 : _b.results) {
updatedData = {
...data,
d: {
...data.d,
results: updatedArtifacts,
},
};
}
else if (data === null || data === void 0 ? void 0 : data.value) {
updatedData = {
...data,
value: updatedArtifacts,
};
}
else if (data === null || data === void 0 ? void 0 : data.IntegrationPackages) {
updatedData = {
...data,
IntegrationPackages: updatedArtifacts,
};
}
else if (data === null || data === void 0 ? void 0 : data.IntegrationRuntimeArtifacts) {
updatedData = {
...data,
IntegrationRuntimeArtifacts: updatedArtifacts,
};
}
else {
updatedData = updatedArtifacts;
}
const result = {
...cachedData,
data: updatedData,
};
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Returning updated cache data:', {
artifactId,
hasData: !!result.data,
dataType: Array.isArray(result.data) ? 'array' : typeof result.data,
dataLength: Array.isArray(result.data) ? result.data.length : (((_e = (_d = (_c = result.data) === null || _c === void 0 ? void 0 : _c.d) === null || _d === void 0 ? void 0 : _d.results) === null || _e === void 0 ? void 0 : _e.length) || ((_g = (_f = result.data) === null || _f === void 0 ? void 0 : _f.value) === null || _g === void 0 ? void 0 : _g.length) || 'unknown'),
});
}
return result;
}
// For other cases, artifact should be in collection but isn't
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Artifact not found in array:', {
artifactId,
arrayLength: artifacts.length,
statusData
});
}
return null; // Artifact not found and no status to add
}
// If Status is null, remove the artifact from the collection (undeploy)
if (statusData.Status === null) {
const updatedArtifacts = artifacts.filter((_, index) => index !== artifactIndex);
// Reconstruct the data structure with the artifact removed
let updatedData;
if (Array.isArray(data)) {
updatedData = updatedArtifacts;
}
else if ((_h = data === null || data === void 0 ? void 0 : data.d) === null || _h === void 0 ? void 0 : _h.results) {
updatedData = {
...data,
d: {
...data.d,
results: updatedArtifacts,
},
};
}
else if (data === null || data === void 0 ? void 0 : data.value) {
updatedData = {
...data,
value: updatedArtifacts,
};
}
else if (data === null || data === void 0 ? void 0 : data.IntegrationPackages) {
updatedData = {
...data,
IntegrationPackages: updatedArtifacts,
};
}
else if (data === null || data === void 0 ? void 0 : data.IntegrationRuntimeArtifacts) {
updatedData = {
...data,
IntegrationRuntimeArtifacts: updatedArtifacts,
};
}
else {
updatedData = updatedArtifacts;
}
return {
...cachedData,
data: updatedData,
};
}
// Update the artifact
const updatedArtifacts = [...artifacts];
const artifact = { ...updatedArtifacts[artifactIndex] };
if (statusData.Status !== undefined) {
artifact.Status = statusData.Status;
}
if (statusData.DeployedBy !== undefined) {
artifact.DeployedBy = statusData.DeployedBy;
}
if (statusData.DeployedOn !== undefined) {
artifact.DeployedOn = statusData.DeployedOn;
}
updatedArtifacts[artifactIndex] = artifact;
// Reconstruct the data structure with the updated array
let updatedData;
if (Array.isArray(data)) {
updatedData = updatedArtifacts;
}
else if ((_j = data === null || data === void 0 ? void 0 : data.d) === null || _j === void 0 ? void 0 : _j.results) {
updatedData = {
...data,
d: {
...data.d,
results: updatedArtifacts,
},
};
}
else if (data === null || data === void 0 ? void 0 : data.value) {
updatedData = {
...data,
value: updatedArtifacts,
};
}
else if (data === null || data === void 0 ? void 0 : data.IntegrationPackages) {
updatedData = {
...data,
IntegrationPackages: updatedArtifacts,
};
}
else if (data === null || data === void 0 ? void 0 : data.IntegrationRuntimeArtifacts) {
updatedData = {
...data,
IntegrationRuntimeArtifacts: updatedArtifacts,
};
}
else {
updatedData = updatedArtifacts;
}
return {
...cachedData,
data: updatedData,
};
}
// Heuristik: Keine erkannte Struktur gefunden
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] No recognized cache structure found. ' +
'Expected array, OData format, or single artifact object, but got:', {
hasData: !!data,
dataType: typeof data,
isArray: Array.isArray(data),
dataKeys: data && typeof data === 'object' ? Object.keys(data).slice(0, 10) : [],
artifactId
});
}
return null;
}
catch (error) {
console.error('[CacheUpdateHelper] Error updating artifact in cache:', error);
return null;
}
}
/**
* Updates an artifact's status in a package cache entry
* Finds the artifact in the package's artifact arrays and updates it
*
* @param cachedData - The cached package data to update
* @param artifactId - The ID of the artifact to update
* @param statusData - The status data to apply
* @returns Updated cached data, or null if artifact not found
*
* @example
* const updated = updateArtifactInPackageCache(cachedData, 'MyArtifact', {
* Status: 'STARTED'
* });
*/
function updateArtifactInPackageCache(cachedData, artifactId, statusData) {
var _a, _b;
try {
const data = cachedData.data;
// Handle package format - could be single package or array of packages
let packages = [];
if (Array.isArray(data)) {
packages = data;
}
else if (((_a = data === null || data === void 0 ? void 0 : data.d) === null || _a === void 0 ? void 0 : _a.results) && Array.isArray(data.d.results)) {
packages = data.d.results;
}
else if ((data === null || data === void 0 ? void 0 : data.value) && Array.isArray(data.value)) {
packages = data.value;
}
else if ((data === null || data === void 0 ? void 0 : data.IntegrationPackages) && Array.isArray(data.IntegrationPackages)) {
packages = data.IntegrationPackages;
}
else if (data && typeof data === 'object' && !Array.isArray(data)) {
// Single package object
packages = [data];
}
let found = false;
const updatedPackages = packages.map((pkg) => {
// Check all artifact arrays in the package
// Note: IntegrationFlows is not a property of packages - use IntegrationDesigntimeArtifacts instead
const artifactArrays = [
pkg.IntegrationDesigntimeArtifacts,
pkg.ValueMappingDesigntimeArtifacts,
pkg.MessageMappingDesigntimeArtifacts,
pkg.ScriptCollectionDesigntimeArtifacts,
].filter(Boolean);
let packageUpdated = false;
const updatedPkg = { ...pkg };
for (const artifactArray of artifactArrays) {
if (Array.isArray(artifactArray)) {
const artifactIndex = artifactArray.findIndex((artifact) => artifact.Id === artifactId || artifact.id === artifactId);
if (artifactIndex !== -1) {
found = true;
packageUpdated = true;
const updatedArtifacts = [...artifactArray];
const artifact = { ...updatedArtifacts[artifactIndex] };
if (statusData.Status !== undefined) {
artifact.Status = statusData.Status;
}
if (statusData.DeployedBy !== undefined) {
artifact.DeployedBy = statusData.DeployedBy;
}
if (statusData.DeployedOn !== undefined) {
artifact.DeployedOn = statusData.DeployedOn;
}
updatedArtifacts[artifactIndex] = artifact;
// Update the corresponding array in the package
if (pkg.IntegrationDesigntimeArtifacts === artifactArray) {
updatedPkg.IntegrationDesigntimeArtifacts = updatedArtifacts;
}
else if (pkg.ValueMappingDesigntimeArtifacts === artifactArray) {
updatedPkg.ValueMappingDesigntimeArtifacts = updatedArtifacts;
}
else if (pkg.MessageMappingDesigntimeArtifacts === artifactArray) {
updatedPkg.MessageMappingDesigntimeArtifacts = updatedArtifacts;
}
else if (pkg.ScriptCollectionDesigntimeArtifacts === artifactArray) {
updatedPkg.ScriptCollectionDesigntimeArtifacts = updatedArtifacts;
}
}
}
}
return packageUpdated ? updatedPkg : pkg;
});
if (!found) {
return null; // Artifact not found in any package
}
// Reconstruct the data structure
let updatedData;
if (Array.isArray(data)) {
updatedData = updatedPackages;
}
else if ((_b = data === null || data === void 0 ? void 0 : data.d) === null || _b === void 0 ? void 0 : _b.results) {
updatedData = {
...data,
d: {
...data.d,
results: updatedPackages,
},
};
}
else if (data === null || data === void 0 ? void 0 : data.value) {
updatedData = {
...data,
value: updatedPackages,
};
}
else if (data === null || data === void 0 ? void 0 : data.IntegrationPackages) {
updatedData = {
...data,
IntegrationPackages: updatedPackages,
};
}
else {
// Single package object
updatedData = updatedPackages[0];
}
return {
...cachedData,
data: updatedData,
};
}
catch (error) {
console.error('[CacheUpdateHelper] Error updating artifact in package cache:', error);
return null;
}
}
/**
* Gets a nested value from an object using a dot-notation path
* Supports array indexing (e.g. 'data.d.results[0].Status')
*
* @param obj - The object to get the value from
* @param path - The dot-notation path (e.g. 'data.Status' or 'data.d.results[0].Status')
* @returns The value at the path, or undefined if the path doesn't exist
*
* @example
* const value = getNestedValue(data, 'data.Status');
* const arrayValue = getNestedValue(data, 'data.d.results[0].Name');
*/
function getNestedValue(obj, path) {
if (!obj || !path) {
return undefined;
}
try {
const parts = path.split('.');
let current = obj;
for (const part of parts) {
// Check if part contains array index (e.g. 'results[0]')
const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
if (arrayMatch) {
const [, arrayName, index] = arrayMatch;
if (current && typeof current === 'object' && arrayName in current) {
const array = current[arrayName];
if (Array.isArray(array) && parseInt(index, 10) < array.length) {
current = array[parseInt(index, 10)];
}
else {
return undefined;
}
}
else {
return undefined;
}
}
else {
if (current && typeof current === 'object' && part in current) {
current = current[part];
}
else {
return undefined;
}
}
}
return current;
}
catch (error) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Error getting nested value:', { path, error });
}
return undefined;
}
}
/**
* Sets a nested value in an object using a dot-notation path
* Supports array indexing (e.g. 'data.d.results[0].Status')
* Creates intermediate objects/arrays if they don't exist
*
* @param obj - The object to set the value in
* @param path - The dot-notation path (e.g. 'data.Status' or 'data.d.results[0].Status')
* @param value - The value to set
* @returns true if the value was set successfully, false otherwise
*
* @example
* setNestedValue(data, 'data.Status', 'STARTED');
* setNestedValue(data, 'data.d.results[0].Name', 'MyArtifact');
*/
function setNestedValue(obj, path, value) {
if (!obj || !path) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Invalid input', { hasObj: !!obj, hasPath: !!path });
}
return false;
}
try {
const parts = path.split('.');
if (parts.length === 0) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Empty path parts', { path });
}
return false;
}
let current = obj;
// Navigate to the parent of the target property
for (let i = 0; i < parts.length - 1; i++) {
const part = parts[i];
const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
if (arrayMatch) {
const [, arrayName, index] = arrayMatch;
// Check if current is a valid object and has the array property
if (!current || typeof current !== 'object' || Array.isArray(current)) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Invalid current for array access', {
part,
currentType: typeof current,
isArray: Array.isArray(current),
path
});
}
return false;
}
// Use 'in' operator to check if property exists (more compatible with nested objects)
if (!(arrayName in current)) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Array property not found', {
arrayName,
availableKeys: Object.keys(current).slice(0, 10),
path
});
}
return false;
}
const array = current[arrayName];
if (!Array.isArray(array)) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Property is not an array', {
arrayName,
actualType: typeof array,
path
});
}
return false;
}
const arrayIndex = parseInt(index, 10);
if (isNaN(arrayIndex) || arrayIndex < 0 || arrayIndex >= array.length) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Array index out of bounds', {
arrayName,
index: arrayIndex,
arrayLength: array.length,
path
});
}
return false;
}
current = array[arrayIndex];
}
else {
// Check if current is a valid object (not null, not array, not primitive)
if (!current || typeof current !== 'object' || Array.isArray(current)) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Invalid current for property access', {
part,
currentType: typeof current,
isArray: Array.isArray(current),
path
});
}
return false;
}
// Use 'in' operator to check if property exists (more compatible with nested objects)
if (!(part in current)) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Property not found', {
part,
availableKeys: Object.keys(current).slice(0, 10),
path
});
}
return false;
}
current = current[part];
}
}
// Set the value at the final path
const finalPart = parts[parts.length - 1];
const arrayMatch = finalPart.match(/^(\w+)\[(\d+)\]$/);
if (arrayMatch) {
const [, arrayName, index] = arrayMatch;
if (!current || typeof current !== 'object' || Array.isArray(current)) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Invalid current for final array access', {
arrayName,
currentType: typeof current,
path
});
}
return false;
}
// Use 'in' operator to check if property exists (more compatible with nested objects)
if (!(arrayName in current)) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Final array property not found', {
arrayName,
availableKeys: Object.keys(current).slice(0, 10),
path
});
}
return false;
}
const array = current[arrayName];
if (!Array.isArray(array)) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Final property is not an array', {
arrayName,
actualType: typeof array,
path
});
}
return false;
}
const arrayIndex = parseInt(index, 10);
if (isNaN(arrayIndex) || arrayIndex < 0 || arrayIndex >= array.length) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Final array index out of bounds', {
arrayName,
index: arrayIndex,
arrayLength: array.length,
path
});
}
return false;
}
array[arrayIndex] = value;
}
else {
// For setting a property, we need current to be a non-null object (not array, not null, not primitive)
if (!current || typeof current !== 'object' || Array.isArray(current) || current === null) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Invalid current for final property access', {
finalPart,
currentType: typeof current,
isArray: Array.isArray(current),
isNull: current === null,
current: JSON.stringify(current).substring(0, 100),
path
});
}
return false;
}
// Set the value directly - we don't need to check if property exists for setting
current[finalPart] = value;
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] setNestedValue: Successfully set value', {
finalPart,
path,
valueType: typeof value
});
}
}
return true;
}
catch (error) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Error setting nested value:', { path, value, error });
}
return false;
}
}
/**
* Adds an artifact to a cache array
* Handles various cache formats (Array, OData-Format, etc.)
*
* @param cachedData - The cached data to update
* @param artifact - The artifact to add
* @param options - Optional configuration
* @param options.arrayPath - Custom dot-notation path to the array (e.g. 'data.custom.path')
* @param options.preventDuplicates - If true, prevents adding duplicate artifacts (checks Id/id property)
* @returns Updated cached data, or null if error or duplicate found (when preventDuplicates is true)
*
* @example
* const updated = addArtifactToCacheArray(cachedData, newArtifact, {
* preventDuplicates: true
* });
*/
function addArtifactToCacheArray(cachedData, artifact, options) {
var _a;
try {
const data = cachedData.data;
// Handle custom array path if provided
if (options === null || options === void 0 ? void 0 : options.arrayPath) {
const array = getNestedValue(data, options.arrayPath);
if (!Array.isArray(array)) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Custom array path does not point to an array:', {
arrayPath: options.arrayPath
});
}
return null;
}
// Check for duplicates if enabled
if (options.preventDuplicates) {
const artifactId = (artifact === null || artifact === void 0 ? void 0 : artifact.Id) || (artifact === null || artifact === void 0 ? void 0 : artifact.id);
if (artifactId) {
const duplicate = array.find((item) => item && (item.Id === artifactId || item.id === artifactId));
if (duplicate) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Duplicate artifact found, preventing addition:', {
artifactId
});
}
return null;
}
}
}
// Add artifact to array
const updatedArray = [...array, artifact];
// Reconstruct data structure with updated array
const updatedData = { ...data };
setNestedValue(updatedData, options.arrayPath, updatedArray);
return {
...cachedData,
data: updatedData,
};
}
// Handle different cache formats
let artifacts = null;
let formatType = null;
// Format 1: Direct array
if (Array.isArray(data)) {
artifacts = data;
formatType = 'direct';
}
// Format 2: OData v2 format (d.results)
else if (((_a = data === null || data === void 0 ? void 0 : data.d) === null || _a === void 0 ? void 0 : _a.results) && Array.isArray(data.d.results)) {
artifacts = data.d.results;
formatType = 'odata-v2';
}
// Format 3: OData v4 format (value array)
else if ((data === null || data === void 0 ? void 0 : data.value) && Array.isArray(data.value)) {
artifacts = data.value;
formatType = 'odata-v4';
}
// Format 4: IntegrationPackages format
else if ((data === null || data === void 0 ? void 0 : data.IntegrationPackages) && Array.isArray(data.IntegrationPackages)) {
artifacts = data.IntegrationPackages;
formatType = 'integration-packages';
}
// Format 5: IntegrationRuntimeArtifacts format
else if ((data === null || data === void 0 ? void 0 : data.IntegrationRuntimeArtifacts) && Array.isArray(data.IntegrationRuntimeArtifacts)) {
artifacts = data.IntegrationRuntimeArtifacts;
formatType = 'integration-runtime-artifacts';
}
if (!artifacts) {
// Unrecognized format
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Cache structure not recognized for addArtifactToCacheArray. ' +
'Expected array, OData format, or known structure, but got:', {
hasData: !!data,
dataType: typeof data,
isArray: Array.isArray(data),
keys: data && typeof data === 'object' ? Object.keys(data).slice(0, 10) : [],
});
}
return null;
}
// Check for duplicates if enabled
if (options === null || options === void 0 ? void 0 : options.preventDuplicates) {
const artifactId = (artifact === null || artifact === void 0 ? void 0 : artifact.Id) || (artifact === null || artifact === void 0 ? void 0 : artifact.id);
if (artifactId) {
const duplicate = artifacts.find((item) => item && (item.Id === artifactId || item.id === artifactId));
if (duplicate) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Duplicate artifact found, preventing addition:', {
artifactId
});
}
return null;
}
}
}
// Add artifact to array
const updatedArtifacts = [...artifacts, artifact];
// Reconstruct the data structure with the updated array
let updatedData;
if (formatType === 'direct') {
updatedData = updatedArtifacts;
}
else if (formatType === 'odata-v2') {
updatedData = {
...data,
d: {
...data.d,
results: updatedArtifacts,
},
};
}
else if (formatType === 'odata-v4') {
updatedData = {
...data,
value: updatedArtifacts,
};
}
else if (formatType === 'integration-packages') {
updatedData = {
...data,
IntegrationPackages: updatedArtifacts,
};
}
else if (formatType === 'integration-runtime-artifacts') {
updatedData = {
...data,
IntegrationRuntimeArtifacts: updatedArtifacts,
};
}
else {
updatedData = updatedArtifacts;
}
return {
...cachedData,
data: updatedData,
};
}
catch (error) {
console.error('[CacheUpdateHelper] Error adding artifact to cache:', error);
return null;
}
}
/**
* Compares two arrays of artifacts and identifies changes
* Used for differential cache updates during background revalidation
*
* @param oldArtifacts - The old artifacts from cache
* @param newArtifacts - The new artifacts from SAP API
* @returns Comparison result with added, updated, removed, and unchanged artifacts
*
* @example
* const comparison = compareArtifacts(cachedData.data, freshData);
* console.log(`Added: ${comparison.added.length}, Updated: ${comparison.updated.length}`);
*/
function compareArtifacts(oldArtifacts, newArtifacts) {
const result = {
added: [],
updated: [],
removed: [],
unchanged: [],
hasChanges: false,
};
try {
// Handle empty arrays
if (!Array.isArray(oldArtifacts) || !Array.isArray(newArtifacts)) {
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Invalid input for compareArtifacts - not arrays:', {
oldIsArray: Array.isArray(oldArtifacts),
newIsArray: Array.isArray(newArtifacts),
});
}
return result;
}
// Create maps for efficient lookup
// Use both Id and id for compatibility
const oldMap = new Map();
const newMap = new Map();
// Build old artifacts map
for (const artifact of oldArtifacts) {
if (!artifact)
continue;
const id = artifact.Id || artifact.id;
if (id) {
oldMap.set(id, artifact);
}
}
// Build new artifacts map
for (const artifact of newArtifacts) {
if (!artifact)
continue;
const id = artifact.Id || artifact.id;
if (id) {
newMap.set(id, artifact);
}
}
// Find added and updated artifacts
for (const [id, newArtifact] of newMap.entries()) {
const oldArtifact = oldMap.get(id);
if (!oldArtifact) {
// New artifact (deployed since last cache)
result.added.push(newArtifact);
}
else {
// Check if artifact has changed
// Compare relevant status fields
const hasStatusChanged = oldArtifact.Status !== newArtifact.Status ||
oldArtifact.DeployedBy !== newArtifact.DeployedBy ||
oldArtifact.DeployedOn !== newArtifact.DeployedOn;
if (hasStatusChanged) {
result.updated.push(newArtifact);
}
else {
result.unchanged.push(newArtifact);
}
}
}
// Find removed artifacts
for (const [id, oldArtifact] of oldMap.entries()) {
if (!newMap.has(id)) {
// Artifact was removed (undeployed)
result.removed.push(oldArtifact);
}
}
// Determine if any changes occurred
result.hasChanges =
result.added.length > 0 ||
result.updated.length > 0 ||
result.removed.length > 0;
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] compareArtifacts result:', {
total: newArtifacts.length,
added: result.added.length,
updated: result.updated.length,
removed: result.removed.length,
unchanged: result.unchanged.length,
hasChanges: result.hasChanges,
});
}
return result;
}
catch (error) {
console.error('[CacheUpdateHelper] Error comparing artifacts:', error);
return result;
}
}
/**
* Merges old cached data with comparison results using differential updates
* Performs selective updates: only changes added/updated/removed artifacts
* ALWAYS updates cache metadata (cachedAt, expiresAt, revalidateAfter) to mark cache as "fresh"
*
* @param cachedData - The old cached data
* @param newArtifacts - The complete new artifacts from SAP API
* @param comparison - The comparison result between old and new
* @param options - Cache options for metadata updates
* @returns Updated CachedData with refreshed metadata
*
* @example
* const comparison = compareArtifacts(oldArtifacts, newArtifacts);
* const merged = mergeArtifactsDifferentially(cachedData, newArtifacts, comparison, options);
*/
function mergeArtifactsDifferentially(cachedData, newArtifacts, comparison, options) {
var _a;
try {
const data = cachedData.data;
// If no changes, just update metadata and return
if (!comparison.hasChanges) {
const now = Date.now();
return {
...cachedData,
cachedAt: now,
expiresAt: now + (options.ttl * 1000),
revalidateAfter: now + (options.revalidateAfter * 1000),
};
}
// Extract artifacts array from the various cache formats
let artifacts = null;
let formatType = null;
// Format 1: Direct array
if (Array.isArray(data)) {
artifacts = data;
formatType = 'direct';
}
// Format 2: OData v2 format (d.results)
else if (((_a = data === null || data === void 0 ? void 0 : data.d) === null || _a === void 0 ? void 0 : _a.results) && Array.isArray(data.d.results)) {
artifacts = data.d.results;
formatType = 'odata-v2';
}
// Format 3: OData v4 format (value array)
else if ((data === null || data === void 0 ? void 0 : data.value) && Array.isArray(data.value)) {
artifacts = data.value;
formatType = 'odata-v4';
}
// Format 4: IntegrationPackages format
else if ((data === null || data === void 0 ? void 0 : data.IntegrationPackages) && Array.isArray(data.IntegrationPackages)) {
artifacts = data.IntegrationPackages;
formatType = 'integration-packages';
}
// Format 5: IntegrationRuntimeArtifacts format
else if ((data === null || data === void 0 ? void 0 : data.IntegrationRuntimeArtifacts) && Array.isArray(data.IntegrationRuntimeArtifacts)) {
artifacts = data.IntegrationRuntimeArtifacts;
formatType = 'integration-runtime-artifacts';
}
if (!artifacts) {
// Fallback: If format is not recognized, replace entirely with new data
if (process.env.DEBUG === 'true') {
console.debug('[CacheUpdateHelper] Cache format not recognized for differential merge, using full replacement');
}
const now = Date.now();
return {
data: newArtifacts,
cachedAt: now,
expiresAt: now + (options.ttl * 1000),
revalidateAfter: now + (options.revalidateAfter * 1000),
};
}
// Build a map of artifacts by ID for efficient updates
const artifactMap = new Map();
for (const artifact of artifacts) {
if (!artifact)
continue;
const id = artifact.Id || artifact.id;
if (id) {
artifactMap.set(id, artifact);
}
}
// Apply changes:
// 1. Remove removed artifacts
for (const removed of comparison.removed) {
const id = removed.Id || removed.id;
if (id) {
artifactMap.delete(id);
}
}
// 2. Update changed artifacts
for (const updated of comparison.updated) {
const id = updated.Id || updated.id;
if (id) {
artifactMap.set(id, updated);
}
}
// 3. Add new artifacts
for (const added of comparison.added) {
const id = added.Id || added.id;
if (id) {
artifactMap.set(id, added);
}
}
// Convert map back to array
const mergedArtifacts = Array.from(artifactMap.values());
// Reconstruct the data structure with the merged array
let updatedData;
if (formatType === 'direct') {
updatedData = mergedArtifacts;
}
else if (formatType === 'odata-v2') {
updatedData = {
...data,
d: {
...data.d,
results: mergedArtifacts,
},
};
}
else if (formatType === 'odata-v4') {
updatedData = {
...data,
value: mergedArtifacts,
};
}
else if (formatType === 'integration-packages') {
updatedData = {
...data,
IntegrationPackages: mergedArtifacts,
};
}
else if (formatType === 'integration-runtime-artifacts') {
updatedData = {
...data,
IntegrationRuntimeArtifacts: mergedArtifacts,
};
}
else {
updatedData = mergedArtifacts;
}
// ALWAYS update cache metadata to mark as "fresh"
const now = Date.now();
return {
data: updatedData,
cachedAt: now,
expiresAt: now + (options.ttl * 1000),
revalidateAfter: now + (options.revalidateAfter * 1000),
};
}
catch (error) {
console.error('[CacheUpdateHelper] Error merging artifacts differentially:', error);
// Fallback: Return full replacement on error
const now = Date.now();
return {
data: newArtifacts,
cachedAt: now,
expiresAt: now + (options.ttl * 1000),
revalidateAfter: now + (options.revalidateAfter * 1000),
};
}
}
/**
* Removes an artifact from a cache array
* Handles various cache formats (Array, OData-Format, etc.)
*
* @param cachedData - The cached data to update
* @param artifactId - The ID of the artifact to remove
* @param options - Optional configuration
* @param options.arrayPath - Custom dot-notation path to the array (e.g. 'data.custom.path')
* @returns Updated cached data, or null if artifact not found or error occurred
*
* @example
* const updated = removeArtifactFromCacheArray(cachedData, 'MyArtifactId');
*/
function removeArtifactF