Skip to content

Commit

Permalink
feat: supports mapping evenType based on RealEventData
Browse files Browse the repository at this point in the history
  • Loading branch information
tclxshunquan-wang committed Aug 23, 2024
1 parent 412a683 commit ae77ae1
Show file tree
Hide file tree
Showing 17 changed files with 844 additions and 169 deletions.
137 changes: 107 additions & 30 deletions src/adapter/adapter-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
AdapterBeforeFunction,
AdapterReportData,
AdapterTransformFunction,
CheckUndefined,
TrackAdapter,
} from '../types/types-adapter.js';
import { TrackAdapterOptions, TrackContext } from '../types/types-create.js';
Expand All @@ -13,26 +14,37 @@ import { TrackEventDataBase } from '../types/types-track.js';
export abstract class BaseAdapter<
Context extends TrackContext<any>,
EventData extends TrackEventDataBase,
AdapterOptions extends TrackAdapterOptions<Context, EventData>,
> implements TrackAdapter<Context, EventData, AdapterOptions>
AdapterOptions extends TrackAdapterOptions<Context, EventData, RealEventData>,
RealEventData extends TrackEventDataBase = EventData,
> implements TrackAdapter<Context, EventData, AdapterOptions, RealEventData>
{
private setupHook?: AdapterOptions['setup'];
private beforeHook?: AdapterBeforeFunction<Context, EventData>;
transformHookMap: {
[K in keyof EventData]?: AdapterTransformFunction<Context, K, EventData>;
[K in keyof EventData]?: {
realEventType: keyof RealEventData | keyof EventData;
execute: AdapterTransformFunction<Context, K, EventData, RealEventData>;
};
} = {};

private afterHook?: AdapterAfterFunction<Context, EventData>;
private afterHook?: AdapterAfterFunction<Context, RealEventData, EventData>;

abstract isTrackable<EventType extends keyof EventData>(
abstract isTrackable<
EventType extends CheckUndefined<RealEventData, EventData>,
>(
ctx: Context,
eventType: EventType,
eventData: EventData[EventType]
eventType: CheckUndefined<RealEventData, EventData>,
reportData?:
| AdapterReportData<RealEventData, EventData, EventType>
| Awaited<AdapterReportData<RealEventData, EventData, EventType>>
): boolean | Promise<boolean>;

protected report(
protected report<EventType extends CheckUndefined<RealEventData, EventData>>(
ctx: Context,
reportData: AdapterReportData,
eventType: CheckUndefined<RealEventData, EventData>,
reportData?:
| AdapterReportData<RealEventData, EventData, EventType>
| Awaited<AdapterReportData<RealEventData, EventData, EventType>>,
setupData?: Required<AdapterOptions>['setup'] extends (...args: any) => any
? Awaited<ReturnType<Required<AdapterOptions>['setup']>>
: undefined
Expand All @@ -48,15 +60,34 @@ export abstract class BaseAdapter<
this.beforeHook = fun;
}

public _mountAfterHook(fun: AdapterAfterFunction<Context, EventData>): void {
public _mountAfterHook(
fun: AdapterAfterFunction<Context, RealEventData, EventData>
): void {
this.afterHook = fun;
}

public _mountTransformHook<EventType extends keyof EventData>(
public _mountTransformHook<
EventType extends keyof EventData | [keyof EventData, keyof RealEventData],
>(
eventType: EventType,
fun: AdapterTransformFunction<Context, EventType, EventData>
fun: AdapterTransformFunction<
Context,
keyof EventData,
EventData,
RealEventData
>
) {
this.transformHookMap[eventType] = fun;
if (typeof eventType === 'string') {
this.transformHookMap[eventType] = {
realEventType: eventType,
execute: fun,
};
} else if (Array.isArray(eventType)) {
this.transformHookMap[eventType[0]] = {
realEventType: eventType[1],
execute: fun,
};
}
}

private executeTransform = async <EventType extends keyof EventData>(
Expand All @@ -66,45 +97,83 @@ export abstract class BaseAdapter<
) => {
if (Object.keys(this.transformHookMap).length < 1) {
ctx.logger?.warn('Adapter transform hook is not defined');
return eventData;
return {
reportData: eventData,
realEventType: eventType as unknown as CheckUndefined<
RealEventData,
EventData
>,
};
}
const transformHook = this.transformHookMap[eventType];

if (!transformHook) {
return eventData;
return {
reportData: eventData,
realEventType: eventType as unknown as CheckUndefined<
RealEventData,
EventData
>,
};
}
const result = await executeFunction(
transformHook as AdapterTransformFunction<Context, EventType, EventData>,

const reportData = await executeFunction(
transformHook.execute,
ctx,
eventType,
eventData
);
return result;

return {
reportData,
realEventType: transformHook.realEventType as unknown as CheckUndefined<
RealEventData,
EventData
>,
};
};

private executeReport = async <ReportData, EventType extends keyof EventData>(
private executeReport = async <
EventType extends CheckUndefined<RealEventData, EventData>,
>(
adapterName: string,
ctx: Context,
eventType: EventType,
eventData: EventData[EventType],
reportData: ReportData
): Promise<ReportData> => {
realEventType: CheckUndefined<RealEventData, EventData>,
eventData: EventData[keyof EventData],
reportData?:
| AdapterReportData<RealEventData, EventData, EventType>
| Awaited<AdapterReportData<RealEventData, EventData, EventType>>
) => {
const isTrackable = await executeFunction(
this.isTrackable,
ctx,
realEventType,
reportData
);

if (!isTrackable) {
ctx.logger?.warn(`Adapter is not trackable: ${adapterName}`);
return;
}

let setupResult;
if (this.setupHook) {
setupResult = await this.setupHook(ctx, eventType, eventData);
setupResult = await this.setupHook(ctx, realEventType, eventData);
}
await this.report(ctx, reportData, setupResult);
return reportData;
await this.report(ctx, realEventType, reportData, setupResult);
};

/**
* Tracks an event.
*
* @param adapterName - The adapter name.
* @param ctx - The context object.
* @param eventType - The type of the event.
* @param eventData - The data associated with the event.
* @returns A promise that resolves when the tracking is complete.
*/
public async track<EventType extends keyof EventData>(
adapterName: string,
ctx: Context,
eventType: EventType,
eventData: EventData[EventType]
Expand All @@ -113,10 +182,18 @@ export abstract class BaseAdapter<
async () =>
await executeFunction(this.beforeHook, ctx, eventType, eventData),
async () => await this.executeTransform(ctx, eventType, eventData),
async (reportData) =>
await this.executeReport(ctx, eventType, eventData, reportData),
async (reportData) =>
await executeFunction(this.afterHook, ctx, eventType, reportData)
async ({ reportData, realEventType }) => {
await this.executeReport(
adapterName,
ctx,
realEventType,
eventData,
reportData
);
return { reportData, realEventType };
},
async ({ reportData, realEventType }) =>
await executeFunction(this.afterHook, ctx, realEventType, reportData)
)();
}
}
65 changes: 46 additions & 19 deletions src/adapter/adapter-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { UnionToTuple } from '../types/type-union-tuple.js';
import {
AdapterAfterFunction,
AdapterBeforeFunction,
AdapterReportData,
TrackAdapter,
TransformEventData,
TransformEventType,
TransformReturns,
} from '../types/types-adapter.js';
import { TrackAdapterOptions, TrackContext } from '../types/types-create.js';
import { TrackEventDataBase } from '../types/types-track.js';
Expand All @@ -18,11 +20,19 @@ import { TrackEventDataBase } from '../types/types-track.js';
export class AdapterBuilder<
Context extends TrackContext<any>,
EventData extends TrackEventDataBase,
AdapterOptions extends TrackAdapterOptions<Context, EventData>,
AdapterOptions extends TrackAdapterOptions<Context, EventData, RealEventData>,
RealEventData extends TrackEventDataBase = EventData,
> {
private adapter: TrackAdapter<Context, EventData, AdapterOptions>;
private adapter: TrackAdapter<
Context,
EventData,
AdapterOptions,
RealEventData
>;

constructor(_adapter: TrackAdapter<Context, EventData, AdapterOptions>) {
constructor(
_adapter: TrackAdapter<Context, EventData, AdapterOptions, RealEventData>
) {
this.adapter = _adapter;
}

Expand Down Expand Up @@ -84,52 +94,69 @@ export class AdapterBuilder<
}

private mountTransformHook = <
Key extends keyof LeftEventData,
Key extends
| keyof LeftEventData
| [keyof LeftEventData, keyof RealEventData],
LeftEventData = EventData,
>(
eventType: Key,
fun: (
ctx: Context,
eventType: Key,
eventData: LeftEventData[Key]
) => AdapterReportData | Promise<AdapterReportData>
eventType: TransformEventType<Key, RealEventData, LeftEventData>,
eventData: TransformEventData<Key, RealEventData, LeftEventData>
) => TransformReturns<Key, RealEventData, LeftEventData>
) => {
this.adapter._mountTransformHook(
eventType as keyof EventData,
fun as (...args: any[]) => AdapterReportData | Promise<AdapterReportData>
eventType as keyof EventData | [keyof EventData, keyof RealEventData],
fun as (
...args: any[]
) => TransformReturns<keyof EventData, RealEventData, EventData>
);

const transform = <
RightKey extends keyof RightEventData,
RightEventData = Omit<LeftEventData, Key>,
RightKey extends
| keyof RightEventData
| [keyof RightEventData, keyof RealEventData],
RightEventData = Key extends keyof LeftEventData
? Omit<LeftEventData, Key>
: Key extends [keyof LeftEventData, keyof RealEventData]
? Omit<LeftEventData, Key[0]>
: never,
>(
eventType: RightKey,
fun: (
ctx: Context,
eventType: RightKey,
eventData: RightEventData[RightKey]
) => AdapterReportData | Promise<AdapterReportData>
eventType: TransformEventType<RightKey, RealEventData, RightEventData>,
eventData: TransformEventData<RightKey, RealEventData, RightEventData>
) => TransformReturns<RightKey, RealEventData, RightEventData>
) => {
this.adapter._mountTransformHook(
eventType as keyof EventData,
eventType as keyof EventData | [keyof EventData, keyof RealEventData],
fun as (
...args: any[]
) => AdapterReportData | Promise<AdapterReportData>
) => TransformReturns<keyof EventData, RealEventData, EventData>
);
return this.mountTransformHook<RightKey, RightEventData>(eventType, fun);
};

const result = {
transform: transform,
...this.buildTransformChainer(),
};

return result as UnionToTuple<
Exclude<keyof LeftEventData, Key>
Exclude<
keyof LeftEventData,
TransformEventType<Key, RealEventData, LeftEventData>
>
>['length'] extends 0
? ReturnType<typeof this.buildTransformChainer>
: typeof result;
};

private mountAfterHook = (fun: AdapterAfterFunction<Context, EventData>) => {
private mountAfterHook = (
fun: AdapterAfterFunction<Context, RealEventData, EventData>
) => {
this.adapter._mountAfterHook(fun);
return this.buildAfterChainer();
};
Expand Down
14 changes: 9 additions & 5 deletions src/adapter/create-adapter-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ import { AdapterBuilder } from './adapter-builder.js';
export function createAdapterBuilder<
Context extends TrackContext<any>,
EventData extends TrackEventDataBase,
AdapterOptions extends TrackAdapterOptions<Context, EventData>,
>(adapter: TrackAdapter<Context, EventData, AdapterOptions>) {
AdapterOptions extends TrackAdapterOptions<Context, EventData, RealEventData>,
RealEventData extends TrackEventDataBase = EventData,
>(adapter: TrackAdapter<Context, EventData, AdapterOptions, RealEventData>) {
if (!adapter) {
throw new Error('Adapter is required');
}

const adapterBuilder = new AdapterBuilder<Context, EventData, AdapterOptions>(
adapter
);
const adapterBuilder = new AdapterBuilder<
Context,
EventData,
AdapterOptions,
RealEventData
>(adapter);

return adapterBuilder.init();
}
14 changes: 1 addition & 13 deletions src/helpers/helper-adapter-track.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { TrackContext } from '../types/types-create.js';
import { TrackAdapterMap, TrackEventDataBase } from '../types/types-track.js';
import { executeFunction } from './helper-execute.js';

/**
* Executes the track function of each adapter in the adapterMap for the given eventType and result.
Expand All @@ -24,18 +23,7 @@ export const executeAdapterTrack = async <
result: EventData[EventType]
): Promise<EventData> => {
for (const [adapterName, adapter] of Object.entries(adapterMap)) {
const isTrackable = await executeFunction(
adapter.isTrackable,
ctx,
eventType,
result
);

if (!isTrackable) {
ctx.logger?.warn(`Adapter is not trackable: ${adapterName}`);
continue;
}
await adapter.track<EventType>(ctx, eventType, result);
await adapter.track<EventType>(adapterName, ctx, eventType, result);
}
return result;
};
Loading

0 comments on commit ae77ae1

Please sign in to comment.