Netflix / Netflix/dgs-codegen

Parsing of PageInfo fails (using generated types)

Open
#347 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Kotlin
Stars
217
Forks
116
PR merge metrics
No merged PRs in 30d

Description

Using the schema found here (also attached as txt)
schema.txt
I've generated a query API.

I make the following query (generated by the query API):

query {
  site {
    settings {
      url {
        vanityUrl
      }
    }
    products(first: 10) {
      pageInfo {
        hasNextPage
        endCursor
      }
      edges {
        node {
          entityId
          name
          plainTextDescription(characterLimit: 1024)
          brand {
            name
          }
          availabilityV2 {
            status
          }
          inventory {
            hasVariantInventory
            aggregated {
              availableToSell
            }
          }
          path
          categories(first: 10) {
            pageInfo {
              hasNextPage
              endCursor
            }
            edges {
              node {
                name
              }
            }
          }
          images(first: 50) {
            pageInfo {
              hasNextPage
              endCursor
            }
            edges {
              node {
                urlOriginal
                altText
              }
            }
          }
          prices {
            price {
              value
              currencyCode
            }
            basePrice {
              value
              currencyCode
            }
            salePrice {
              value
              currencyCode
            }
          }
          productOptions(first: 3) {
            pageInfo {
              hasNextPage
              endCursor
            }
            edges {
              node {
                displayName
                ... on MultipleChoiceOption {
                  __typename
                  values {
                    pageInfo {
                      hasNextPage
                      endCursor
                    }
                    edges {
                      node {
                        label
                      }
                    }
                  }
                }
              }
            }
          }
          variants(first: 200) {
            pageInfo {
              hasNextPage
              endCursor
            }
            edges {
              node {
                inventory {
                  aggregated {
                    availableToSell
                  }
                }
                entityId
                productOptions(first: 3) {
                  pageInfo {
                    hasNextPage
                    endCursor
                  }
                  edges {
                    node {
                      entityId
                      displayName
                      ... on MultipleChoiceOption {
                        __typename
                        values {
                          pageInfo {
                            hasNextPage
                            endCursor
                          }
                          edges {
                            node {
                              label
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

I get this response back:

{
 "data" : {
   "site" : {
     "settings" : {
       "url" : {
         "vanityUrl" : "https://shop.soapboxus.com"
       }
     },
     "products" : {
       "pageInfo" : {
         "hasNextPage" : false,
         "endCursor" : "YXJyYXljb25uZWN0aW9uOjA="
       },
       "edges" : [ {
         "node" : {
           "entityId" : 175,
           "name" : "Tide ReLoad Compatible Detergent",
           "plainTextDescription" : "\n\nThis Box Contains\nOne Tide ReLoad™ tray and Detergent\nTide ReLoad™ tray works with multiple types of detergent, shipped directly from us with free shipping. Just choose the type of Tide you like best – Tide PODS Original, Tide PODS Spring Meadow, or Tide Eco-Box Original. Please note that Tide ReLoad™ tray will not work with Tide products purchased elsewhere.\n",
           "brand" : null,
           "availabilityV2" : {
             "status" : "Available"
           },
           "inventory" : {
             "hasVariantInventory" : false,
             "aggregated" : null
           },
           "path" : "/tide-smart-tray-detergent/",
           "categories" : {
             "pageInfo" : {
               "hasNextPage" : false,
               "endCursor" : null
             },
             "edges" : [ ]
           },
           "images" : {
             "pageInfo" : {
               "hasNextPage" : false,
               "endCursor" : "YXJyYXljb25uZWN0aW9uOjY="
             },
             "edges" : [ {
               "node" : {
                 "urlOriginal" : "https://cdn11.bigcommerce.com/s-v1a6036467/images/stencil/original/products/175/800/Image_1_-_Delivered__03950.1633532989.jpg",
                 "altText" : ""
               }
             }, {
               "node" : {
                 "urlOriginal" : "https://cdn11.bigcommerce.com/s-v1a6036467/images/stencil/original/products/175/802/Image_2_-_Free_Shipping__53500.1633532989.jpg",
                 "altText" : ""
               }
             }, {
               "node" : {
                 "urlOriginal" : "https://cdn11.bigcommerce.com/s-v1a6036467/images/stencil/original/products/175/815/Image_3_-_Never_run_out_copy__63238.1633532989.jpg",
                 "altText" : ""
               }
             }, {
               "node" : {
                 "urlOriginal" : "https://cdn11.bigcommerce.com/s-v1a6036467/images/stencil/original/products/175/799/Image_4_-_Cancel_Anytime__26398.1633532989.jpg",
                 "altText" : ""
               }
             }, {
               "node" : {
                 "urlOriginal" : "https://cdn11.bigcommerce.com/s-v1a6036467/images/stencil/original/products/175/798/Image_5_-_Set_up__06965.1633532989.jpg",
                 "altText" : ""
               }
             }, {
               "node" : {
                 "urlOriginal" : "https://cdn11.bigcommerce.com/s-v1a6036467/images/stencil/original/products/175/803/Image_6_-_Skip_the_trip_to_the_store__76617.1633532989.jpg",
                 "altText" : ""
               }
             }, {
               "node" : {
                 "urlOriginal" : "https://cdn11.bigcommerce.com/s-v1a6036467/images/stencil/original/products/175/801/Image_7_-_Comes_with__54036.1633532989.jpg",
                 "altText" : ""
               }
             } ]
           },
           "prices" : {
             "price" : {
               "value" : 19.99,
               "currencyCode" : "USD"
             },
             "basePrice" : {
               "value" : 19.99,
               "currencyCode" : "USD"
             },
             "salePrice" : null
           },
           "productOptions" : {
             "pageInfo" : {
               "hasNextPage" : false,
               "endCursor" : "YXJyYXljb25uZWN0aW9uOjA="
             },
             "edges" : [ {
               "node" : {
                 "displayName" : "Your detergent selection",
                 "__typename" : "MultipleChoiceOption",
                 "values" : {
                   "pageInfo" : {
                     "hasNextPage" : false,
                     "endCursor" : "YXJyYXljb25uZWN0aW9uOjI="
                   },
                   "edges" : [ {
                     "node" : {
                       "label" : "Original"
                     }
                   }, {
                     "node" : {
                       "label" : "Spring Meadow"
                     }
                   }, {
                     "node" : {
                       "label" : "Tide Eco Box"
                     }
                   } ]
                 }
               }
             } ]
           },
           "variants" : {
             "pageInfo" : {
               "hasNextPage" : false,
               "endCursor" : "YXJyYXljb25uZWN0aW9uOjI="
             },
             "edges" : [ {
               "node" : {
                 "inventory" : {
                   "aggregated" : null
                 },
                 "entityId" : 170,
                 "productOptions" : {
                   "pageInfo" : {
                     "hasNextPage" : false,
                     "endCursor" : "YXJyYXljb25uZWN0aW9uOjA="
                   },
                   "edges" : [ {
                     "node" : {
                       "entityId" : 21,
                       "displayName" : "Your detergent selection",
                       "__typename" : "MultipleChoiceOption",
                       "values" : {
                         "pageInfo" : {
                           "hasNextPage" : false,
                           "endCursor" : "YXJyYXljb25uZWN0aW9uOjA="
                         },
                         "edges" : [ {
                           "node" : {
                             "label" : "Original"
                           }
                         } ]
                       }
                     }
                   } ]
                 }
               }
             }, {
               "node" : {
                 "inventory" : {
                   "aggregated" : null
                 },
                 "entityId" : 171,
                 "productOptions" : {
                   "pageInfo" : {
                     "hasNextPage" : false,
                     "endCursor" : "YXJyYXljb25uZWN0aW9uOjA="
                   },
                   "edges" : [ {
                     "node" : {
                       "entityId" : 21,
                       "displayName" : "Your detergent selection",
                       "__typename" : "MultipleChoiceOption",
                       "values" : {
                         "pageInfo" : {
                           "hasNextPage" : false,
                           "endCursor" : "YXJyYXljb25uZWN0aW9uOjA="
                         },
                         "edges" : [ {
                           "node" : {
                             "label" : "Spring Meadow"
                           }
                         } ]
                       }
                     }
                   } ]
                 }
               }
             }, {
               "node" : {
                 "inventory" : {
                   "aggregated" : null
                 },
                 "entityId" : 172,
                 "productOptions" : {
                   "pageInfo" : {
                     "hasNextPage" : false,
                     "endCursor" : "YXJyYXljb25uZWN0aW9uOjA="
                   },
                   "edges" : [ {
                     "node" : {
                       "entityId" : 21,
                       "displayName" : "Your detergent selection",
                       "__typename" : "MultipleChoiceOption",
                       "values" : {
                         "pageInfo" : {
                           "hasNextPage" : false,
                           "endCursor" : "YXJyYXljb25uZWN0aW9uOjA="
                         },
                         "edges" : [ {
                           "node" : {
                             "label" : "Tide Eco Box"
                           }
                         } ]
                       }
                     }
                   } ]
                 }
               }
             } ]
           }
         }
       } ]
     }
   }
 }
}

Then when I attempt to parse the site object into a type generated by the codegen plugin...

Optional<JsonNode> dataOptional =
                Optional.of(objectMapper.readTree(graphQLResponse.getJson()));

        System.out.println(dataOptional.get().toPrettyString());

        Optional<JsonNode> siteOptional =
                dataOptional.map(json -> json.get("data")).map(data -> data.get("site"));

        if (siteOptional.isEmpty()) {
            throw new NullPointerException();
        }

        com.attentivemobile.syncprocessor.model.bigcommerce.types.Site site =
                objectMapper.treeToValue(siteOptional.get(), Site.class);

I get this

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `graphql.relay.PageInfo` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: com.attentivemobile.syncprocessor.model.bigcommerce.types.Site["products"]->com.attentivemobile.syncprocessor.model.bigcommerce.types.ProductConnection["pageInfo"])

	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
	at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1904)
	at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)
	at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1349)
	at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:274)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:176)
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:176)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
	at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:4650)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2831)
	at com.fasterxml.jackson.databind.ObjectMapper.treeToValue(ObjectMapper.java:3295)
	at com.attentivemobile.syncprocessor.service.vendor.bigcommerce.Test.test(Test.java:78)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at ...

This is my codegen configuration, via the community maven port

<plugin>
                <groupId>io.github.deweyjose</groupId>
                <artifactId>graphqlcodegen-maven-plugin</artifactId>
                <version>1.16</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <schemaPaths>
                                <param>
                                    src/main/resources/api/bigcommerce/schema.graphql
                                </param>
                            </schemaPaths>
                            <packageName>com.attentivemobile.syncprocessor.model.bigcommerce</packageName>

                            <!--                            Required because scalars chosen by BigCommerce impose graphql-java scalar mappings-->
                            <typeMapping>
                                <BigDecimal>java.math.BigDecimal</BigDecimal>
                                <Long>java.lang.Long</Long>
                            </typeMapping>

                            <generateClient>true</generateClient>

                            <includeQueries>site</includeQueries>
                            <includeSubscriptions>[]</includeSubscriptions>
                            <includeMutations>[]</includeMutations>

                            <omitNullInputFields>true</omitNullInputFields>

                            <maxProjectionDepth>20</maxProjectionDepth>

                            <outputDir>${project.build.directory}/generated-sources/graphqlcodegen</outputDir>

                            <kotlinAllFieldsOptional>true</kotlinAllFieldsOptional>
                            <language>java</language>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

and these are the versions of the dgs libraries I'm using

<!-- https://mvnrepository.com/artifact/com.netflix.graphql.dgs.codegen/graphql-dgs-codegen-client-core -->
        <dependency>
            <groupId>com.netflix.graphql.dgs.codegen</groupId>
            <artifactId>graphql-dgs-codegen-client-core</artifactId>
            <version>5.1.16</version>
        </dependency>

        <dependency>
            <groupId>com.netflix.graphql.dgs</groupId>
            <artifactId>graphql-dgs-client</artifactId>
            <version>4.9.20</version>
        </dependency>

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the attached schema.txt and the generated query API, then reproduce the PageInfo parsing failure using the schema and response shown in the issue. Compare the generated PageInfo types with the returned pageInfo objects; done means the generated types parse the query response without failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
graphql, kotlin
Domain
api, tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.