Skip to content

Instantly share code, notes, and snippets.

@tengla
Last active May 8, 2025 14:14
Show Gist options
  • Select an option

  • Save tengla/1c2cf5b40881e6a6d5463227a5527cbc to your computer and use it in GitHub Desktop.

Select an option

Save tengla/1c2cf5b40881e6a6d5463227a5527cbc to your computer and use it in GitHub Desktop.
Audit logging w decorators
import "reflect-metadata/lite";
import EventEmitter from "events";
import { container, inject, injectable } from "tsyringe";
const em = new EventEmitter();
em.on("audit", (data) => {
console.log(data);
// I can maybe write to a audit log here (db, stdout, or file?)...
});
function Audited<T extends (...args: any[]) => any>(
action: "read" | "create" | "update" | "delete",
entity: string,
getRecordId: (result: ReturnType<T>) => number
): MethodDecorator {
return function (
_: any,
propertyKey: string | symbol,
descriptor: any
): void {
const originalMethod = descriptor.value!;
descriptor.value = async function (...args: any[]) {
if (Object.prototype.hasOwnProperty.call(this, "authUserService")) {
const user = this.authUserService.getUser();
if (!user) {
throw new Error("User not authenticated");
}
const useCaseName = this.constructor.name;
const result = await originalMethod.apply(this, args);
const recordId = getRecordId(result);
em.emit("audit", {
action,
entity,
recordId,
useCaseName,
propertyKey,
authUser: user,
args
});
return result;
}
};
};
}
container.register("UserRepository", {
useValue: {
create: async (item: any) => {
// Simulate writing to a database
return {
id: Math.random().toString(36).slice(2),
...item,
};
}
}
});
container.register("AuthUserService", {
useValue: {
getUser() {
return {
id: 1,
name: "Jane Doe",
email: "[email protected]"
}
}
}
});
@injectable()
class CreateUserUseCase {
constructor(
@inject("UserRepository") private userRepository: any, // Simulating a repository
@inject("AuthUserService") private authUserService: { getUser(): any } // Simulating an auth service
) { }
@Audited("create", "user", (result) => result.id)
execute<T extends { name: string }>(item: T): T {
// Simulate writing to a database and returning the item with an id
return this.userRepository.create(item);
}
}
const useCase = container.resolve(CreateUserUseCase);
useCase.execute({ name: "John Doe", age: 29 });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment