Last active
March 1, 2024 21:29
-
-
Save luisvinicius09/78677634941bb476c31e5f1427eda871 to your computer and use it in GitHub Desktop.
Event Processor challenge - NO BS TS
This file contains hidden or 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
---- BASIC CHALLENGE ---- | |
interface MyEvent<T> { | |
eventName: keyof T; | |
data: T[keyof T]; | |
} | |
type MyFilterType<T, Key extends keyof T> = { eventName: Key; callback: (data: T[Key]) => boolean }; | |
type MyMapType<T, Key extends keyof T> = { eventName: Key; callback: (data: T[Key]) => T[Key] }; | |
abstract class EventProcessor<T> { | |
protected events: MyEvent<T>[] = []; | |
protected filters: MyFilterType<T, keyof T>[] = []; | |
protected maps: MyMapType<T, keyof T>[] = []; | |
abstract handleEvent(eventName: keyof T, data: T[keyof T]): void; | |
addFilter<Key extends keyof T>(eventName: Key, filter: MyFilterType<T, Key>['callback']): void { | |
this.filters.push({ eventName: eventName, callback: filter as (data: T[keyof T]) => boolean }); | |
return; | |
} | |
addMap<Key extends keyof T>(eventName: Key, map: MyMapType<T, Key>['callback']): void { | |
this.maps.push({ | |
eventName: eventName, | |
callback: map as unknown as (data: T[keyof T]) => T[keyof T], | |
}); | |
return; | |
} | |
public getProcessedEvents(): MyEvent<T>[] { | |
return this.events; | |
} | |
} | |
interface EventMap { | |
login: { user?: string | null; name?: string; hasSession?: boolean }; | |
logout: { user?: string }; | |
} | |
class UserEventProcessor extends EventProcessor<EventMap> { | |
public handleEvent(eventName: keyof EventMap, data: EventMap[keyof EventMap]) { | |
let shouldProcess = true; | |
for (const item of this.filters) { | |
if (item.eventName === eventName) { | |
if (!item.callback(data)) { | |
shouldProcess = false; | |
break; | |
} | |
} | |
} | |
if (shouldProcess) { | |
let mappedData = { ...data }; | |
this.maps.forEach((item) => { | |
if (item.eventName === eventName) { | |
mappedData = item.callback(data); | |
} | |
}); | |
this.events.push({ eventName: eventName, data: mappedData }); | |
} | |
return; | |
} | |
} | |
const uep = new UserEventProcessor(); | |
uep.addFilter('login', ({ user }) => Boolean(user)); | |
uep.addMap('login', (data) => ({ | |
...data, | |
hasSession: Boolean(data.user && data.name), | |
})); | |
uep.handleEvent('login', { | |
user: null, | |
name: 'jack', | |
}); | |
uep.handleEvent('login', { | |
user: 'tom', | |
name: 'tomas', | |
}); | |
uep.handleEvent('logout', { | |
user: 'tom', | |
}); | |
console.log(uep.getProcessedEvents()); | |
/* | |
Result: | |
[ | |
{ | |
eventName: 'login', | |
data: { user: 'tom', name: 'tomas', hasSession: true } | |
}, | |
{ eventName: 'logout', data: { user: 'tom' } } | |
] | |
*/ | |
---- ADVANCED CHALLENGE ---- | |
type Handlers = "filter" | "map"; | |
type HandlerFunctionName<T> = `${Handlers}${Capitalize<Extract<keyof T, string>>}`; // that extract weirdly sucks | |
// type HandlerFunctionName<T> = `${Handlers}${Capitalize<keyof T & string>}`; // that extract weirdly sucks | |
// https://stackoverflow.com/questions/72264448/template-literal-from-keyof-in-class | |
// TODO: fix that extract or make sure it's okay | |
type MyEventType<T, Key extends keyof T> = { eventName: Key, data: T[Key] }; | |
type MyHandlerType<T, Key extends keyof T> = { [Property in HandlerFunctionName<T>]: (data: T[Key]) => T[Key] }; | |
class EventProcessor<T extends object> { | |
private events: MyEventType<T, keyof T>[] = []; | |
private handlers: MyHandlerType<T, keyof T>[] = []; | |
handleEvent<Key extends keyof T>(eventName: Key, data: T[Key]): void { | |
// tODO run events | |
let shouldProcess: true; | |
} | |
addHandler(handler: MyHandlerType<T, keyof T>): void { | |
// this.handlers = { ...this.handlers, handler } | |
this.handlers.push(handler); | |
return; | |
} | |
getProcessedEvents(): MyEventType<T, keyof T>[] { | |
return this.events; | |
} | |
} | |
interface EventMap { | |
login: { user?: string; name?: string; hasSession?: boolean }; | |
logout: { user?: string }; | |
} | |
class UserEventProcessor extends EventProcessor<EventMap> { } | |
const uep = new UserEventProcessor(); | |
uep.addHandler({ | |
filterLogin: ({ user }) => Boolean(user), | |
mapLogin: (data) => ({ | |
...data, | |
hasSession: Boolean(data.user && data.name), | |
}), | |
}); | |
uep.handleEvent("login", { | |
user: null, | |
name: "jack", | |
}); | |
uep.handleEvent("login", { | |
user: "tom", | |
name: "tomas", | |
}); | |
uep.handleEvent("logout", { | |
user: "tom", | |
}); | |
console.log(uep.getProcessedEvents()); | |
/* | |
Result: | |
[ | |
{ | |
eventName: 'login', | |
data: { user: 'tom', name: 'tomas', hasSession: true } | |
}, | |
{ eventName: 'logout', data: { user: 'tom' } } | |
] | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment