-
Notifications
You must be signed in to change notification settings - Fork 2
feat: Use type discriminators for event payload. #89
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
Conversation
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.
A quick check now, will need to compare with the code and the documentation to make a deeper review.
Thanks! Much appreciated. I fixed the comments you had now so far. Let me know when more arrives ❤️ |
By the way, here's TypeScript's official Discriminate Types example if you want to try out how something like this would work: More documentation here: https://basarat.gitbook.io/typescript/type-system/discriminated-unions |
I appreciate the teaching mindset you are exhibiting. But I understand Discriminate Types, that's no issue. I will try to make time to review your changes today or tomorrow. |
Yeah sorry, didn't mean to be condescending in any way 🙏 When I saw the private methods that weren't really private (but just prefixed with underscore like you'd do in Python), I was just unsure if the team maintaining this repo had experience with the more advanced TypeScript features, if they came from a Python background ❤️ I see now that you do! 👌 |
The issue was a bit thornier than I thought and will have to get some more input from other people on how this actually works next week. Found a few things that should be changed until then. Suggested edit: diff --git a/source/event_hub.ts b/source/event_hub.ts
index f784715..3154ed9 100644
--- a/source/event_hub.ts
+++ b/source/event_hub.ts
@@ -20,9 +20,10 @@ interface BaseActionData {
interface BaseEventPayload {
target: string;
- inReplyToEvent: string;
+ inReplyToEvent?: string;
source: EventSource;
id: string;
+ sent?: boolean;
}
export interface ActionDiscoverEventPayload extends BaseEventPayload {
@@ -32,8 +33,9 @@ export interface ActionDiscoverEventPayload extends BaseEventPayload {
export interface ActionLaunchEventData extends BaseActionData {
actionIdentifier: string;
- description: string;
- label: string;
+ description?: string;
+ label?: string;
+ applicationIdentifier?: string;
}
export interface ActionLaunchEventPayload extends BaseEventPayload {
|
The given changes have been applied! Thank you for looking into all of this so fast and with such high priority. It really sparks a motivation to make many more pull requests in the future. |
The more I look into this the less rigid the event structure seems. There are always a bunch of exceptions to the general rules. To ensure nothing breaks at runtime it seems best to assume everything can come back as undefined and make sure to handle that. We might have to make even more things optional (And there's a handful I might actually be able to put back as not optional). After these changes, I'll approve the PR and set up a task to make an even more thorough review of the entire event hub. The changes will not make everything 100% correct for all event topics yet, but it's definitely closer than the current state. Suggested edit: diff --git a/source/event_hub.ts b/source/event_hub.ts
index dc7d820..5adba5f 100644
--- a/source/event_hub.ts
+++ b/source/event_hub.ts
@@ -44,14 +44,14 @@ export interface ActionLaunchEventPayload extends BaseEventPayload {
}
export interface UpdateEventData {
- entities: EventEntity[];
- pushToken: string;
- parents: string[];
- user: {
+ entities?: EventEntity[];
+ pushToken?: string;
+ parents?: string[];
+ user?: {
userid: string;
name: string;
};
- clientToken: string;
+ clientToken?: string;
}
export interface UpdateEventPayload extends BaseEventPayload {
@@ -81,20 +81,20 @@ export interface EventSource {
}
export interface EventEntity {
- entity_type: string;
- keys: string[];
- objectTypeId: string;
- entityType: string;
- parents: {
+ entity_type?: string;
+ keys?: string[];
+ objectTypeId?: string;
+ entityType?: string;
+ parents?: {
entityId: string;
entityType: string;
entity_type: string;
parentId?: string;
}[];
- parentId: string;
- action: string;
- entityId: string;
- changes: Data;
+ parentId?: string;
+ action?: string;
+ entityId?: string;
+ changes?: Data;
}
export interface SubscriberMetadata {
|
Do those exceptions have distinguishable scenarios that can be handled by discriminators? Or do some of them always go in pairs? For instance, (hypothetically) if parentId is set, is entityId then also always set, etc. If that's the case, we can still have separate types for them, and then have unions without type discriminators. Then it's still more flexible and stable. I agree that it doesn't have to be perfect. Just better every time. That being said, we could also try to make some of them required (for the ones where we aren't sure) and turn them back to optional if it becomes a problem. I'd actually rather do that than the other way around. But it's of course up to you! Thank you so much for the effort so far - this is amazing stuff. |
I have applied the changes now. Once again thanks! As for when you'll get back to making the types better (and look at the event hub), I think splitting all the different types into separate files could also do wonders to the clarity (but I assume you know this already - just sharing my opinion in case). |
Suggested edit: diff --git a/source/event_hub.ts b/source/event_hub.ts
index 5adba5f..a4369e4 100644
--- a/source/event_hub.ts
+++ b/source/event_hub.ts
@@ -578,7 +578,9 @@ export class EventHub {
*/
_handleReply(eventPayload: EventPayload) {
this.logger.debug("Reply received", eventPayload);
- const onReplyCallback = this._replyCallbacks[eventPayload.inReplyToEvent];
+ const onReplyCallback = !eventPayload.inReplyToEvent
+ ? null
+ : this._replyCallbacks[eventPayload.inReplyToEvent];
if (onReplyCallback) {
onReplyCallback(eventPayload);
}
|
I'm of the viewpoint that it's better to have things that possibly might be missing to be optional. It can be a hassle writing null-checks, but a runtime crash because data that "should" be there isn't is worse. With that said, I'll investigate if there are more things we can ensure are there. Had a small suggestion that needs to be fixed, but then I'm happy. I'll assign a colleague to do a final sanity check as well. |
Fair enough, that is understandable! I applied the most recent fixes now! 🙏 |
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.
Ping @jimmycallin for a final sanity check
Changes
Currently, there are two main issues with the typings of Ftrack events.
EventData
type is sometimes incorrect. It assumes that the data for the event is always for anftrack.update
event. So for most other events, the type is incorrect, which is a bug in my opinion.Here's an example that explains the issue before my pull request.
After my pull request, the TypeScript compiler understands the discriminator and can narrow the event down without casting.
Test
It should be tested by running tests and compiling the code. This has already been done.