Skip to content

Conversation

@paulosouza-stark
Copy link
Contributor

Added

  • log on Event resource

@alexguerra
Copy link

Fix: Event.log not populated on Event.query(), Event.page(), Event.get()

Problem

When using Event.query(), Event.page(), or Event.get() methods, the log field is always null even though the API response contains the log data.

Root Cause

The Event.Deserializer uses context.deserialize() which does not have access to the registered type adapters needed to properly deserialize nested objects like Boleto.Log, Invoice.Log, etc.

Interestingly, line 121 already uses the correct approach for PaymentRequestEvent:

case "payment-request":
    return GsonEvent.getInstance().fromJson(jsonObject, PaymentRequestEvent.class);

This same pattern should be applied to all other event types.

Repository

This fix should be applied to sdk-java (com.starkbank:sdk), not core-java.

File: src/main/java/com/starkbank/Event.java


Code Changes

FROM (Current - Lines 73-129):

public static class Deserializer implements JsonDeserializer<Event> {
    @Override
    public Event deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();
        JsonElement type = jsonObject.get("subscription");
        if (type != null) {
            switch (type.getAsString()) {
                case "transfer":
                    return context.deserialize(jsonObject,
                            TransferEvent.class);
                case "boleto":
                    return context.deserialize(jsonObject,
                            BoletoEvent.class);
                case "boleto-payment":
                    return context.deserialize(jsonObject,
                            BoletoPaymentEvent.class);
                case "utility-payment":
                    return context.deserialize(jsonObject,
                            UtilityPaymentEvent.class);
                case "boleto-holmes":
                    return context.deserialize(jsonObject,
                            BoletoHolmesEvent.class);
                case "invoice":
                    return context.deserialize(jsonObject,
                            InvoiceEvent.class);
                case "deposit":
                    return context.deserialize(jsonObject,
                            DepositEvent.class);
                case "brcode-payment":
                    return context.deserialize(jsonObject,
                            BrcodePaymentEvent.class);
                case "tax-payment":
                    return context.deserialize(jsonObject,
                            TaxPaymentEvent.class);
                case "darf-payment":
                    return context.deserialize(jsonObject,
                            DarfPaymentEvent.class);
                case "invoice-pull-subscription":
                    return context.deserialize(jsonObject,
                            InvoicePullSubscriptionEvent.class);
                case "invoice-pull-request":
                    return context.deserialize(jsonObject,
                            InvoicePullRequestEvent.class);
                case "verified-account":
                    return context.deserialize(jsonObject,
                            VerifiedAccountEvent.class);
                case "payment-request":
                    return GsonEvent.getInstance().fromJson(jsonObject, PaymentRequestEvent.class);
                default:
                    return context.deserialize(jsonObject,
                            UnknownEvent.class);
            }
        }
        return null;
    }
}

TO (Fixed):

public static class Deserializer implements JsonDeserializer<Event> {
    @Override
    public Event deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();
        JsonElement type = jsonObject.get("subscription");
        if (type != null) {
            Gson gson = GsonEvent.getInstance();
            switch (type.getAsString()) {
                case "transfer":
                    return gson.fromJson(jsonObject, TransferEvent.class);
                case "boleto":
                    return gson.fromJson(jsonObject, BoletoEvent.class);
                case "boleto-payment":
                    return gson.fromJson(jsonObject, BoletoPaymentEvent.class);
                case "utility-payment":
                    return gson.fromJson(jsonObject, UtilityPaymentEvent.class);
                case "boleto-holmes":
                    return gson.fromJson(jsonObject, BoletoHolmesEvent.class);
                case "invoice":
                    return gson.fromJson(jsonObject, InvoiceEvent.class);
                case "deposit":
                    return gson.fromJson(jsonObject, DepositEvent.class);
                case "brcode-payment":
                    return gson.fromJson(jsonObject, BrcodePaymentEvent.class);
                case "tax-payment":
                    return gson.fromJson(jsonObject, TaxPaymentEvent.class);
                case "darf-payment":
                    return gson.fromJson(jsonObject, DarfPaymentEvent.class);
                case "invoice-pull-subscription":
                    return gson.fromJson(jsonObject, InvoicePullSubscriptionEvent.class);
                case "invoice-pull-request":
                    return gson.fromJson(jsonObject, InvoicePullRequestEvent.class);
                case "verified-account":
                    return gson.fromJson(jsonObject, VerifiedAccountEvent.class);
                case "payment-request":
                    return gson.fromJson(jsonObject, PaymentRequestEvent.class);
                default:
                    return gson.fromJson(jsonObject, UnknownEvent.class);
            }
        }
        return null;
    }
}

Summary of Changes

Line FROM TO
79 (nothing) Gson gson = GsonEvent.getInstance();
81-83 return context.deserialize(jsonObject, TransferEvent.class); return gson.fromJson(jsonObject, TransferEvent.class);
84-86 return context.deserialize(jsonObject, BoletoEvent.class); return gson.fromJson(jsonObject, BoletoEvent.class);
87-89 return context.deserialize(jsonObject, BoletoPaymentEvent.class); return gson.fromJson(jsonObject, BoletoPaymentEvent.class);
90-92 return context.deserialize(jsonObject, UtilityPaymentEvent.class); return gson.fromJson(jsonObject, UtilityPaymentEvent.class);
93-95 return context.deserialize(jsonObject, BoletoHolmesEvent.class); return gson.fromJson(jsonObject, BoletoHolmesEvent.class);
96-98 return context.deserialize(jsonObject, InvoiceEvent.class); return gson.fromJson(jsonObject, InvoiceEvent.class);
99-101 return context.deserialize(jsonObject, DepositEvent.class); return gson.fromJson(jsonObject, DepositEvent.class);
102-104 return context.deserialize(jsonObject, BrcodePaymentEvent.class); return gson.fromJson(jsonObject, BrcodePaymentEvent.class);
105-107 return context.deserialize(jsonObject, TaxPaymentEvent.class); return gson.fromJson(jsonObject, TaxPaymentEvent.class);
108-110 return context.deserialize(jsonObject, DarfPaymentEvent.class); return gson.fromJson(jsonObject, DarfPaymentEvent.class);
111-113 return context.deserialize(jsonObject, InvoicePullSubscriptionEvent.class); return gson.fromJson(jsonObject, InvoicePullSubscriptionEvent.class);
114-116 return context.deserialize(jsonObject, InvoicePullRequestEvent.class); return gson.fromJson(jsonObject, InvoicePullRequestEvent.class);
117-119 return context.deserialize(jsonObject, VerifiedAccountEvent.class); return gson.fromJson(jsonObject, VerifiedAccountEvent.class);
120-121 (already correct) (no change needed)
122-124 return context.deserialize(jsonObject, UnknownEvent.class); return gson.fromJson(jsonObject, UnknownEvent.class);

Why This Fix Works

  1. GsonEvent.getInstance() returns a Gson instance with all registered type adapters for Boleto.Log, Invoice.Log, etc.

  2. context.deserialize() uses a different Gson instance that does not have these type adapters registered.

  3. This fix ensures consistent deserialization across all code paths:

    • Event.parse() already uses GsonEvent.getInstance()
    • Event.query() will now use GsonEvent.getInstance()
    • Event.page() will now use GsonEvent.getInstance()
    • Event.get() will now use GsonEvent.getInstance()

Comparison with PR #117

PR #117 proposes adding a JsonObject log field to the base Event class. While this provides access to the log data, it has some trade-offs:

Aspect PR #117 This Fix
Approach Adds JsonObject log to base Event class Fixes deserialization in Deserializer
Type Safety Raw JsonObject requires manual parsing Maintains strong typing (Boleto.Log, Invoice.Log)
Usage event.log.get("type").getAsString() boletoEvent.log.type
Breaking Changes May affect existing code checking log == null None

Both approaches could be combined if desired, but this fix alone resolves the core issue while preserving the existing typed API.


Expected Behavior After Fix

Event.Page page = Event.page();
for (Event event : page.events) {
    if (event instanceof Event.BoletoEvent) {
        Event.BoletoEvent boletoEvent = (Event.BoletoEvent) event;
        System.out.println(boletoEvent.log.type);      // "paid"
        System.out.println(boletoEvent.log.boleto.id); // "5384248229888000"
    }
}

Thank you for considering this fix. Please let me know if you have any questions or need further clarification.

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.

3 participants