diff --git a/apps/api/src/helper/object.helper.ts b/apps/api/src/helper/object.helper.ts index 2f389352f..b24ee42ee 100644 --- a/apps/api/src/helper/object.helper.ts +++ b/apps/api/src/helper/object.helper.ts @@ -27,3 +27,40 @@ export function nullifyValuesInObjects(aObjects: T[], keys: string[]): T[] { return nullifyValuesInObject(object, keys); }); } + +export function redactAttributes({ + object, + options +}: { + object: any; + options: { attribute: string; valueMap: { [key: string]: any } }[]; +}): any { + if (!object || !options || !options.length) { + return object; + } + + const redactedObject = cloneDeep(object); + + for (const option of options) { + if (redactedObject.hasOwnProperty(option.attribute)) { + redactedObject[option.attribute] = + option.valueMap[redactedObject[option.attribute]] ?? + option.valueMap['*'] ?? + redactedObject[option.attribute]; + } 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 (typeof redactedObject[property] === 'object') { + // Recursively call the function on the nested object + redactedObject[property] = redactAttributes({ + options, + object: redactedObject[property] + }); + } + } + } + } + + return redactedObject; +} diff --git a/apps/api/src/interceptors/transform-data-source-in-response.interceptor.ts b/apps/api/src/interceptors/transform-data-source-in-response.interceptor.ts index 4b80038f5..d02c26fca 100644 --- a/apps/api/src/interceptors/transform-data-source-in-response.interceptor.ts +++ b/apps/api/src/interceptors/transform-data-source-in-response.interceptor.ts @@ -1,3 +1,4 @@ +import { redactAttributes } from '@ghostfolio/api/helper/object.helper'; import { encodeDataSource } from '@ghostfolio/common/helper'; import { CallHandler, @@ -5,7 +6,7 @@ import { Injectable, NestInterceptor } from '@nestjs/common'; -import { isArray } from 'lodash'; +import { DataSource } from '@prisma/client'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -28,63 +29,23 @@ export class TransformDataSourceInResponseInterceptor if ( this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION') === true ) { - if (data.activities) { - data.activities.map((activity) => { - activity.SymbolProfile.dataSource = encodeDataSource( - activity.SymbolProfile.dataSource - ); - return activity; - }); - } - - if (isArray(data.benchmarks)) { - data.benchmarks.map((benchmark) => { - benchmark.dataSource = encodeDataSource(benchmark.dataSource); - return benchmark; - }); - } - - if (data.dataSource) { - data.dataSource = encodeDataSource(data.dataSource); - } - - if (data.errors) { - for (const error of data.errors) { - if (error.dataSource) { - error.dataSource = encodeDataSource(error.dataSource); - } - } - } - - if (data.holdings) { - for (const symbol of Object.keys(data.holdings)) { - if (data.holdings[symbol].dataSource) { - data.holdings[symbol].dataSource = encodeDataSource( - data.holdings[symbol].dataSource - ); + data = redactAttributes({ + options: [ + { + attribute: 'dataSource', + valueMap: Object.keys(DataSource).reduce( + (valueMap, dataSource) => { + valueMap[dataSource] = encodeDataSource( + DataSource[dataSource] + ); + return valueMap; + }, + {} + ) } - } - } - - if (data.items) { - data.items.map((item) => { - item.dataSource = encodeDataSource(item.dataSource); - return item; - }); - } - - if (data.positions) { - data.positions.map((position) => { - position.dataSource = encodeDataSource(position.dataSource); - return position; - }); - } - - if (data.SymbolProfile) { - data.SymbolProfile.dataSource = encodeDataSource( - data.SymbolProfile.dataSource - ); - } + ], + object: data + }); } return data;