Skip to content

Commit

Permalink
Merge 86bc4ea into dc96169
Browse files Browse the repository at this point in the history
  • Loading branch information
nikksan authored Mar 30, 2021
2 parents dc96169 + 86bc4ea commit 2cbe23b
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 184 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@luckbox/logger-factory",
"version": "3.2.0",
"version": "4.0.0",
"description": "Easy to use logger with several levels of logging as well as different adapters that can be used separately or in combinations",
"author": "Luckbox",
"license": "ISC",
Expand Down
104 changes: 0 additions & 104 deletions src/Obfuscator.ts

This file was deleted.

97 changes: 97 additions & 0 deletions src/data-tagging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import lodash from 'lodash';

export enum Tag {
PII = 'PII',
SECRET = 'SECRET'
}

type PlainObject = Record<string, unknown>;

export function tagString(value: string, tag: Tag): string {
return `[${tag}]${value}[/${tag}]`;
}

export function tagObject<T extends PlainObject | Error>(object: T, tagSettings: Array<[string, Tag]>): T {
if (isError(object)) {
return tagError(object, tagSettings);
} else if (isPlainObject(object)) {
return tagPlainObject(object, tagSettings);
}

throw new Error('Input must be plain object or a class inheriting from Error');
}

function tagPlainObject<T extends PlainObject>(plainObject: T, tagSettings: Array<[string, Tag]>): T {
const clonedObj = clone(plainObject);
const pathToTagMap = constructPathToTagMap(tagSettings);

const allPaths = collectPaths(plainObject);
for (const path of allPaths) {
const tag = pathToTagMap.get(path);
if (!tag) {
continue;
}

const rawValue = lodash.get(clonedObj, path);
if (typeof rawValue === 'string') {
lodash.set(clonedObj, path, tagString(rawValue, tag));
}
}

return clonedObj;
}

function tagError<T extends Error>(err: T, tagSettings: Array<[string, Tag]>): T {
const clonedErr = new Error(err.message);
Object.setPrototypeOf(clonedErr, Object.getPrototypeOf(err));

const dataToAssign = {};
for (const prop of Object.getOwnPropertyNames(err)) {
dataToAssign[prop] = clone(err[prop]);
}

Object.assign(clonedErr, tagPlainObject(dataToAssign, tagSettings));

return clonedErr as T;
}

function collectPaths(input: any, currentPath?: string) {
const paths: Array<string> = [];

if (lodash.isPlainObject(input)) {
for (const key in input) {
const fullPath = buildPath(key, currentPath);
const value = input[key];

paths.push(fullPath, ...collectPaths(value).map((nestedPath) => buildPath(nestedPath, fullPath)));
}
}

return paths;
}

function buildPath(propPath: string, basePath?: string) {
return basePath === undefined ? String(propPath) : `${basePath}.${propPath}`;
}

function isPlainObject(value: unknown): value is PlainObject {
return lodash.isPlainObject(value);
}

function isError(value: unknown): value is Error {
return value instanceof Error;
}

function constructPathToTagMap(tagSettings: Array<[string, Tag]>) {
const pathToTagMap = new Map();
for (const [path, tag] of tagSettings) {
pathToTagMap.set(path, tag);
}

return pathToTagMap;
}

function clone<T extends any>(value: T): T {
return lodash.cloneDeep(value);
}

17 changes: 3 additions & 14 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
import { LoggerFactory, Adapters, LogLevels, ConsoleAdapterSettings, SentryAdapterSettings } from './LoggerFactory';
import { Logger } from './Logger';
import { Obfuscator, Tag } from './Obfuscator';

export {
LoggerFactory,
Adapters,
LogLevels,
Logger,
ConsoleAdapterSettings,
SentryAdapterSettings,
Obfuscator,
Tag as ObfuscatorTag,
};
export * from './LoggerFactory';
export * from './Logger';
export * from './data-tagging';
64 changes: 0 additions & 64 deletions src/tests/unit-tests/Obfuscator.test.ts

This file was deleted.

66 changes: 66 additions & 0 deletions src/tests/unit-tests/data-tagging.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
tagString,
tagObject,
Tag,
} from '../../index';

describe('Data Tagging', () => {
describe('tagString', () => {
it('should wrap the provided tag around the string that needs to be tagd', () => {
expect(tagString('string', Tag.PII)).toEqual('[PII]string[/PII]');
});
});

describe('tagObject', () => {
it('should wrap the provided tag around root-level elements in object', () => {
const originalObject = { name: 'Pencho' };
const taggedObject = { name: '[PII]Pencho[/PII]' };
expect(tagObject(originalObject, [['name', Tag.PII]])).toEqual(taggedObject);
});

it('should wrap the provided tag around nested elements in object', () => {
const originalObject = { id: 1, data: { name: 'Gosho', email: 'email@example.com' } };
const taggedObject = { id: 1, data: { name: '[PII]Gosho[/PII]', email: '[PII]email@example.com[/PII]' } };
expect(tagObject(originalObject, [['data.name', Tag.PII], ['data.email', Tag.PII]])).toEqual(taggedObject);
});

it('should NOT wrap the provided tag around elements that are not specified for taging in object', () => {
const originalObject = { favouriteColor: 'red', nested: { field: 'value' } };
expect(tagObject(originalObject, [['name', Tag.PII]])).toEqual(originalObject);
});

it('should return a copy of the error and not modify the original', () => {
const originalError = new Error();
const taggedError = tagObject(originalError, [['bar', Tag.PII]]);
expect(taggedError).not.toBe(originalError);
});

it('should preserve the prototype, name, message and stack of the error', () => {
class CustomError extends Error {}
const originalError = new CustomError();
const taggedError = tagObject(originalError, [['bar', Tag.PII]]);

expect(taggedError).toBeInstanceOf(CustomError);
expect(taggedError.name).toEqual(originalError.name);
expect(taggedError.message).toEqual(originalError.message);
expect(taggedError.stack).toEqual(originalError.stack);
});

it('should tag error specific props', () => {
class CustomError extends Error {
bar = 'foo'
foo = {
test: 'test',
}
}
const originalError = new CustomError();
const taggedError = tagObject(originalError, [
['bar', Tag.PII],
['foo.test', Tag.PII],
]);

expect(taggedError.bar).toEqual('[PII]foo[/PII]');
expect(taggedError.foo.test).toEqual('[PII]test[/PII]');
});
});
});

0 comments on commit 2cbe23b

Please sign in to comment.