-
-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ability to combine multiple response projections #985
Comments
If you don't care about to get all the values, you can use |
@ruipliu if I understand correctly your problem statement, you want the ability to create a new instance of GraphQLResponseProjection out of another instance of GraphQLResponseProjection. |
@kobylynskyi Yes correct. |
Unfortunately adding So the approach of creating an additional "copy" constructor would be better, what do you think? Type1ResponseProjection type1 = new Type1ResponseProjection();
Type1ResponseProjection type1Copy = new Type1ResponseProjection(type1); |
Sorry for the late reply. For example, if we want to add Here are some alternative solutions, could you give some comments?
|
We should not modify the original |
There is no getFields() in current Graph l response projection. We are thinking of adding this methods to access and modify the Do you mean the |
we should only modify it by response projection method. If the returned by getField is mutable that will break this.
If the internal fields can be modified after creating object, it may lead to insecurity. |
Could you explain the insecurity case a bit more? about what scenario and what kind of security problem. Thanks! My feeling is the |
When a projection needs to be defined and subsequently used multiple times. Some times it may be modified, which results in multiple calls not having the same result. |
@ruipliu, Let's take your case as an example:
We could still solve it with the copy-constructor thing: ParentResponseProjection proj1 = new ParentResponseProjection()
.user(new UserResponseProjection()
.id()
.order(new OrderResponseProjection()
.total()
.product(new ProductResponseProjection()
.name()
)
)
)
ParentResponseProjection proj2 = new ParentResponseProjection(proj1) // resuing all projection from proj1
.user(new UserResponseProjection()
.order(new OrderResponseProjection()
.product(new ProductResponseProjection()
.price() // this will add price to an existing projection which already has name
)
)
)
ParentResponseProjection proj3 = new ParentResponseProjection(proj1) // resuing all projection from proj1
.user(new UserResponseProjection()
.order(new OrderResponseProjection()
.product(new ProductResponseProjection()
.none$() // this will clean up all projected fields that might already exist
.id()
)
)
)
// proj1 will have { user { id order { total product { name } } } }
// proj2 will have { user { id order { total product { name price } } } }
// proj3 will have { user { id order { total product { id } } } } How does this sound to you? |
Hi @kobylynskyi , There are two comments on your idea:
Please let me know your comment. Thanks. |
@ruipliu, thanks for your response. To your 1st point:
This should not be a problem. I think we can use a Line 13 in 7364f7d
To your 2nd point:
What do you mean by ProductRespProj minimalProductDetails = new ProductRespProj()
.id().name())
ProductRespProj extendedProductDetails = new ProductRespProj()
.id().name().price().sku())
ParentRespProj proj1WithMinimalProductDetails = new ParentRespProj()
.user(new UserRespProj()
.id()
.order(new OrderRespProj()
.total()
.product(minimalProductDetails)
)
)
ParentRespProj proj2WithExtendedProductDetails = new ParentRespProj()
.user(new UserRespProj()
.id()
.order(new OrderRespProj()
.total()
.product(extendedProductDetails)
)
) And with a "constructor" thing it will make a code cleaner by extending ParentRespProj proj2WithExtendedProductDetails = new ParentRespProj(proj1WithMinimalProductDetails)
.user(new UserRespProj()
.order(new OrderRespProj()
.product(extendedProductDetails)
)
) Do you see how we can make projections more reusable? Please provide code samples if you can. |
Hi @kobylynskyi , sorry for the late reply. I've consolidated the idea and let's take the below example: Suppose we have a GQL model
And a Java ApplicationThe Java application is designed to listen and orchestrate data loading requirements and combine a GraphQL request. And the GraphQL service is used to load data from different data storages. Consider the Java application supports two data loading requirements, which allows clients to select:
Some of the clients needs requirement A, some needs B, while some needs A + B. They could specify their requirements through a parameter when invoking the Java application. In the application, we need to maintain AccountResponseProjection projectionA = new AccountProjection().order(
new OrderResponseProjection().product(
new ProductResponseProjection().name()));
PersonNameResponseProjection personNameFragment =
new PersonNameResponseProjection().firstName().lastName();
AccountResponseProjection projectionB = new AccountProjection()
.user(new PersonResponseProjection().name(personNameFragment))
.order(new OrderResponseProjection()
.recipient(new PersonResponseProjection().name(personNameFragment)));
AccountResponseProjection projectionAB = new AccountProjection()
.user(new PersonResponseProjection().name(personNameFragment))
.order(new OrderResponseProjection()
.recipient(new PersonResponseProjection().name(personNameFragment))
.product(new ProductResponseProjection().name())); Consider if we have tens of requirements, we should list out all combinations of requirements to make GQL call, which creates quite a lot of redundant codes. Two possible options that could both make the response projection reusable and keep it immutable:Option 1: Make constructor accept list of response projectionsAccountResponseProjection projectionAB = new AccountProjection(Arrays.asList(projectionA, projectionB)); Option 2: Factory method to build response projectionsAccountResponseProjection projectionAB = AccountResponseProjectionFactory.combine(projectionA, projectionB);
Tree iteration and field combination logic have to be implemented.No matter which option we take, we need to add response projection combining logic.
Advantage: The logic is independent to the existing response projection code, so it would not have any impact on the existing code base. Challenge: The combination logic should be written in generic way and apply to the auto-generated projection classes. @kobylynskyi Is my explanation clear to understand? Any comments on this approach? |
@ruipliu I like the approach with combining multiple projections! |
@kobylynskyi |
Yes, I am planning to work on this in the nearest days. Will keep you posted. |
@ruipliu, plz check the initial version of the "combining" logic. I am open to your suggestions on how to set alias / input parameters if provided values in the incoming projection objects are different. For example: Also, TBD for this feature: Kotlin and Scala support. |
@kobylynskyi Thanks. I checked the code change. From my knowledge, GraphQL service could support duplicate field with different alias. For example, this query is supported, as the alias are different: query {
query: {
ids1: id (from: 1 to: 10)
ids2: id (from: 11 to: 20)
}
} The result would be However, I'm not too sure whether field with the same alias and different parameters is supported by GraphQL service, but it is definitely a bad practice. So my conclusion is:
Possible solution
|
Thanks for the heads-up. |
Will include the feature in the upcoming release. Stay tuned |
Discussed in #984
Originally posted by ruipliu July 22, 2022
This is to support complex use cases from client side building queries.
Now the response projections only have builder methods to add fields. But in client side there are certain business needs to dynamically build queries. The current client practice is creating the response projections and adding the fields in one time, which is hard coding. Could the plugin support
getFields()
inGraphQLResponseProjection
for clients to access thefields
in response projection after they created?The text was updated successfully, but these errors were encountered: