diff --git a/apps/api/src/helper/object.helper.ts b/apps/api/src/helper/object.helper.ts index a5854e9d9..15d8e01a5 100644 --- a/apps/api/src/helper/object.helper.ts +++ b/apps/api/src/helper/object.helper.ts @@ -1,6 +1,8 @@ import { Big } from 'big.js'; import { cloneDeep, isArray, isObject } from 'lodash'; +import { RedactylNullSupported } from './redactyle.helper'; + export function hasNotDefinedValuesInObject(aObject: Object): boolean { for (const key in aObject) { if (aObject[key] === null || aObject[key] === undefined) { @@ -44,47 +46,67 @@ export function redactAttributes({ return object; } - // Create deep clone - const redactedObject = isFirstRun - ? JSON.parse(JSON.stringify(object)) - : object; + let redactedObject = isFirstRun ? cloneDeep(object) : object; for (const option of options) { - if (redactedObject.hasOwnProperty(option.attribute)) { - if (option.valueMap['*'] || option.valueMap['*'] === null) { - redactedObject[option.attribute] = option.valueMap['*']; - } else if (option.valueMap[redactedObject[option.attribute]]) { - redactedObject[option.attribute] = - option.valueMap[redactedObject[option.attribute]]; - } + const { attribute, valueMap } = option; + + const redactyl = new RedactylNullSupported({ properties: [] }); + redactyl.addProperties([attribute]); + + // Handle different value mapping scenarios + if ('*' in valueMap) { + // Wildcard replacement - replace all instances of this attribute + redactyl.setText(valueMap['*']); + redactedObject = redactyl.redact(redactedObject); } else { - // If the attribute is not present on the current object, - // check if it exists on any nested objects - for (const property in redactedObject) { - if (isArray(redactedObject[property])) { - redactedObject[property] = redactedObject[property].map( - (currentObject) => { - return redactAttributes({ - options, - isFirstRun: false, - object: currentObject - }); - } - ); - } else if ( - isObject(redactedObject[property]) && - !(redactedObject[property] instanceof Big) - ) { - // Recursively call the function on the nested object - redactedObject[property] = redactAttributes({ - options, - isFirstRun: false, - object: redactedObject[property] - }); - } - } + // Specific value mapping - need to handle this differently + // We'll use a custom approach for specific value mappings + redactedObject = redactSpecificValues( + redactedObject, + attribute, + valueMap + ); } } return redactedObject; } + +// Helper function to handle specific value mappings (non-wildcard) +function redactSpecificValues( + obj: any, + attribute: string, + valueMap: { [key: string]: any } +): any { + if (!obj || typeof obj !== 'object') { + return obj; + } + + if (isArray(obj)) { + return obj.map((item) => redactSpecificValues(item, attribute, valueMap)); + } + + const result = { ...obj }; + + // Check if current level has the attribute and if its value should be replaced + if (attribute in result) { + const currentValue = result[attribute]; + if (currentValue in valueMap) { + result[attribute] = valueMap[currentValue]; + } + } + + // Recursively process nested objects + for (const key in result) { + if (isObject(result[key]) && !(result[key] instanceof Big)) { + result[key] = redactSpecificValues(result[key], attribute, valueMap); + } else if (isArray(result[key])) { + result[key] = result[key].map((item: any) => + isObject(item) ? redactSpecificValues(item, attribute, valueMap) : item + ); + } + } + + return result; +} diff --git a/apps/api/src/helper/redactyle.helper.ts b/apps/api/src/helper/redactyle.helper.ts new file mode 100644 index 000000000..f0d1765ae --- /dev/null +++ b/apps/api/src/helper/redactyle.helper.ts @@ -0,0 +1,8 @@ +import Redactyl = require('redactyl.js'); +export class RedactylNullSupported extends Redactyl { + text: string | null = null; + setText(text) { + this.text = text; + return this; + } +} diff --git a/package-lock.json b/package-lock.json index 6722459a3..5493b0470 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,6 +87,7 @@ "passport-google-oauth20": "2.0.0", "passport-headerapikey": "1.2.2", "passport-jwt": "4.0.1", + "redactyl.js": "^1.6.0", "reflect-metadata": "0.2.2", "rxjs": "7.8.1", "stripe": "18.2.1", @@ -30036,6 +30037,11 @@ "node": ">= 0.10" } }, + "node_modules/redactyl.js": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/redactyl.js/-/redactyl.js-1.6.0.tgz", + "integrity": "sha512-K5HZ+fVPfR7PWABroIUb+of2HVDM/VNcHYCTB1ZTqXVpCAwpj++c0eSHg0oJcDIBaNAe59DeKHzJ6rC7p91B5g==" + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", diff --git a/package.json b/package.json index bc10005cb..b9449f733 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,7 @@ "passport-google-oauth20": "2.0.0", "passport-headerapikey": "1.2.2", "passport-jwt": "4.0.1", + "redactyl.js": "^1.6.0", "reflect-metadata": "0.2.2", "rxjs": "7.8.1", "stripe": "18.2.1",