ChilliCream / graphql-platform

Welcome to the home of the Hot Chocolate GraphQL server for .NET, the Strawberry Shake GraphQL client for .NET and Banana Cake Pop the awesome Monaco based GraphQL IDE.
https://chillicream.com
MIT License
5.26k stars 746 forks source link

Generated query response is empty #6285

Open kpiekara opened 1 year ago

kpiekara commented 1 year ago

Is there an existing issue for this?

Product

Strawberry Shake

Describe the bug

Based on Demo project from docs I prepared project pointing to my endpoint and using my query. I'm using .NET 7.

I had to remove JSON scalar from schema because of other bug: https://github.com/ChilliCream/graphql-platform/issues/5899 to make it work.

Repository: https://github.com/kpiekara/crystallize-example/blob/main/Demo

Schema: https://github.com/kpiekara/crystallize-example/blob/main/Demo/schema.graphql

Query: https://github.com/kpiekara/crystallize-example/blob/main/Demo/GetItem.graphql Most interesting part:

query GetItem ($language: String!, $path: String!) {
    catalogue(language: $language, path: $path) {
        ...on Product {
            ...product
            topics {
                path
                name
            }
        }
    }
}

fragment product on Product {
    id
    name
    type
    language
    path

    components {
        ...component
    }

    variants {
        name
        sku
        components {
            ...component
        }
        price
        priceVariants {
            identifier
            name
            price
            currency
        }
        stockLocations {
            identifier
            name
            stock
        }
        isDefault
        images {
            url
            altText
            key

            variants {
                key
                width
                url
            }
        }

        subscriptionPlans {
            identifier
            name
            periods {
                id
                name
                initial {
                    priceVariants {
                        identifier
                        name
                        price
                        currency
                    }
                }
                recurring {
                    priceVariants {
                        identifier
                        name
                        price
                        currency
                    }
                }
            }
        }
    }

    vatType {
        name
        percent
    }
}

Generated client:

[global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "13.2.1.0")]
public partial interface IGetItemResult
{
    public global::Demo.IGetItem_Catalogue? Catalogue { get; }
}

[global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "13.2.1.0")]
public partial interface IGetItem_Catalogue
{
}

[global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "13.2.1.0")]
public partial interface IGetItem_Catalogue_Document : IGetItem_Catalogue
{
}

[global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "13.2.1.0")]
public partial interface IGetItem_Catalogue_Folder : IGetItem_Catalogue
{
}

[global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "13.2.1.0")]
public partial interface IGetItem_Catalogue_Product : IGetItem_Catalogue
{
    public global::System.Collections.Generic.IReadOnlyList<global::Demo.IGetItem_Catalogue_Topics>? Topics { get; }
}

For code:

var client = services.GetRequiredService<Icatalogue>();
var result = await client.GetItem.ExecuteAsync("", "");
result.EnsureNoErrors();
result.Data.Catalogue.

Catalogue has no properties.

var client = services.GetRequiredService<Icatalogue>();
var result = await client.GetItem.ExecuteAsync("", "");
result.EnsureNoErrors();
(result.Data.Catalogue as IGetItem_Catalogue_Product).

Catalogue has only 'Topics' property.

Steps to reproduce

  1. Clone repository
  2. Build

Relevant log output

No response

Additional Context?

No response

Version

13.2.1

jarlef commented 1 year ago

Why are you calling "await client.GetItem.ExecuteAsync("", "");" without specifing the language and path parameters?

kpiekara commented 1 year ago

@jarlef hi, does it matter? Parameters will be relevant during runtime and issue is about code generation.

Issue is that generated response type IGetItem_Catalogue doesn't have properties which should have. Issue is not empty response object during runtime. I'm calling await client.GetItem.ExecuteAsync("", ""); only to get access to response object in Program.cs example to check what properties it have. Properties as class, not properties as values in instance.

If I fill arguments in mentioned line, class IGetItem_Catalogue will not get new properties in class definition.

jarlef commented 1 year ago

Ah. I misunderstood the issue

One way to resolve this is to replace this

query GetItem ($language: String!, $path: String!) {
    catalogue(language: $language, path: $path) {
        ...on Product {
            ...product
            topics {
                path
                name
            }
        }
    }
}

with

query GetItem ($language: String!, $path: String!) {
    catalogue(language: $language, path: $path) {
             ...product
            topics {
                path
                name
            }

    }
}
 [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "13.2.1.0")]
    public partial class GetItem_Catalogue_Product : global::System.IEquatable<GetItem_Catalogue_Product>, IGetItem_Catalogue_Product
    {
        public GetItem_Catalogue_Product(global::System.String id, global::System.String? name, global::System.String? type, global::System.String? language, global::System.String? path, global::System.Collections.Generic.IReadOnlyList<global::Demo.IGetItem_Catalogue_Components>? components, global::System.Collections.Generic.IReadOnlyList<global::Demo.IGetItem_Catalogue_Variants>? variants, global::Demo.IGetItem_Catalogue_VatType? vatType, global::System.Collections.Generic.IReadOnlyList<global::Demo.IGetItem_Catalogue_Topics>? topics)
        {
            Id = id;
            Name = name;
            Type = type;
            Language = language;
            Path = path;
            Components = components;
            Variants = variants;
            VatType = vatType;
            Topics = topics;
        }

        public global::System.String Id { get; }

        public global::System.String? Name { get; }

        public global::System.String? Type { get; }

        public global::System.String? Language { get; }

        public global::System.String? Path { get; }

        public global::System.Collections.Generic.IReadOnlyList<global::Demo.IGetItem_Catalogue_Components>? Components { get; }

        public global::System.Collections.Generic.IReadOnlyList<global::Demo.IGetItem_Catalogue_Variants>? Variants { get; }

        public global::Demo.IGetItem_Catalogue_VatType? VatType { get; }

        public global::System.Collections.Generic.IReadOnlyList<global::Demo.IGetItem_Catalogue_Topics>? Topics { get; }

        public virtual global::System.Boolean Equals(GetItem_Catalogue_Product? other)
        {
            if (ReferenceEquals(null, other))
            {
                return false;
            }

            if (ReferenceEquals(this, other))
            {
                return true;
            }

            if (other.GetType() != GetType())
            {
                return false;
            }

            return (Id.Equals(other.Id)) && ((Name is null && other.Name is null) || Name != null && Name.Equals(other.Name)) && ((Type is null && other.Type is null) || Type != null && Type.Equals(other.Type)) && ((Language is null && other.Language is null) || Language != null && Language.Equals(other.Language)) && ((Path is null && other.Path is null) || Path != null && Path.Equals(other.Path)) && global::StrawberryShake.Internal.ComparisonHelper.SequenceEqual(Components, other.Components) && global::StrawberryShake.Internal.ComparisonHelper.SequenceEqual(Variants, other.Variants) && ((VatType is null && other.VatType is null) || VatType != null && VatType.Equals(other.VatType)) && global::StrawberryShake.Internal.ComparisonHelper.SequenceEqual(Topics, other.Topics);
        }

        public override global::System.Boolean Equals(global::System.Object? obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }

            if (ReferenceEquals(this, obj))
            {
                return true;
            }

            if (obj.GetType() != GetType())
            {
                return false;
            }

            return Equals((GetItem_Catalogue_Product)obj);
        }

        public override global::System.Int32 GetHashCode()
        {
            unchecked
            {
                int hash = 5;
                hash ^= 397 * Id.GetHashCode();
                if (Name != null)
                {
                    hash ^= 397 * Name.GetHashCode();
                }

                if (Type != null)
                {
                    hash ^= 397 * Type.GetHashCode();
                }

                if (Language != null)
                {
                    hash ^= 397 * Language.GetHashCode();
                }

                if (Path != null)
                {
                    hash ^= 397 * Path.GetHashCode();
                }

                if (Components != null)
                {
                    foreach (var Components_elm in Components)
                    {
                        hash ^= 397 * Components_elm.GetHashCode();
                    }
                }

                if (Variants != null)
                {
                    foreach (var Variants_elm in Variants)
                    {
                        hash ^= 397 * Variants_elm.GetHashCode();
                    }
                }

                if (VatType != null)
                {
                    hash ^= 397 * VatType.GetHashCode();
                }

                if (Topics != null)
                {
                    foreach (var Topics_elm in Topics)
                    {
                        hash ^= 397 * Topics_elm.GetHashCode();
                    }
                }

                return hash;
            }
        }
    }
kpiekara commented 1 year ago

That is amazing! I will check this in a moment.

So there is something wrong with this syntax or it is just not supported? Query was generated by external system.

kpiekara commented 1 year ago

I checked and can confirm it works! As I understand this is workaround? Should I close issue since it is resolved for me?

What would happen if I will have a query in future, which will be able to return two types there? This solution will not work?