hasura / go-graphql-client

Package graphql provides a GraphQL client implementation.
MIT License
405 stars 94 forks source link

Can't use pointers for GQL fragments #91

Open austingandy opened 1 year ago

austingandy commented 1 year ago

I'm writing a query that involves querying a type union. Here are the relevant gql typedefs:

type SellingPlanPriceAdjustment {
    adjustmentValue: SellingPlanPriceAdjustmentValue!
    orderCount: Int
}

union SellingPlanPriceAdjustmentValue = SellingPlanFixedAmountPriceAdjustment | SellingPlanFixedPriceAdjustment | SellingPlanPercentagePriceAdjustment

type SellingPlanFixedAmountPriceAdjustment {
    adjustmentAmount: MoneyV2!
}

type SellingPlanFixedPriceAdjustment {
    price: MoneyV2!
}

type SellingPlanPercentagePriceAdjustment {
    adjustmentPercentage: Int!
}

type MoneyV2 {
    amount: Decimal!
    currencyCode: CurrencyCode!
}

My query to get back the price adjustment on a selling plan is as follows:

adjustmentValue {
    ... on SellingPlanFixedAmountPriceAdjustment {
        adjustmentAmount {
            amount
            currencyCode
        }
    }
    ... on SellingPlanFixedPriceAdjustment {
        price {
            amount
            currencyCode
        }
    }
    ... on SellingPlanPercentagePriceAdjustment {
        adjustmentPercentage
    }
}

When the query gets run, I end up with a response like:

"adjustmentValue": {
  "adjustmentPercentage": 5
}

I would like for my go structs for this query to look something along the lines of:

type AdjustmentValue struct {
    PercentageAdjustment  *PercentageAdjustment  `graphql:"... on SellingPlanPercentagePriceAdjustment"`
    FixedAmountAdjustment *FixedAmountAdjustment `graphql:"... on SellingPlanFixedAmountPriceAdjustment"`
    FixedPriceAdjustment  *FixedPriceAdjustment  `graphql:"... on SellingPlanFixedPriceAdjustment"`
}

type PercentageAdjustment struct {
    AdjustmentPercentage int
}

type FixedAmountAdjustment struct {
    AdjustmentAmount Money
}

type FixedPriceAdjustment struct {
    Price Money
}

type Money struct {
    Amount       graphql.Float
    CurrencyCode string
}

Unfortunately, using pointers for the fields in the AdjustmentValue struct results in an error:

struct field for "adjustmentPercentage" doesn't exist in any of 2 places to unmarshal

I suspect that this has something to do with which fields end up getting added to the stack on d.vs in the decoder.decode() func when iterating through the frontier, but it's not totally clear to me what specifically needs to change in order to remediate this.

Here's a test case for the above:

func TestUnmarshalGraphQL_pointerForInlineFragment(t *testing.T) {
    type user struct {
        DatabaseID uint64
    }
    type actor struct {
        User *user `graphql:"... on User"`
        Login string
    }
    type query struct {
        Author actor
        Editor *actor
    }
    var got query
    err := jsonutil.UnmarshalGraphQL([]byte(`{
        "author": {
            "databaseId": 1,
            "login": "test1"
        },
        "editor": {
            "databaseId": 2,
            "login": "test2"
        }
    }`), &got)
    if err != nil {
        t.Fatal(err)
    }
    var want query
    want.Author = actor{
        User:  struct{ DatabaseID uint64 }{1},
        Login: "test1",
    }
    want.Editor = &actor{
        User:  struct{ DatabaseID uint64 }{2},
        Login: "test2",
    }

    if !reflect.DeepEqual(got, want) {
        t.Error("not equal")
    }
}

I'll add -- if this is expected behavior and not something to fix, I'd love to see the constraint reflected a bit more clearly in the gql fragment section of the docs. As-is, I'm not sure it's particularly clear that we should expect for this situation to be unsupported.