-
Notifications
You must be signed in to change notification settings - Fork 387
Best-attempt event read on version pinning #654
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
Best-attempt event read on version pinning #654
Conversation
|
Nice @mickjermsurawong-stripe! Thanks for taking a stab at this one. I think this looks like a super plausible approach. The one thing I'd consider changing though is just the public interface that we expose — to my eye, the one in here presents the user with too many options to leaf through. If we boil everything right down, I think what we want to do is give the user two paths for accessing event data:
The way I'd love to see the interface work is something like this: maybeData = event.getDataMaybe();
if (maybeData.deserialize()) {
maybeData.getData().doSomething();
} else {
maybeData.getRawJson().doSomething();
}Or for the unsafe variant: try {
data = event.getDataMaybe().unwrap();
} catch (EventDataDeserializationException e) {
e.getRawJson().doSomething();
}I don't think it's too dissimilar to what you have, but IMO the current interface might be shoehorning too many methods onto |
|
Thank you @brandur-stripe! |
396970c to
34ba516
Compare
src/main/java/com/stripe/exception/EventDataDeserializationException.java
Outdated
Show resolved
Hide resolved
src/test/resources/api_fixtures/source_mandate_notification_event.json
Outdated
Show resolved
Hide resolved
|
ptal @brandur-stripe, the revised interface now will work like this. I try to make the naming for the two options a little consistent. StripeObject stripeObject;
if (eventData.safeDeserialize()) {
stripeObject = eventData.getObject();
} else {
eventData.getObject(); // gives null
try {
stripeObject = eventData.forceDeserialize();
} catch (EventDataDeserializationException e) {
JsonElement rawJson = e.getRawJson();
// should we offer functionality below?
// JsonElement patchedRawJson = patch(rawJson);
// stripeObject = eventData.deserializeStripeObject(patchedRawJson);
}
}
public class Event extends ApiResource implements HasId {
...
String apiVersion;
Long created;
@Deprecated
EventData data;
EventVersionedData versionedData;
... |
34ba516 to
94f1103
Compare
94f1103 to
9a4ed80
Compare
mickjermsurawong-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.
I decided to do another iteration which gets rid major changes to the serialization logic.
The story here is that we deprecate event.getData().getObject(), in favor of event.getVersionedData().getObject().
- Introduced a proxy class
EventVersionedDataso there's no overloading originalEventDatafully represents to the actual JSON. - The versioned data in constructed during a method call, to get properties of the original
EventDataand also to include api version ofEvent.apiVersion. - The versioned data has interfaces
safeDeserializeforceDeserializefor safe/unsafe, and a recovery methodretryDeserialize
| * Get event data with explicit deserialization support. This object contains superset | ||
| * information of {@link EventData}. | ||
| */ | ||
| public EventVersionedData getVersionedData() { |
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.
Entry point to the new proxy object that is preferred over the original EventData
| * Raw JSON object intended to be deserialized as {@code StripeObject}. The deserialization | ||
| * should be deferred to the user. See the now deprecated method {@link EventData#getObject()}. | ||
| */ | ||
| JsonObject object; |
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.
Having JSON makes sure that class-specific deserialization failure is deferred (not during deserializing the outer class Event) and can be handled in the new API EventVersionedData.
Although there is type change here, but the exposed API to client getting StripeObject is still the same via getObject deprecated method below.
| StripeObject object = ApiResource.GSON.fromJson( | ||
| entry.getValue(), cl != null ? cl : StripeRawJsonObject.class); | ||
| eventData.setObject(object); | ||
| eventData.setObject(entry.getValue().getAsJsonObject()); |
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.
EventData no longer tries to deserialize risky class-specific JSON, and it is exposed in EventVersionedData instead making using of the method below deserializeStripeObject
| * event.getApiVersion()); | ||
| * stripeObject = versionedData.retryDeserialize(compatibleJson); | ||
| * } | ||
| * } |
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.
The flow of how client may use this new object.
|
Nice @mickjermsurawong-stripe! LGTM. The only thing I'd argue for are some name changes:
|
f5365a9 to
2df017e
Compare
2df017e to
d4af253
Compare
|
thank you @brandur-stripe !
|
|
Woot. Thanks @mickjermsurawong-stripe! @ob-stripe Mind taking a look at this one as well? You probably have more informed ideas than I do. |
|
Thank you @ob-stripe for the discussion on Slack.
Event event = Event.retrieve("evt_123");
EventDataObjectDeserializer deserializer = event.getDataObjectDeserializer();
StripeObject stripeObject;
if (deserializer.deserialize()) {
stripeObject = deserializer.getObject();
} else {
try {
stripeObject = deserializer.deserializeUnsafe();
} catch (EventDataDeserializationException e) {
stripeObject = deserializer.deserializeUnsafeWith(
new EventDataObjectDeserializer.CompatibilityTransformer() {
@Override
public JsonObject transform(JsonObject rawJsonObject,
String apiVersion,
String eventType) {
if (apiVersion.equals("2018-05-01")) {
String fooVal = rawJsonObject.remove("foo");
rawJsonObject.add("foo_v2", fooVal);
}
if (apiVersion.equals("2018-08-01")) {
anotherTransform(rawJsonObject);
}
return rawJsonObject;
}
});
);
}
}On a separate note, I know this is essentially looks like pushing users to do their own forward compatibility, but I think it's the fullest support we can provide when we move to pinned-version, given the current state of our backed. |
|
Addressed the pending concern, and from discussion on slack @ob-stripe agrees with this as a temporary solution for event endpoint integration. |
|
Rebasing to |
EventDatamore explicit, having 3 the read mode: safe, best attempt, and raw read on failure.getData()to existing integration, and has the same behavior except on failure case.r? @ob-stripe @brandur-stripe
cc @stripe/api-libraries
The build fail now is due to jdk 11 failing to download.