Skip to content

[Detail Bug] ETA API deserialization drops travel times and transport type due to JSON name and enum case mismatches #64

@detail-app

Description

@detail-app

Detail Bug Report

https://app.detail.dev/org_befd6425-a158-4e24-9d4d-1e5c08769515/bugs/bug_f741e739-ab99-4728-9333-8d245496a59f

Summary

  • Context: EtaEstimate is a domain model record used to represent travel time and distance estimates returned by the Apple Maps Server API.
  • Bug: The field names expectedTravelTimeSeconds and staticTravelTimeSeconds in the EtaEstimate record do not match the corresponding field names expectedTravelTime and staticTravelTime returned by the Apple Maps Server API. Additionally, the TransportType enum fails to deserialize when the API returns uppercase values (e.g., "AUTOMOBILE") because it is configured to use toString(), which returns mixed case (e.g., "Automobile").
  • Actual vs. expected: Jackson fails to populate the travel time fields during deserialization, resulting in Optional.empty() for these values even when the API provides them. For the transport type, it also results in Optional.empty() (or null) due to the case mismatch.
  • Impact: Applications using this SDK to fetch ETAs will receive empty travel time estimates and missing transport types, making the ETA functionality effectively broken and unreliable.

Code with Bug

// src/main/java/com/williamcallahan/applemaps/domain/model/EtaEstimate.java

public record EtaEstimate(
    Optional<Location> destination,
    Optional<Long> distanceMeters,
    Optional<Long> expectedTravelTimeSeconds, // <-- BUG 🔴 Should be expectedTravelTime to match API
    Optional<Long> staticTravelTimeSeconds,   // <-- BUG 🔴 Should be staticTravelTime to match API
    Optional<TransportType> transportType
) {
// src/main/java/com/williamcallahan/applemaps/adapters/jackson/AppleMapsObjectMapperFactory.java

.enable(EnumFeature.READ_ENUMS_USING_TO_STRING) // <-- BUG 🔴 Causes mismatch if API returns "AUTOMOBILE"
// src/main/java/com/williamcallahan/applemaps/domain/model/TransportType.java

public enum TransportType {
    AUTOMOBILE("Automobile"), // toString() returns "Automobile"
    // ...
    @Override
    public String toString() {
        return apiValue;
    }
}

Explanation

  • Jackson maps record component names to JSON properties by default. Because the API returns expectedTravelTime / staticTravelTime but the model expects expectedTravelTimeSeconds / staticTravelTimeSeconds, those values are not bound and remain Optional.empty().
  • The object mapper is configured with READ_ENUMS_USING_TO_STRING, so Jackson expects enum JSON values to match TransportType.toString() (e.g., "Automobile"). The API returns uppercase values (e.g., "AUTOMOBILE"), so deserialization fails to match and the field is missing/empty.

Failing Test

// src/test/java/com/williamcallahan/applemaps/domain/model/EtaEstimateDeserializationTest.java

@Test
void deserializesFromApiJson() throws Exception {
    ObjectMapper objectMapper = AppleMapsObjectMapperFactory.create();
    // This JSON follows the actual Apple Maps Server API for ETAs
    String json = """
        {
          "destination": { "lat": 37.3346, "lng": -122.0090 },
          "distanceMeters": 5000,
          "expectedTravelTime": 600,
          "staticTravelTime": 550,
          "transportType": "AUTOMOBILE"
        }
        """;

    EtaEstimate estimate = objectMapper.readValue(json, EtaEstimate.class);

    // This assertion FAILED
    assertTrue(estimate.expectedTravelTimeSeconds().isPresent(), "expectedTravelTimeSeconds should be present");
}

Test output:
EtaEstimateDeserializationTest > deserializesFromApiJson() FAILED org.opentest4j.AssertionFailedError

Recommended Fix

Add explicit JSON mapping so the model matches the API keys (or rename the record components). For TransportType, either stop using READ_ENUMS_USING_TO_STRING or ensure toString() matches the API’s uppercase values.

public record EtaEstimate(
    Optional<Location> destination,
    Optional<Long> distanceMeters,
    @JsonAlias("expectedTravelTime") Optional<Long> expectedTravelTimeSeconds, // <-- FIX 🟢
    @JsonAlias("staticTravelTime") Optional<Long> staticTravelTimeSeconds,     // <-- FIX 🟢
    Optional<TransportType> transportType
)

History

This bug was introduced in commit 33eca2a. This commit introduced the core domain models, but used descriptive field names (like expectedTravelTimeSeconds) and mixed-case enum strings that do not match the literal JSON keys and values returned by the Apple Maps Server API.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions