Open jmini opened 2 weeks ago
Also I don't know if the error message SRGQLDC035010 is used somewhere else, but it would be better to say:
Cannot instantiate
WorkItemWidgetEmailParticipants
(not defined as sub-type ofWorkItemWidget
)
To help developers to understand what is going on.
The fix is simple for this error is simple.
For my example project: https://github.com/jmini/gitlab-experiments/commit/7a4edce7cb3e530983054cebd66508bf294ee468
We could theoretically ignore unknown subtypes, but it doesn't sound very safe to me, it could lead to unexpected issues, for example, if the user wrongly declares the name of the type, then the object in the response would be silently skipped. The improved exception message makes sense to me...
@t1 any opinion on this?
We could theoretically ignore unknown subtypes, but it doesn't sound very safe to me, it could lead to unexpected issues, for example, if the user wrongly declares the name of the type, then the object in the response would be silently skipped.
Yes I am also not sure about what is correct.
What is sure with the current pattern:
1) when GitLab is adding a new widget type, this breaks our client until we add something like https://github.com/jmini/gitlab-experiments/commit/7a4edce7cb3e530983054cebd66508bf294ee468
2) I had to add many empty classes just to make the Json de-serialization happy, even if in my case (at graphQL query level) I am only interested in ... on WorkItemWidgetLabels
and ... on WorkItemWidgetLinkedItems
. (I could have reused a WorkItemWidgetDummy
and mapped all the cases I am not interested in to this class, but the @JsonbSubtype
has to be complete, for each of the possible __typename
values)
Maybe the Server API design is not ideal:
The WorkItem Type (kind OBJECT) have this field:
widgets
[WorkItemWidget!] Collection of widgets that belong to the work item.
Without any filtering possibility. and the WorkItemWidget
has tons of subtypes.
Do I get this right: it would work, if you would only receive the types you are interested in? In other words: it only fails, if you actually receive a type that you don't expect? So you want to filter some types on the client side. That feels to be going against a design goals of GraphQL: the client specifies exactly what they want.
So I think it would be better (for the world, not necessarily for you at this moment ;-), if the API would allow that type of filtering on the widgets
field... something like widgets(typeFilter: ["WorkItemWidgetLabels", "WorkItemWidgetLinkedItems"])
. That would also reduce the amount of data that the server had to produce and transport to the client. I have no idea how big these things can be, but it looks like this could make a relevant difference... even without fixing the problems you have. Maybe you can suggest that to GitLab?
Despite me being reluctant to add this, I've looked at the Typesafe Client code: the change would actually be quite simple. The TypeInfo#subtype
method could return an Optional
, and readObject
method could ignore an empty value. If we actually do decide to do that, I think we should absolutely make this an opt-in with a new config option.
Do you understand or even agree with my point of view?
BTW: I completely agree on improving on the error message.
So I think it would be better (for the world, not necessarily for you at this moment ;-), if the API would allow that type of filtering on the
widgets
field... something likewidgets(typeFilter: ["WorkItemWidgetLabels", "WorkItemWidgetLinkedItems"])
.
I think we all agree on that point, but the GitLab server is what it is. Because you asked, I will open the issue in their tracker, but I am not sure they will change their design.
I will not try to defend their pattern (that seems to be very UI/frontent driven at the end), but I don't think that there is too much data transported, since only the ... on WorkItemWidgetXXXX
are contributing to the response. If you check my example query, you will see in the response:
"widgets": [
{},
{},
{},
{
... content of WorkItemWidgetLabels
},
{},
{},
{},
{},
{},
{},
{
... content of WorkItemWidgetLinkedItems
},
{},
{},
{},
{},
{},
{}
]
I agree that those {}
are noisy, but they are not generating too much data.
For me this discussion is about the deserialization at client side.
Currently we need one entry for each of the possible __typename
value that is use as key to discriminate between the subtypes.
I think we should absolutely make this an opt-in with a new config option.
For me this would go in the same direction as .allowUnexpectedResponseFields(true)
. Something you do not really need an idealistic word.
With that new config set to true
my goal would be an attempt to make the client more robust and ignore those empty {}
objects. I could even reduce my model to:
@Union
@JsonbTypeInfo(key = "__typename", value = {
@JsonbSubtype(alias = "WorkItemWidgetLabels", type = WorkItemWidgetLabels.class),
@JsonbSubtype(alias = "WorkItemWidgetLinkedItems", type = WorkItemWidgetLinkedItems.class)
})
@Name("WorkItemWidget")
public interface WorkItemWidget {
}
--> which is great, because I only need those two case.
Without this config, I have to fix the client like this https://github.com/jmini/gitlab-experiments/commit/7a4edce7cb3e530983054cebd66508bf294ee468 on each schema change (each addition of a new Widget sub-type is breaking)
But indeed, for users that have more control of the GraphQL server, this new config would not be necessary.
I can understand if you reject this idea of introducing a config. Then this would just be a known fact and users like me needs to update their model, which is fine (maybe this GitLab use-case is a corner case)
In all cases, the error message can be improved to help identifying which sub-type declaration is missing in which interface.
Of course! You're right. The "filtering" already happens (practically) via the fragments you specify; the empty {}
s are just a bit ugly.
So forget what I've said before. This is a useful feature. Do you think you could create a PR for this?
I am not yet really familiar with the code base. I would need some pointers:
io.smallrye.graphql.client.impl.typesafe.reflection.TypeInfo.subtype(String)
be changed to Optional<TypeInfo>
sorry for the delay...
- can the signature of io.smallrye.graphql.client.impl.typesafe.reflection.TypeInfo.subtype(String) be changed to Optional
yes, sure.
- where are you defining the config and reading them?
We use MP Config. As a lot of config keys depend on a logical API client name (a.k.a. configKey
) or API interface name, they like my-api/mp-graphql/url
. But I'm not sure if we want that for this, too. What do you think?
- where are the tests? And how do you extend them (is there a backend somewhere)?
The tests live in the client/tck
module. They end with Behavior
instead of Test
, which is more BDD like. Maybe UnionBeharior
is a good test class to add your test.
On the GitLab GraphQL server (no password required), the
WorkItemWidget
type (kindINTERFACE
) has multiple possible types.You can run a query like this:
https://gitlab.com/-/graphql-explorer
With the java typed client, you need to have all the sub-types declared (as empty class), even if you are only interested in two widgets.
If one is missing (as in this example) you get this stacktrace:
Setting
allowUnexpectedResponseFields
does not help here.Complete project: https://github.com/jmini/gitlab-experiments/tree/main/smallrye-graphql-client
I am not sure if something can be done about this, but I wanted to share this with you (and it might help other users).