Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Events #76

Merged
merged 22 commits into from
Feb 19, 2024
Merged

Feature: Events #76

merged 22 commits into from
Feb 19, 2024

Conversation

marco-souza
Copy link
Contributor

@marco-souza marco-souza commented Feb 15, 2024

Description

This Pull Request provides a simple observer implementation, allowing you to subscribe and listen for various events that occur in your application. Events serve as a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other.

This PR implements EventEmitter module for danet, trying to keep a similar interface with the Nest.JS events.


Issue Ticket Number

Relates to


Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to
    not work as expected)
  • This change requires a documentation update

Checklist:

  • I have run deno lint AND deno fmt AND deno task test and got no
    errors.
  • I have followed the contributing guidelines of this project as mentioned
    in CONTRIBUTING.md
  • I have checked to ensure there aren't other open
    Pull Requests for the same
    update/change?
  • I have performed a self-review of my own code
  • I have made corresponding changes needed to the documentation

@Sorikairox
Copy link
Collaborator

Thanks for this first draft !

I believe we can use the "new" https://developer.mozilla.org/en-US/docs/Web/API/EventTarget API instead of implementing our own class !

I also think we should have both EventEmitter and EventEmitterModule (which may not contain a forRoot method, because there is no settings for this module).

Regarding adding code to the injector, it's fine to get it working first, and then we will probably want to move that logic outside of Danet core and implement it in its own module. But that will require change in Danet so let's first focus on making this work !

@marco-souza
Copy link
Contributor Author

Sounds great @Sorikairox , I'll work on these changes 👍🏼

@marco-souza
Copy link
Contributor Author

@Sorikairox changes done!
Let me know your thoughts on this.

I'm also planning to add some test cases, but I'm not sure if I'll have the time 'til the weekend

src/events/events.ts Outdated Show resolved Hide resolved
example/events.ts Outdated Show resolved Hide resolved
@Sorikairox
Copy link
Collaborator

@marco-souza Thanks for your efforts!

To make this (and the future Queue/Pubsub module) standalone, I need to make Danet's injector a singleton, modify modules handling (instantiate them instead of relying on their constructor type), allow us to use a similar pattern to forRoot and handle onApplicationBootstrap on Module. I will work on that in the meantime

@marco-souza
Copy link
Contributor Author

@Sorikairox Nice, thank you for taking care of that! Let me know when you have it so I can rebase and adjust this PR.

By the way, do you think the EventEmitter implementation is looking good? If so, I will update the docs and add some tests to the module.

@Sorikairox
Copy link
Collaborator

@marco-souza Yeah it looks good, if it works, then go for it ! ❤️

@Sorikairox
Copy link
Collaborator

Sorikairox commented Feb 17, 2024

@marco-souza Main branch now contains (if I did that well) everything required to make it standalone.
It should be something like this:

import { injector }  from 'danet/mod.ts'

function registerAvailableEventListeners(injectableInstance: any) {
		const methods = Object.getOwnPropertyNames(Type.prototype);

		for (const method of methods) {
			const target = Type.prototype[method];
			const eventListenerMedatada = MetadataHelper.getMetadata<
				{ channel: string }
			>(
				eventListenerMetadataKey,
				target,
			);
			if (!eventListenerMedatada) continue;
			const { channel } = eventListenerMedatada;

			const emmiter = this.resolved.get(EventEmitter)?.() as EventEmitter;
			emmiter.subscribe(channel, target);
		}
	}


@Module({
	injectables: [EventEmitter],
})
export class EventEmitterModule implements OnAppBoostrap {
 onAppBoostrap() {
  const injectables = injector.injectables;
  for (const injectable of injectables) {
    registerAvailableEventListeners(injectable);
  }
}
}

So you should be able to remove the code from the injector, and once it works, we can take everything to its own repository 😄

@Sorikairox
Copy link
Collaborator

Sidenote, I noticed that there is a typo on emmitter, there is an additional m.

- separate module and service file
@marco-souza
Copy link
Contributor Author

@Sorikairox changes done!

I had to expose injector.resolvedTypes to get access to the actual type rather than its instance, or else I wouldn't be able to get its metadata using injector.injectables.

Let me know what you think about this approach.

@marco-souza
Copy link
Contributor Author

@Sorikairox tests and docs added! 🎉

I think now I can move this PR to "ready for review"

@marco-souza marco-souza marked this pull request as ready for review February 17, 2024 19:25
@codecov-commenter
Copy link

Codecov Report

Attention: 1 lines in your changes are missing coverage. Please review.

Comparison is base (465c68e) 85.84% compared to head (cfb2a15) 86.65%.

❗ Current head cfb2a15 differs from pull request most recent head df91b90. Consider uploading reports for the commit df91b90 to get more accurate results

Files Patch % Lines
src/events/events.ts 97.87% 1 Missing ⚠️

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #76      +/-   ##
==========================================
+ Coverage   85.84%   86.65%   +0.80%     
==========================================
  Files          47       54       +7     
  Lines        1561     1663     +102     
  Branches      163      176      +13     
==========================================
+ Hits         1340     1441     +101     
- Misses        219      220       +1     
  Partials        2        2              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Sorikairox
Copy link
Collaborator

Sorikairox commented Feb 18, 2024

@Sorikairox changes done!

I had to expose injector.resolvedTypes to get access to the actual type rather than its instance, or else I wouldn't be able to get its metadata using injector.injectables.

Let me know what you think about this approach.

I managed to make it work with injector.injectables, using the following:

const methods = Object.getOwnPropertyNames(injectableInstance.constructor.prototype);

Copy link
Collaborator

@Sorikairox Sorikairox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submitted code change to use injectables !

src/events/module.ts Outdated Show resolved Hide resolved
src/events/module.ts Outdated Show resolved Hide resolved
@Sorikairox Sorikairox merged commit 28ccfe7 into Savory:main Feb 19, 2024
1 check passed
@marco-souza
Copy link
Contributor Author

Awesome @Sorikairox, thank you for the changes! 🙏🏼

I didn't know we could use Object.getOwnPropertyNames(injectableInstance.constructor.prototype) for getting this info, great tip!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants