-
Notifications
You must be signed in to change notification settings - Fork 585
add event handler class #3250
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
base: beta
Are you sure you want to change the base?
add event handler class #3250
Conversation
7a594d9 to
3e3987b
Compare
| /// <param name="client">The StripeClient instance to use for parsing and API requests.</param> | ||
| /// <param name="webhookSecret">The webhook secret used for signature verification.</param> | ||
| /// <param name="unhandledEventHandler">TODO: ADD UNHANDLED DETAILS.</param> | ||
| public StripeEventRouter(StripeClient client, string webhookSecret, EventHandler<StripeUnhandledEventNotificationEventArgs> unhandledEventHandler) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since unhandledEventHandler i'm not convinced it actually needs to be an event handler? but i'm not sure if that's what users expect anyway
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think I've seen any classes take an event handler as a constructor arg, but also I don't think I've seen too many patterns where an event is expected to have a handler when it is fired or else its an error. I think the shape of this is fine, but if you wanted to make it a little more "this is just a function"-like, you could do:
public StripeEventRouter(StripeClient client, string webhookSecret, Action<object, StripeUnhandledEventNotificationEventArgs> unhandledEventHandler)
{
this.client = client ?? throw new ArgumentNullException(nameof(client));
this.webhookSecret = webhookSecret ?? throw new ArgumentNullException(nameof(webhookSecret));
this.UnhandledEventHandler += new EventHandler<StripeUnhandledEventNotificationEventArgs>(unhandledEventHandler);
}
This has the added benefit of making it easy to pass anonymous functions in; but they also could do the new EventHandler... adapter call if you wanted to keep the argument the way it is.
Absent user feedback, I don't think one way is necessarily better than the other.
|
|
||
| _dotnet_install tpv: | ||
| ./scripts/dotnet-install.sh --channel {{ tpv }} --install-dir $DOTNET_ROOT | ||
| ./scripts/dotnet-install.sh --channel {{ tpv }} --install-dir "$(mise where dotnet)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Env isn't shared between recipes, so this doesn't work if you don't have the root set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we may want to fail if DOTNET_ROOT isnt set here; iirc a lot of stuff doesnt work right if that env var isn't already set.
|
Not sure if the comment should go here or in your doc, but HandleUnhandledError seems like the wrong name in your example. That's just HandleUnhandledEventNotification, right? |
| /// </summary> | ||
| /// <param name="eventNotification">The event notification instance.</param> | ||
| /// <param name="client">The StripeClient instance.</param> | ||
| public StripeEventNotificationEventArgs(TEventNotification eventNotification, StripeClient client) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will this ever get constructed outside of our SDK code? If not, I would consider changing this constructor to be internal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't users need it for tests? Like, if they write their function and want to test it in isolation, that function takes these args and they'd nee to be able to construct it?
jar-stripe
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took a pass thru, it looks largely good (the names are pretty long and I left a comment in your naming doc), but I have concerns about StripeClient/ApiRequestor reuse when you swap in and out the StripeContext
| /// <param name="client">The StripeClient instance.</param> | ||
| public StripeEventNotificationEventArgs(TEventNotification eventNotification, StripeClient client) | ||
| { | ||
| this.EventNotification = eventNotification ?? throw new ArgumentNullException(nameof(eventNotification)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its a small nit, but can you pull the null check and throw into a separate line/conditional? I find that easier to read
| /// <param name="client">The StripeClient instance to use for parsing and API requests.</param> | ||
| /// <param name="webhookSecret">The webhook secret used for signature verification.</param> | ||
| /// <param name="unhandledEventHandler">TODO: ADD UNHANDLED DETAILS.</param> | ||
| public StripeEventRouter(StripeClient client, string webhookSecret, EventHandler<StripeUnhandledEventNotificationEventArgs> unhandledEventHandler) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think I've seen any classes take an event handler as a constructor arg, but also I don't think I've seen too many patterns where an event is expected to have a handler when it is fired or else its an error. I think the shape of this is fine, but if you wanted to make it a little more "this is just a function"-like, you could do:
public StripeEventRouter(StripeClient client, string webhookSecret, Action<object, StripeUnhandledEventNotificationEventArgs> unhandledEventHandler)
{
this.client = client ?? throw new ArgumentNullException(nameof(client));
this.webhookSecret = webhookSecret ?? throw new ArgumentNullException(nameof(webhookSecret));
this.UnhandledEventHandler += new EventHandler<StripeUnhandledEventNotificationEventArgs>(unhandledEventHandler);
}
This has the added benefit of making it easy to pass anonymous functions in; but they also could do the new EventHandler... adapter call if you wanted to keep the argument the way it is.
Absent user feedback, I don't think one way is necessarily better than the other.
|
|
||
| _dotnet_install tpv: | ||
| ./scripts/dotnet-install.sh --channel {{ tpv }} --install-dir $DOTNET_ROOT | ||
| ./scripts/dotnet-install.sh --channel {{ tpv }} --install-dir "$(mise where dotnet)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we may want to fail if DOTNET_ROOT isnt set here; iirc a lot of stuff doesnt work right if that env var isn't already set.
| } | ||
|
|
||
| // Save the original context and temporarily set the event's context | ||
| var originalContext = requestor.CurrentStripeContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you can safely swap out and back the StripeContext in an environment with concurrent API calls, if this.client represents the StripeClient that created the router. The EventRouter should hold its own StripeClient and requestor, if it is expected to make API calls with different context than e.g. a user's "normal" StripeClient instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably check this in the other SDKs too; we're not always consistent about which request objects are reused vs recreated in different scenarios.
| // event-handler-dispatch: The end of the section generated from our OpenAPI spec | ||
| else | ||
| { | ||
| throw new Exception("unexpected state, please file a bug"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this message needs some love 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but actually, in all seriousness could this just fall thru to the UnhandledEventHandler? at least then the user might see it and have context in which to raise an issue back to us and/or recover on their side if we have to fix something.
|
Also, we should consider adding the example from the PR description as an actual example in the project, so folks can see how to use this from code they can easily find. |
Why?
We've been designing a streamlined approach to handling incoming events that is easy to get right and hard to get wrong. This PR has the initial implementation of this new system.
The only other pending item is to add a method to allow handling a webhook without verifying the signature. This is good for testing and for Event Bridge, which doesn't use the signature-based verification. Otherwise, this is ready for review.
What?
EventHandlerclassStripeClientExample usage
See Also