Skip to content

Commit

Permalink
feat(logger): add a decorator to log a class method (#798)
Browse files Browse the repository at this point in the history
  • Loading branch information
rfprod authored Dec 2, 2023
1 parent a7edb46 commit ac17d75
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 14 deletions.
3 changes: 2 additions & 1 deletion .eslint/.eslintrc.module-boundaries.client.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ exports.constraints = [
'scope:client-testing-unit',
'scope:client-service-worker',
'scope:client-util',
'scope:client-util-decorators',
'scope:client-util-ngrx',
'scope:client-util-security'
'scope:client-util-security',
],
sourceTag: 'scope:documentation',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NestedTreeControl } from '@angular/cdk/tree';
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { MatTreeNestedDataSource } from '@angular/material/tree';
import { logMethod } from '@app/client-util-decorators';
import { Store } from '@ngrx/store';

import { DOCUMENTATION_ENVIRONMENT, IDocumentationEnvironment } from '../../interfaces/environment.interface';
Expand Down Expand Up @@ -28,7 +29,8 @@ export class AppDocMarkdownReferenceTreeComponent {

public readonly dataSource = new MatTreeNestedDataSource<IMarkdownReferenceNode>();

private readonly treeData = () => {
@logMethod()
private treeData() {
const mdFilePaths = [
'/README.md', // the root readme in not present in the autogenerated array and should be added here
...this.env.mdFilePaths,
Expand All @@ -42,7 +44,7 @@ export class AppDocMarkdownReferenceTreeComponent {
});
this.showReadme(`md${mdFilePaths[0]}`);
return treeNodes;
};
}

constructor(
private readonly store: Store<IMdFilesState>,
Expand All @@ -51,8 +53,10 @@ export class AppDocMarkdownReferenceTreeComponent {
this.dataSource.data = this.treeData();
}

public readonly hasChild = (index: number, node: IMarkdownReferenceNode) =>
typeof node.children !== 'undefined' && node.children.length > 0;
@logMethod()
public hasChild(index: number, node: IMarkdownReferenceNode) {
return typeof node.children !== 'undefined' && node.children.length > 0;
}

public showReadme(filePath: string): void {
this.store.dispatch(mdFilesAction.showReadme({ filePath }));
Expand Down
10 changes: 9 additions & 1 deletion libs/client-util-decorators/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
{
"extends": ["../../.eslintrc.js", "../../.eslintrc.angular.js"],
"ignorePatterns": ["!**/*"]
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["log-method.decorator.ts"],
"rules": {
"no-console": "off"
}
}
]
}
2 changes: 1 addition & 1 deletion libs/client-util-decorators/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './lib/decorators';
export * from './lib';
1 change: 1 addition & 0 deletions libs/client-util-decorators/src/lib/decorators/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './log-method/log-method.decorator';
export * from './track-changes/track-changes.decorator';
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';

import { logMethod } from './log-method.decorator';

@Component({
selector: 'app-log-method-testing-component',
template: `<div></div>`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
class AppLogMethodTestingComponent {
@logMethod(true)
public logEnabled(options: { test: string }) {
return options;
}

@logMethod()
public logEnabledImplicitly(options: { test: string }) {
return options;
}

@logMethod(false)
public logDisabled(options: { test: string }) {
return options;
}
}

describe('trackChanges', () => {
const testBedConfig: TestModuleMetadata = {
declarations: [AppLogMethodTestingComponent],
};

let fixture: ComponentFixture<AppLogMethodTestingComponent>;
let component: AppLogMethodTestingComponent;

let spy: jest.SpyInstance;

beforeEach(async () => {
await TestBed.configureTestingModule(testBedConfig).compileComponents();
fixture = TestBed.createComponent(AppLogMethodTestingComponent);
component = fixture.componentInstance;
spy = jest.spyOn(console, 'log');
});

afterEach(() => {
jest.resetAllMocks();
});

it('should log changes if attached and enabled explicitly', () => {
component.logEnabled({ test: 'test' });
expect(spy).toHaveBeenCalledTimes(1);
});

it('should log changes if attached and enabled implicitly', () => {
component.logEnabledImplicitly({ test: 'test' });
expect(spy).toHaveBeenCalledTimes(1);
});

it('should not log changes if attached and disabled', () => {
component.logDisabled({ test: 'test' });
expect(spy).not.toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @title The log method decorator.
* @description This log is mainly useful for development and should not be used in production.
* @param enable Indicates whether to enable the logger or not.
* @param propertyKey The class property key.
*/
export function logMethod<TargetClass extends object = Record<string, unknown>>(enable = true) {
return function (targetClass: TargetClass, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod: (...args: any[]) => unknown = descriptor.value;
descriptor.value = function (...args: any[]) {
const className = targetClass.constructor.name;
const result = originalMethod.apply(this, args);
if (enable) {
console.log(`Executing: ${className}.${propertyKey}`, `\nArguments: ${JSON.stringify(args)}`, `\nResult: ${result}`);
}
return result;
};
return descriptor;
};
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChange, SimpleChanges } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { ComponentFixture, TestBed, TestModuleMetadata } from '@angular/core/testing';

import { trackChanges } from './track-changes.decorator';

interface IInputChanges extends SimpleChanges {
input: SimpleChange;
}

const controlSuffix = 'Changes';

@Component({
Expand All @@ -21,7 +17,7 @@ class AppTrackChangesTestingComponent implements OnChanges {

@trackChanges<AppTrackChangesTestingComponent, string | undefined>('noinput', 'inputChangeHandler')
@trackChanges<AppTrackChangesTestingComponent, string | undefined>('input', 'inputChangeHandler')
public ngOnChanges(changes: IInputChanges) {
public ngOnChanges(changes: SimpleChanges) {
return changes;
}

Expand Down
1 change: 1 addition & 0 deletions libs/client-util-decorators/src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './decorators';
10 changes: 9 additions & 1 deletion tools/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
{
"extends": ["../.eslintrc.js"],
"ignorePatterns": ["!**/*"]
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*logger*.ts"],
"rules": {
"no-console": "off"
}
}
]
}

0 comments on commit ac17d75

Please sign in to comment.