Interface code generation breaks serialization for nested objects
Open
Nobody has claimed this yet.
- Dominant language
- Kotlin
- Stars
- 217
- Forks
- 116
- PR merge metrics
- No merged PRs in 30d
Description
generateInterfaces = true
with a schema type like:
extend type Query {
"""
Returns a `ShipmentRating` resource by ID.
"""
shipmentRating(input: ID!): ShipmentRating
"""
Returns a list of `ShipmentRating` resources with the given filters.
"""
shipmentRatings(input: ShipmentRatingsInput): [ShipmentRating]
}
extend type Mutation {
"""
Allows an API consumer to create a `shipmentRating`.
"""
createShipmentRating(input: CreateShipmentRatingInput!): ShipmentRating
"""
Allows an API consumer to calculate possible shipment ratings based on the organization's configured settings.
"""
calculateShipmentRatings(input: CalculateShipmentRatingsInput!): [ShipmentRating]
}
enum ShipmentAmountType {
BUFFER
DISCOUNT
FUEL_SURCHARGE
INSURANCE
PUBLISHED_RATE
SURCHARGE
}
"""
A Shipment rating quote that can be displayed to
"""
type ShipmentRating implements Node {
"ShipmentRating ID, prefixed with `shipmentRating_`"
id: ID!
"The quoted amount for the `ShipmentRating`"
amount: Decimal!
"Subtotal amounts of how the `ShipmentRating` amount was calculated"
amountSubtotals: ShipmentRatingSubtotals!
"The currencyCode of the amount fields."
currencyCode: CurrencyCode!
"When this `ShipmentRating` was created"
createdAt: DateTime!
"The user who created the `ShipmentRating`"
createdBy: ID!
"Breakdown of the details returned from the carrier."
details: [ShipmentRatingDetail!]
"The customer facing display name of the `ShipmentRating`."
displayName: String!
"The ISO-8601 timestamp of when the delivery could first be delivered"
minTransitAt: DateTime
"The ISO-8601 timestamp of when the delivery will be delivered"
maxTransitAt: DateTime
"""
For shipments that contain multiple fulfillment warehouses the multipleShipFromRatings will contain the individual `ShipmentRating` calculations.
The aggregated totals will be reflected on the parent object. In these cases the shipFrom location will be null.
"""
multipleShipFromRatings: [ShipmentRating]
"The `organization` associated with the `ShipmentRating`"
organization: ID!
"A carrier service level code to identify how to fulfill the shipment"
serviceLevelCode: String!
"The `location` associated with the origin of the shipment."
shipFrom: Location
"The destination `location` used to generate the shipment Quote"
shipTo: Location!
"The Carton data included in the `ShipmentRating`"
shipmentRatingCartons: [ShipmentRatingCarton!]!
"The `ShippingProfile` associated with the `ShipmentRating`"
shippingProfile: ShippingProfile!
"When this `ShipmentRating` was most recently updated."
updatedAt: DateTime!
"The user who most recently updated the `ShipmentRating`."
updatedBy: ID!
}
"""
The Carton details containing the package dimensions and items included in the `ShipmentRating`
"""
type ShipmentRatingCarton {
"The total amount that the package is insured based in the currencyCode from the `ShipmentRating`."
amountInsured: Decimal!
"The Carton associated with the `ShipmentRatingCarton`."
carton: Carton!
"The weight the carrier is charging for this carton. This could be actual or dimensional weight of the carton."
chargeableWeight: Decimal
"The Dimensional weight factor used to determine the dimensionalWeight."
dimensionalFactor: Int
"The calculated dimensionalWeight of the carton."
dimensionalWeight: Decimal
}
"""
A surcharge or discount breakdown provided by the carrier.
"""
type ShipmentRatingDetail {
"Amount for each charge as defined by the `carrier`."
amount: Decimal!
"Unique identifier that will be tied to each fee that is charged by the carrier for the `shipmentRating`."
carrierCode: String!
"The type of fee that is being broken down as part of the `shipmentRating`."
type: ShipmentAmountType!
}
"""
Subtotal amounts of how the `ShipmentRating` amount was calculated
"""
type ShipmentRatingSubtotals {
"Amount charged for the fuel surcharge by the `carrier`."
fuelSurcharge: Decimal
"Cost to insure items that is charged by the `carrier`."
insuranceCost: Decimal
"The sum of any other surcharges that are not individually broken down by the `carrier` (residential falls into this bucked)."
otherSurcharge: Decimal
"Cost of shipping as defined by the `carrier`."
shipping: Decimal!
}
"""
Input to create a non-calculated shipmentRating.
"""
input CreateShipmentRatingInput {
amount: Decimal
cartons: [ID!]!
serviceLevelCode: String!
}
"""
Input to calculate a `shipmentRating` for the given Order.
"""
input CalculateShipmentRatingsInput {
order: ID!
}
input ShipmentRatingsInput {
countryCode: CountryCode
organization: ID
serviceLevel: ID
}
type Root @key(fields: "id") @extends {
id: ID! @external
shipmentRatings: [ShipmentRating]
}
type Location @key(fields: "id") @extends {
id: ID! @external
}
type Carton @key(fields: "id")
@extends
{
id: ID! @external
}
and a hibernate entity like this:
package com.zonos.shipmentrating.shipmentRating.model;
import com.zonos.shipmentratingclient.types.IShipmentRatingCarton;
import lombok.*;
import javax.persistence.Entity;
import java.math.BigDecimal;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
public class ShipmentRatingCartonEntity {
private BigDecimal amountInsured;
private CartonEntity carton;
private BigDecimal chargeableWeight;
private Integer dimensionalFactor;
private BigDecimal dimensionalWeight;
}
fails to serialize with a DgsQueryExecutor call like this:
@Test
void getShipmentRatingTest() throws InvocationTargetException, IllegalAccessException {
// test retrieving a shipment rating by ID
ShipmentRating shipmentRating = createShipmentRating();
var projection = new ShipmentRatingProjectionRoot();
projection.id()
.amount()
.serviceLevelCode()
.shipmentRatingCartons()
.amountInsured();
GraphQLQueryRequest request = new GraphQLQueryRequest(
new ShipmentRatingGraphQLQuery.Builder()
.input(shipmentRating.getId())
.build(),
projection,
getZonosScalars()
);
ShipmentRating retrievedShipmentRating = dgsQueryExecutor.executeAndExtractJsonPathAsObject(request.serialize(),
"data." + new ShipmentRatingGraphQLQuery().getOperationName(),
new TypeRef<List<ItemEntity>>() {
});
assertNotNull(retrievedShipmentRating);
assertTrue(retrievedShipmentRating.getId().equals(shipmentRating.getId()));
assertTrue(retrievedShipmentRating.getShipmentRatingCartons().size() == 1);
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the failure in the DgsQueryExecutor test shown in the issue, using the ShipmentRating schema and ShipmentRatingCartonEntity. Trace the generated interface and nested ShipmentRatingCarton serialization for the amountInsured projection. Done means the nested object serializes successfully and the test can extract the shipment rating with its carton data.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- graphql, java, kotlin
- Domain
- api, backend, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100