354 lines
9.8 KiB
TypeScript
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 };
|