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/request scoped event emitters #544

Merged

Conversation

thiagomini
Copy link
Contributor

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Other... Please describe:

What is the current behavior?

Request Scoped listeners are not available right now.

What is the new behavior?

We can inject the EVENT_PAYLOAD symbol and make our listeners Request Scoped:

class EventListener {
  constructor(@Inject(EVENT_PAYLOAD) private readonly eventPayload: any) { }
}

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

You can follow the commits to understand how it became from the first version to the refactored one, which I used to reduce the complexity of the EventSubscribersLoader class.

}
}

public static injectedEventPayload = {};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason why I created this static property is to make it easy to check the event payload was injected in the tests. Otherwise we would need a specific contextId

@@ -9,7 +9,7 @@
"esModuleInterop": true,
"experimentalDecorators": true,
"target": "es6",
"sourceMap": false,
"sourceMap": true,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed this to enable local debug. It was really helpful in my experience here

Copy link

@delucca delucca left a comment

Choose a reason for hiding this comment

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

Great job 🚀

I'm not going to approve it, since I don't know if this could break something. But I added a few minor comments 😄

instance,
methodKey,
isRequestScoped,
wrapper.host as Module,
Copy link

Choose a reason for hiding this comment

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

There is no other way of doing this without using as? Just to avoid losing type-safety

Copy link
Contributor Author

@thiagomini thiagomini Jul 7, 2022

Choose a reason for hiding this comment

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

I tried a couple of things but apparently this is the less hacky way of doing it. To give you more context on that, the reason Why I had to do it is because of the following:

  1. The InstanceWrapper.host is an optional attribute, as you can see here

  2. This host attribute is used by the injector.loadPerContext method in this same file

  3. loadPerContext method requires a module attribute, hence it's not optional

  4. this project has strict flag set to true, as you can see in the tsconfig.json

The reason 4 is the actual culprit of this as Module statement. You can see in the nestjs/bull package the module is handed over the function with no casting, and that's because its tsconfig file does not have strict: true

Copy link

Choose a reason for hiding this comment

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

if it's optional, can't you just if it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You mean to add an if statement before calling subscribeToEventIfListener ?

Choose a reason for hiding this comment

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

Yes.
Maybe like

if(!wrapper.host) {
  throw new Error()
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmmm I see, but do you think this is really the expected behavior here? To be honest, I'm not sure what would happen if this host really didn't exist when we tried to inject the instance. @kamilmysliwiec do you have some opinion on that as wel?

Copy link
Member

Choose a reason for hiding this comment

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

At that time, host will never be undefined so the as Module assertion is correct here.

lib/event-subscribers.loader.ts Outdated Show resolved Hide resolved
*/

const payloadObjectOrArray =
eventPayload.length > 1 ? eventPayload : eventPayload[0];
Copy link

Choose a reason for hiding this comment

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

I'm not 100% sure about the context here, but if eventPayload can be a string this conditional would not work, since 'foo'.length is 1.

In other words. To be sure that something is an array you need to use the static Array.isArray()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll double check this, but I think the eventPayload here will actually always be an array, even if we passed a string. That's because the eventPayload here is actually an alias for the function's args parameters, which is a rest parameter (and thus, an array)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a test for the case you mentioned in this commit and it works as expected, therefore I think we don't have to check against an array here

@kamilmysliwiec
Copy link
Member

Fantastic PR @thiagomini, thank you 🙌

refactor request-scoped event payloads to increase the readability and reduce the burden to add other request payload types in the future
@thiagomini
Copy link
Contributor Author

Fantastic PR @thiagomini, thank you 🙌

Thank you, Kamil! I added a few other commits here to resolve the review suggestions. I also refactored a bit the test code to use constants instead of manually set objects. In the final commit, I refactored a bit of the EventsProviderRequestScopedConsumer to look cleaner. Before this commit, I was adding more and more static variables to the class to handle different event payloads, so I decided to move this logic away to another object, the RequestScopedEventPayload class.

@kamilmysliwiec
Copy link
Member

Left 1 minor comment, otherwise, it should be good to go!

rename EventSubscribersLoader private method
@kamilmysliwiec kamilmysliwiec merged commit c209f33 into nestjs:master Jul 12, 2022
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.

None yet

4 participants