Files
deployment-portal-fe/src/helper/ChangeRequest.ts

354 lines
9.8 KiB
TypeScript

import _ from 'lodash';
import { toast } from 'react-toastify';
import { getHashedIdentifier } from './changeRequestUtils';
interface ValueWithWeight {
type: string;
weight: number;
}
const isTarget = limitObject => {
return limitObject.hasOwnProperty('approvalFrom');
};
const isObject = input => {
return (
null !== input &&
typeof input === 'object' &&
Object.getPrototypeOf(input).isPrototypeOf(Object)
);
};
export const getBreachPath = (manifestPath, key, index, hasHashedIdentifier) => {
if (hasHashedIdentifier) {
return `${manifestPath}/${key}/*`;
}
return `${manifestPath}/${key}/${index}`;
};
export const parseValue = str => {
const len = str.length;
const unit = str.substring(len - 2, len);
if (!isNaN(parseInt(unit))) {
return parseInt(str);
}
let val = parseInt(str.substring(0, len - 2));
if (isNaN(val)) {
return str;
}
switch (unit) {
case 'Mi':
return val;
case 'Gi':
return val * 1024;
default:
console.log(`Unit (${unit}) is not found. Please contact DevOps team.`);
}
return val;
};
const isString = value => typeof value === 'string';
const isUndefined = value => value === undefined;
const findInArray = (limit, value) => {
const valueSet = new Set(value);
for (const requestValues of limit.values) {
if (valueSet.has(requestValues)) return true;
}
return false;
};
const findSingleValue = (limit, value) => {
for (const requestValues of limit.values) {
if (isUndefined(value) || requestValues === value) return true;
}
return false;
};
const isWeightGreater = (
limit: { valuesWithWeights?: ValueWithWeight[] },
value: string,
previousValue: string,
): boolean => {
if (!limit.valuesWithWeights) return false;
const weightsMap = new Map<string, number>();
limit.valuesWithWeights.forEach(({ type, weight }) => {
weightsMap.set(type, weight);
});
const currentWeight = weightsMap.get(value);
const previousWeight = weightsMap.get(previousValue);
return (
currentWeight !== undefined && (previousWeight === undefined || currentWeight > previousWeight)
);
};
const isChangeRequestRequired = (limit, value, previousValue, manifestEnv) => {
if (value === undefined) return false;
const parsedValue = isString(value) ? parseValue(value) : value;
const previousParsedValue = isString(previousValue) ? parseValue(previousValue) : previousValue;
const hasUpperBound = Object.prototype.hasOwnProperty.call(limit, 'upperBound');
const hasLowerBound = Object.prototype.hasOwnProperty.call(limit, 'lowerBound');
if (
hasUpperBound &&
((previousParsedValue >= limit.upperBound &&
parsedValue >= limit.upperBound &&
parsedValue <= previousParsedValue) ||
(previousValue >= limit.upperBound && value >= limit.upperBound && value <= previousValue))
) {
return false;
}
if (
hasLowerBound &&
((previousParsedValue <= limit.lowerBound &&
parsedValue <= limit.lowerBound &&
parsedValue >= previousParsedValue) ||
(previousValue <= limit.lowerBound && value <= limit.lowerBound && value >= previousValue))
) {
return false;
}
if (hasUpperBound && parsedValue >= limit.upperBound) return true;
if (hasLowerBound && parsedValue <= limit.lowerBound) return true;
if (limit.hasOwnProperty('values')) {
if (Array.isArray(value)) {
if (findInArray(limit, value)) {
return true;
}
} else {
if (findSingleValue(limit, value)) return true;
}
}
if (
Object.prototype.hasOwnProperty.call(limit, 'valuesWithWeights') &&
isWeightGreater(limit, value, previousValue)
) {
return true;
}
if (limit.hasOwnProperty('object')) {
for (const json of limit.object) {
const jsonObj = JSON.parse(json);
const flag = _.some([value], jsonObj);
if (flag) return true;
}
return false;
}
if (limit.hasOwnProperty('environments')) {
if (previousValue !== undefined) {
return false;
}
for (const blacklistedEnv of limit.environments) {
if (blacklistedEnv === manifestEnv) return true;
}
}
if (limit.hasOwnProperty('textDiff')) {
if (limit['textDiff'] === true) return !_.isEqual(value, previousValue);
}
return false;
};
const getOp = currentPreManifestObj => (isUndefined(currentPreManifestObj) ? 'add' : 'replace');
const safeMapAccess = (preManifestObject, key) => {
if (isUndefined(preManifestObject)) return undefined;
return preManifestObject[key];
};
const safeArrayAccess = (array, identifier: number | string) => {
if (!Array.isArray(array)) {
return undefined;
}
if (typeof identifier === 'number') {
return array[identifier];
} else if (typeof identifier === 'string') {
return array?.find(obj => obj.hashedIdentifier == identifier);
}
return undefined;
};
const getBreachedLimits = (
limitObject,
manifestObject,
preManifestObject,
replaceWithPreviousValue,
manifestEnv,
key,
limitPath = '',
manifestPath = '',
) => {
let breaches: any[] = [];
const currentLimitObj = limitObject[key];
const currentManifestObj = manifestObject[key];
const currentPreManifestObj = safeMapAccess(preManifestObject, key);
if (isTarget(currentLimitObj)) {
if (currentLimitObj.hasOwnProperty('forEach') && currentManifestObj?.length !== undefined) {
for (let i = 0; i < currentManifestObj.length; i++) {
const obj = currentManifestObj[i];
const hasHashedIdentifier = currentLimitObj['hasHashedIdentifier'] == true;
const isBreach =
hasHashedIdentifier ||
isChangeRequestRequired(
currentLimitObj['forEach'],
obj,
currentPreManifestObj,
manifestEnv,
);
const path = getBreachPath(manifestPath, key, i, hasHashedIdentifier);
console.log('path', path);
const identifier = hasHashedIdentifier ? getHashedIdentifier(obj, key) : i;
console.log('identifier', identifier);
if (isBreach && !_.isEqual(obj, safeArrayAccess(currentPreManifestObj, identifier))) {
const diffObj = {
op: getOp(currentPreManifestObj),
path: path,
limitPath: `${limitPath}/${key}`,
value: obj,
};
if (replaceWithPreviousValue) {
manifestObject[key] = safeMapAccess(preManifestObject, key);
}
breaches = breaches.concat(diffObj);
}
}
}
const isBreach = isChangeRequestRequired(
currentLimitObj,
currentManifestObj,
currentPreManifestObj,
manifestEnv,
);
const path = `${manifestPath}/${key}`;
if (isBreach && currentManifestObj !== currentPreManifestObj) {
console.log(`${path} is changed`);
const obj = {
op: getOp(currentPreManifestObj),
path: path,
limitPath: `${limitPath}/${key}`,
value: currentManifestObj,
};
if (replaceWithPreviousValue) {
manifestObject[key] = safeMapAccess(preManifestObject, key);
}
breaches = breaches.concat(obj);
}
}
if (!isObject(currentLimitObj)) return breaches;
for (const nextKey in currentLimitObj) {
const nextManifestPath = `${manifestPath}/${key}`;
const nextLimitPath = `${limitPath}/${key}`;
if (isObject(currentManifestObj)) {
breaches = breaches.concat(
getBreachedLimits(
currentLimitObj,
currentManifestObj,
currentPreManifestObj,
replaceWithPreviousValue,
manifestEnv,
nextKey,
nextLimitPath,
nextManifestPath,
),
);
} else if (Array.isArray(currentManifestObj)) {
for (let i = 0; i < currentManifestObj.length; i++) {
const nextManifestArrayPath = `${nextManifestPath}/${i}`;
breaches = breaches.concat(
getBreachedLimits(
currentLimitObj,
currentManifestObj[i],
safeArrayAccess(currentPreManifestObj, i),
replaceWithPreviousValue,
manifestEnv,
nextKey,
nextLimitPath,
nextManifestArrayPath,
),
);
}
}
}
return breaches;
};
const handleCopyUrlToClipboard = (baseUrl: string, requestId: number): void => {
const urlToCopy = baseUrl + '/' + requestId;
navigator.clipboard
.writeText(urlToCopy)
.then(() => {
toast.info('URL copied to clipboard');
})
.catch(error => {
toast.error('Failed to copy URL to clipboard');
});
};
const getBreachedValues = (
limitObject,
manifestObject,
preManifestObject,
replaceWithPreviousValue = false,
) => {
let breaches: any[] = [];
for (const key in limitObject) {
breaches = breaches.concat(
getBreachedLimits(
limitObject,
manifestObject,
preManifestObject,
replaceWithPreviousValue,
manifestObject['environment'],
key,
),
);
}
console.log('Breached values:', breaches);
return breaches;
};
function setValueByPath(obj, path, value = undefined): void {
if (!_.isArray(path) || path.length === 0) {
console.error('Invalid path');
return;
}
const lastKey = path.pop();
const target = path.reduce((acc, key) => {
if (_.isArray(acc) && !isNaN(key)) {
return acc[parseInt(key, 10)];
}
if (acc && acc[key] !== undefined) {
return acc[key];
}
if (_.isString(key) && !acc[key]) {
acc[key] = {};
}
return acc[key];
}, obj);
if (_.isArray(target) && !isNaN(lastKey)) {
if (value === undefined) {
target.splice(parseInt(lastKey, 10), 1);
} else {
target[parseInt(lastKey, 10)] = value;
}
} else if (target !== undefined && !(_.isArray(target) && isNaN(lastKey))) {
target[lastKey] = value;
} else {
console.error('Property not found or cannot set value at path:', path);
}
}
export { getBreachedValues, isObject, handleCopyUrlToClipboard, setValueByPath };