-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
352 additions
and
5 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { ApplicationService } from '@adonisjs/core/types' | ||
import MorphMap from '../src/morph_map.js' | ||
import { LogManager } from '../src/logger.js' | ||
import ActivityLog from '../src/models/activity_log.js' | ||
|
||
declare module '@adonisjs/core/types' { | ||
export interface ContainerBindings { | ||
morphMap: MorphMap | ||
} | ||
} | ||
export default class ActivityLogProvider { | ||
constructor(protected app: ApplicationService) {} | ||
|
||
register() { | ||
this.app.container.singleton('morphMap', async () => { | ||
return new MorphMap() | ||
}) | ||
} | ||
|
||
async boot() { | ||
LogManager.setModelClass(ActivityLog) | ||
const map = await this.app.container.make('morphMap') | ||
LogManager.setMorphMap(map) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import app from '@adonisjs/core/services/app' | ||
|
||
export function MorphMap(param: string) { | ||
return function <T extends { new (...args: any[]): {} }>(target: T) { | ||
const service = async function () { | ||
var result = await app.container.make('morphMap') | ||
result.set(param, target) | ||
return param | ||
} | ||
|
||
target.prototype.__morphMapName = service() | ||
target.prototype.__morphMapName = param | ||
} | ||
} | ||
|
||
export function getClassPath<T extends { new (...args: any[]): {} }>(clazz: T): string { | ||
const morphMapName = clazz.prototype.__morphMapName | ||
if (!morphMapName) { | ||
throw new Error('morph map name not specified') | ||
} | ||
|
||
return morphMapName | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import { BaseModel } from '@adonisjs/lucid/orm' | ||
import { | ||
LucidModel, | ||
ModelAdapterOptions, | ||
ModelQueryBuilderContract, | ||
} from '@adonisjs/lucid/types/model' | ||
import { LogModel, MorphInterface } from './types.js' | ||
|
||
export class LogManager { | ||
static _modelClass: typeof BaseModel | ||
|
||
private static _map: MorphInterface | ||
|
||
static setModelClass(modelClass: typeof BaseModel) { | ||
this._modelClass = modelClass | ||
} | ||
|
||
static setMorphMap(map: MorphInterface) { | ||
this._map = map | ||
} | ||
|
||
static morphMap() { | ||
return this._map | ||
} | ||
} | ||
|
||
export class ActivityBuilder { | ||
private _adapterOptions?: ModelAdapterOptions | ||
private _queryBuilder: ModelQueryBuilderContract<LucidModel, LucidModel> | null = null | ||
|
||
queryOptions(options?: ModelAdapterOptions) { | ||
this._adapterOptions = options | ||
return this | ||
} | ||
|
||
private _name: string | null = null | ||
private _description: string | null = null | ||
|
||
private _modelId: string | number | null = null | ||
private _modelType: string | null = null | ||
|
||
private _event: string | null = null | ||
|
||
private _entityId: string | number | null = null | ||
private _entityType: string | null = null | ||
|
||
private _properties: Object | null = null | ||
private _batchId: string | null = null | ||
|
||
name(name: string) { | ||
this._name = name | ||
return this | ||
} | ||
|
||
by(model: string, modelId: string | number): ActivityBuilder | ||
by(model: LogModel): ActivityBuilder | ||
by(model: LogModel | string, modelId?: string | number) { | ||
if (typeof model !== 'string') { | ||
this._modelId = model.getModelId() | ||
this._modelType = LogManager.morphMap().getAlias(model) | ||
} else if (typeof modelId === 'string' || typeof modelId === 'number') { | ||
this._modelId = modelId | ||
this._modelType = model | ||
} else { | ||
throw new Error('Invalid arguments provided') | ||
} | ||
return this | ||
} | ||
|
||
making(event: string) { | ||
this._event = event | ||
return this | ||
} | ||
|
||
on(entity: string, entityId: string | number): ActivityBuilder | ||
on(entity: LogModel): ActivityBuilder | ||
on(entity: LogModel | string, entityId?: string | number) { | ||
if (typeof entity !== 'string') { | ||
this._entityId = entity.getModelId() | ||
this._entityType = LogManager.morphMap().getAlias(entity) | ||
} else if (typeof entityId === 'string' || typeof entityId === 'number') { | ||
this._entityId = entityId | ||
this._entityType = entity | ||
} else { | ||
throw new Error('Invalid arguments provided') | ||
} | ||
return this | ||
} | ||
|
||
havingProperties(state: Object) { | ||
this._properties = state | ||
return this | ||
} | ||
|
||
withBatch(batchId: string) { | ||
this._batchId = batchId | ||
return this | ||
} | ||
|
||
log(message: string) { | ||
const state = this.state() | ||
state.description = message | ||
// Here you would typically save the log to the database or perform the logging operation | ||
console.log(state) | ||
} | ||
|
||
state() { | ||
return { | ||
name: this._name, | ||
model_id: this._modelId, | ||
model_type: this._modelType, | ||
event: this._event, | ||
entity_id: this._entityId, | ||
entity_type: this._entityType, | ||
properties: this._properties, | ||
batch_id: this._batchId, | ||
description: this._description, | ||
} | ||
} | ||
|
||
customQuery(callback: (query: ModelQueryBuilderContract<LucidModel, LucidModel>) => void) { | ||
callback(this.getBuilder()) | ||
return this | ||
} | ||
|
||
private getBuilder() { | ||
if (!this._queryBuilder) { | ||
this._queryBuilder = LogManager._modelClass.query(this._adapterOptions) | ||
} | ||
return this._queryBuilder | ||
} | ||
} | ||
|
||
export function activity() { | ||
return new ActivityBuilder() | ||
} | ||
|
||
// Example usage: | ||
activity() | ||
.by('User', 1) | ||
.making('edit') | ||
.on('Product', 2) | ||
.havingProperties({}) | ||
.customQuery((query) => { | ||
query.where('status', 'active') | ||
query.where('created_at', '>=', new Date('2023-01-01')) | ||
}) | ||
.log('Edited product') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { DateTime } from 'luxon' | ||
import { BaseModel, column, scope } from '@adonisjs/lucid/orm' | ||
|
||
export default class ActivityLog extends BaseModel { | ||
@column({ isPrimary: true }) | ||
declare id: number | ||
|
||
@column() | ||
declare name: string | ||
|
||
@column() | ||
declare description: string | ||
|
||
@column() | ||
declare model_type: string | ||
|
||
@column() | ||
declare model_id: string | ||
|
||
@column() | ||
declare event: string | ||
|
||
@column() | ||
declare entity_type: string | ||
|
||
@column() | ||
declare entity_id: string | ||
|
||
@column() | ||
declare properties: Object | ||
|
||
@column() | ||
declare batch_id: string | ||
|
||
@column.dateTime({ autoCreate: true }) | ||
declare createdAt: DateTime | ||
|
||
@column.dateTime({ autoCreate: true, autoUpdate: true }) | ||
declare updatedAt: DateTime | ||
|
||
static published = scope((query) => { | ||
query.where('publishedOn', '<=', DateTime.utc().toSQLDate()) | ||
}) | ||
} | ||
|
||
const a = ActivityLog.query() | ||
a.where('a', 4).withScopes((scopes) => scopes.published()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { MorphInterface, MorphMapInterface } from './types.js' | ||
|
||
export default class MorphMap implements MorphInterface { | ||
private _map: MorphMapInterface = {} | ||
|
||
private static _instance?: MorphMap | ||
|
||
static create() { | ||
if (this._instance) { | ||
return this._instance | ||
} | ||
|
||
return new MorphMap() | ||
} | ||
|
||
set(alias: string, target: any) { | ||
this._map[alias] = target | ||
} | ||
|
||
get(alias: string) { | ||
if (!(alias in this._map)) { | ||
throw new Error('morph map not found for ' + alias) | ||
} | ||
|
||
return this._map[alias] || null | ||
} | ||
|
||
has(alias: string) { | ||
return alias in this._map | ||
} | ||
|
||
hasTarget(target: any) { | ||
const keys = Object.keys(this._map) | ||
for (const key of keys) { | ||
if (this._map[key] === target) { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
getAlias(target: any) { | ||
const keys = Object.keys(this._map) | ||
for (const key of keys) { | ||
if (target instanceof this._map[key] || target === this._map[key]) { | ||
return key | ||
} | ||
} | ||
|
||
throw new Error('Target not found') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { LucidModel } from '@adonisjs/lucid/types/model' | ||
|
||
export interface LogModelInterface { | ||
getModelId(): string | number | ||
} | ||
|
||
// export type LogModel = InstanceType<LucidModel> & LogModelInterface | ||
export interface LogModel extends LucidModel, LogModelInterface {} | ||
|
||
export interface MorphMapInterface { | ||
[key: string]: any | ||
} | ||
|
||
export interface MorphInterface { | ||
set(alias: string, target: any): void | ||
get(alias: string): any | ||
has(alias: string): boolean | ||
hasTarget(target: any): boolean | ||
getAlias(target: any): string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{{{ | ||
exports({ | ||
to: app.makePath('database', 'migrations', prefix + '_create_role_permissions_table.ts') | ||
}) | ||
}}} | ||
import { BaseSchema } from '@adonisjs/lucid/schema' | ||
|
||
export default class extends BaseSchema { | ||
protected tableName = 'activity_logs' | ||
|
||
async up() { | ||
this.schema.createTable(this.tableName, (table) => { | ||
table.increments('id') | ||
|
||
table.string('name').nullable().index() | ||
table.text('description') | ||
table.string('model_type').nullable() | ||
table.string('model_id').nullable() | ||
table.string('event').nullable() | ||
table.string('entity_type').nullable() | ||
table.string('entity_id').nullable() | ||
table.json('properties').nullable() | ||
table.string('batch_id').nullable().index() | ||
|
||
table.index(['model_type', 'model_id']) | ||
table.index(['entity_type', 'entity_id']) | ||
|
||
table.timestamp('created_at') | ||
table.timestamp('updated_at') | ||
}) | ||
} | ||
|
||
async down() { | ||
this.schema.dropTable(this.tableName) | ||
} | ||
} |