Netflix / Netflix/dgs-codegen

Parsing of response to generated query to generated type fails when encountering an enum

Open
#348 0 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

I use the schema found here (also attached as txt)
schema.txt

Using codegen, I generate this request:

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:

{
  "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"
                            }
                          } ]
                        }
                      }
                    } ]
                  }
                }
              } ]
            }
          }
        } ]
      }
    }
  }
}

I then attempt to parse the response to the proper generated type

GraphQLResponse graphQLResponse =
                graphQLClient.executeQuery(graphQLQueryRequest.serialize());

        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);

then I get this

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class com.attentivemobile.syncprocessor.model.bigcommerce.types.ProductAvailability]: missing type id property '__typename' (for POJO property 'availabilityV2')
 at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: com.attentivemobile.syncprocessor.model.bigcommerce.types.Site["products"]->com.attentivemobile.syncprocessor.model.bigcommerce.types.ProductConnection["edges"]->java.util.ArrayList[0]->com.attentivemobile.syncprocessor.model.bigcommerce.types.ProductEdge["node"]->com.attentivemobile.syncprocessor.model.bigcommerce.types.Product["availabilityV2"])

I'm using the following 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>
                                <PageInfo>
                                    com.attentivemobile.syncprocessor.model.bigcommerce.GraphQLConnection.PageInfo
                                </PageInfo>
                            </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>

The PageInfo mapping is a circumvention of this issue

The versions of 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 by examining the attached schema.txt and reproducing code generation with the GraphQL query and response shown in the issue. Trace the response parsing path for the enum value; done means the generated type can parse this response without failure, with a regression test covering the case.

Written by the indexing model from the issue text.

Assessment

Tech stack
graphql, kotlin
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.