-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Allow users to access unsupported Activty event payloads as Json strings #1645
Conversation
- Alter `GetJsonFieldName` to append a unique suffix to fields that have `AllowDuplicates == true` so that it can be stored in the SetterCache dictionary - Alter deserializer to trim off the unique suffix when looking up the API field to populate via Setter delegate
… `payload` (using new `AllowDuplicates` field on `Parameter` attribute)
…all 8 payloads are covered by tests Add a test for unsupported event payload
Actually I just realised that there are unit tests here covering each of the payload deserialization (using hardcoded json responses) so im not sure if we need the integration tests or not. On the one hand they are useful since there's no telling whether the upstream API responses may look different to the hardcoded test fixtures but the rarer events do pose a problem in finding them within the 300 events/90day limits on the GitHub events API |
Would it be more convenient to provide a type wrapping the payload JSON? So you could add a method like |
I am not following what this gains? |
It gives you a really convenient way to deserialize the JSON into the type you provide, using the custom (GitHub-specific) JSON (de-)serialization settings. |
Do you think there is a use case for consumers providing their own type? Given how specific the deserializer rules are (eg mapping ruby/snake case to c# camel case, using our That said I do still like the idea not so that people can BYO response class but more so because it could provide a bit more discoverability and compile time guidance than we currently have. Eg currently you just "have to know" that you can cast the Payload to another type via Whereas if we implemented a method like Im not sure if |
Yes, the use case is that there might be no official type for the payload yet, and you're blocked until next release. Isn't this what we're trying to fix?
Yes, that would be preferable. But this will let them do the work and unblock themselves first, then submit a PR 😄
What if it's a new payload type that doesn't have a type yet?
In my opinion, this problem is the exact same problem as the Is this generic enough for a |
@ryangribble Yes, this exactly what I need - then I can use something like JsonConvert in order to deserialize the object into my own DeleteEventPayload class which contains all the fields which I need to access. But what would be even better is not having to create this class at all, have Octokit deserialize into some kind of dynamic object instead, so that I can access its fields however I like. |
We could return a I figured the string json was the most flexible though |
As a consumer of this library, literally anything than what is currently being done now would be preferable. Not having access to the raw JSON payload for all of the unimplemented activity types means that I need to resort to executing a raw request and parse into my own types - which is really unfortunate. The addition of this change would be really helpful. |
Thanks for the feedback @thedillonb, as you can see above we had a few options regarding how to actually implement this, however I agree that anything is better than how things are now! I'll aim to pick this back up and at least get something in |
There are cases where a user can set a payload such as Perhaps a solution that handles all these cases is important. If I were using JSON.NET I'd make those properties a The nice thing about Is it worth considering moving to |
👋 Hey Friends, this pull request has been automatically marked as |
Background
As part of the Events API,
Activity
responses are retrieved that contain aPayload
field which varies according to the event in question. In Octokit, we implement thePayload
field of theActivity
response model as a base type ofActivityPayload
but specific inherited payload classes such asIssueCommentPayload
,PullRequestEventPayload
etc are assigned to this field via some deserializerhackeryMAGIC 😀Problem
When GitHub API implements new event payloads, the payload cannot be obtained via Octokit until a response model class for the specific payload is added (and the magic switch statement is updated).
It actually turns out that GitHub API has currently 37 event payloads, and only 8 of these are implemented in Octokit.net! 🤔
What does this PR do?
Whilst we should implement all of these outstanding payload types (and any future ones!) (see #1646), this PR aims to add a generic "workaround" to Octokit so that in addition to the specific
Payload
response model classes, there is aPayloadJson
field containing the raw json of the event payload. This field can be used by consumers to obtain the payload of all events - regardless of whether they are yet implemented in Octokit or not.A picture tells 1000 words so here is some debugger output showing what the
Payload
andPayloadJson
fields look like for an implemented and unimplemented event type:IssueCommentEvent
(implemented in Octokit)The Payload is of explicit type
IssueCommentPayload
(andPayloadJson
is also available)DeleteEvent
(not currently implemented in Octokit)The Payload is of base type
ActivityPayload
and doesn't provide anything useful, but thePayloadJson
is now available and can be used by clients to at least understand this event prior to a Model for it being added to Octokit.netImplementation Notes
Implementing this required some tweaks to the deserializer so that we can have more than one response field that maps to the same API value (eg in this case the API field
payload
maps to bothActivityPayload Payload
andstring PayloadJson
). To limit the impact, I added a new field to the[Parameter]
attribute we use, that explicitly marks the parameter declarationAllowDuplicates
. In the internals of the deserializer, it then appends a unique value to the api field (so it can be stored in the dictionary for theSetterCache
along with other instances of the same api field). In the case ofPayloadJson
it would end up in theSetterCache
aspayload_octokit_payload_json
. Then when populating models from the api response it removes this appended text from the dictionary key, so it can find the correct API field (payload
). It's a bit hacky but IMO no more so than the existing "magic" that was switching out the classes derived fromActivityPayload
base class in the first place.A 2nd deserializer tweak was needed so that an entire
JsonObject
could be written to astring
property (by calling.ToString()
on it) - this is used so that thepayload
API field (aJsonObject
structure) can be mapped to ourstring PayloadJson
field.I also fixed up the existing Event payload integration tests which were previuosly skipped due to them looking for events that are too long ago. I tweaked them to use our pagination support to keep searching until they find an event of the desired type. We also now have an integration test for each of the 8 payload types octokit currently supports. NOTE that there is a chance that these tests can fail if we hit the pagination limit of the events API before we encounter an event of the desired type but this is acceptable for the time being IMO