to get a personalized navigation.
to get a personalized navigation.
Has there been a recent breaking change on how graphql-variables are used when updating fields?
When calling an update mutation on a record, the fields that are included in the field list in the mutation but are not included in the variables, will be cleared on the record. Previously these fields were ignored in the update, and they would still contain the original value from before the mutation.
For the last couple of months we have successfully used an update method that allows us to update over 20 fields on the Customer record. Recently we noticed that many of the fields in our records were blank. We eventually found the cause of this was the update method. For example, if the method was called to update one single field, all the other fields in the update mutation were cleared. Our first record of this happening was on 16.05. On 15.05 it was working correctly. We have not changed our code during this period.
Example of a query for getting customer info:
query GetCustomer(
$companyID: Int!,
$customerNo: Int!
)
{
useCompany(no: $companyID) {
associate(filter: { customerNo: { _eq: $customerNo } }) {
items {
customerNo
phone
mobilePhone
privatePhone
}
}
}
}
If we call the query with the following variables:
{
"companyID" : 4000000,
"customerNo": 11000
}
we get the result:
{
"data": {
"useCompany": {
"associate": {
"items": [
{
"customerNo": 11000,
"phone": "90000000",
"mobilePhone": "91000000",
"privatePhone": "92000000"
}
]
}
}
}
}
Then, if we call the following mutation:
mutation UpdateCompany(
$companyID: Int!
$customerNo: Int!,
$phone: String,
$mobilePhone: String,
$privatePhone: String,
)
{
useCompany(no: $companyID) {
associate_update(
filter:
{
customerNo: { _eq: $customerNo }
},
value:
{
phone: $phone
mobilePhone: $mobilePhone
privatePhone: $privatePhone
}
)
{ affectedRows }
}
}
with these variables (mobilePhone and privatePhone are not included):
{
"companyID" : 4000000,
"customerNo": 11000,
"phone": "94000000"
}
the three values will all be updated. The phone field will be updated with the new phone number. The mobilePhone and privatePhone filed will be set to blank. Previously these two fields would not be updated. They would still have the same value that they contained before the mutation.
When querying again the same customer, we get the result:
{
"data": {
"useCompany": {
"associate": {
"items": [
{
"customerNo": 11000,
"phone": "94000000",
"mobilePhone": "",
"privatePhone": ""
}
]
}
}
}
}
We have made some changes to that fields that are assigned from unset variables are ignored.
https://docs.business.visma.net/docs/schema/mutations/inserts#assigning-fields-from-unset-variables
I can change the implementation so that fields assigned from unset variables are ignored.
In your example, you set phone, mobilePhone, and privatePhone:
mutation UpdateCompany(
$companyID: Int!
$customerNo: Int!,
$phone: String,
$mobilePhone: String,
$privatePhone: String,
)
{
useCompany(no: $companyID) {
associate_update(
filter:
{
customerNo: { _eq: $customerNo }
},
value:
{
phone: $phone
mobilePhone: $mobilePhone
privatePhone: $privatePhone
}
)
{ affectedRows }
}
}
But only set $phone in the variables:
{
"companyID" : 4000000,
"customerNo": 11000,
"phone": "94000000"
}
We can identify variables that are not given a value, and then leave out the fields assigned through them. The query would be treated by our service as it was written like this:
mutation UpdateCompany(
$companyID: Int!
$customerNo: Int!,
$phone: String
)
{
useCompany(no: $companyID) {
associate_update(
filter:
{
customerNo: { _eq: $customerNo }
},
value:
{
phone: $phone
}
)
{ affectedRows }
}
}
Is this what you would want?
There is no need for to revert the functionality for us. I have created a fix on our update function. I changed the mutation to pass an entity object instead of single field values, as described by omelhus in a previous post. Thanks for following it up anyway!
I'd want our API to be as simple and usable as possible. But building APIs is hard. There is always something that people want different. We are trying to meet the needs but it's not always possible. Sometimes bugs become features, sometimes unintentional breaking changes happen. Changing the functionality as described is possible and not very complicated. I'm only trying to figure out if it makes more sense this way or the way it works now.
This makes sense to me.
I see you are defining some parameters but do not supply values for them.
You are explicitly asking for the update of 3 fields:
value: {
phone: $phone
mobilePhone: $mobilePhone
privatePhone: $privatePhone
}
Why do you expect these would not be set?
Parameters/variables without a supplied value take the default value of their types. For string, it's empty. Now that I'm thinking about it, I'd argue that it didn't work as it supposed to previously.
How should we make the different between a sting variable without a supplied value which is seen as an empty string and a string variable set to an empty string "" ?
Thank you for the reply. When I developed the function I used the GraphiQL tool to prototype the mutation. Excluding the variables caused them to be ignored, so I assumed it was a viable way of handling updates. I am quite sure that previously when a variable was included, and the value was set to empty (like phone: "") the field was cleared. So I believe there was a difference between excluding them and including with empty content.
I want to avoid doing direct string manipulation of mutations and queries in the code, and only keep them as static strings. Then I can use only variables to update the entities as they are provided in the GraphQL client library. This makes for much cleaner code with a lower risk for bugs. This was the only way I was able to do it.
I think the bigger issue is that this has been changed without any notice, no release notes, no heads up for integrations to change. We only notice when the integrations blow up after a release has been done, without notice.
I understand that this is a problem because it's a breaking change. I want to mention that this has not been done on purpose, although we did not realize the change in behavior. To be honest, we didn't have a test where fields are set in an insert/update operation from a variable that is not set. I'll make sure to include that now.
Hey,
This is probably in regards to Visma moving away from VBS - a change we were suppose to not notice at all.
Your mutation is updating with empty values. If you want to maybe update some fields, you should have the whole entity as a parameter instead. At least that's your workaround until Visma (maybe) fixes this.
mutation UpdateCompany($companyID: Int!, $customerNo: Int!, $entity: Associate_Input!) {
useCompany(no: $companyID) {
associate_update(filter: {customerNo: {_eq: $customerNo}}, value: $entity) {
affectedRows
}
}
}
{
"companyID" : 4000000,
"customerNo": 11000,
"entity": {
"phone": "94000000"
}
}
The drawback here is that if you are using dotnet you need to make sure that your serializer is set to not include NULL-values, or you'll be in for a surprise with suggested values.
var graphQLSerializer = new SystemTextJsonSerializer(o => o.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull);
var graphQLClient = new GraphQLHttpClient(_configuration.GraphQLEnpoint, graphQLSerializer, pollyEnabledClient);
graphQLClient.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessTokenResult.Value);
var request = new GraphQLRequest
{
Query = VismaNXTGraphQLMutations.UpdateCustomerWithEntity,
Variables = new
{
siteUpdate.companyID,
siteUpdate.customerNo,
entity = siteUpdate.associateUpdates
}
};
var graphQLResponse = await graphQLClient.SendMutationAsync<UpdateCompanyResponse>(request, cancellationToken);
Please include the visma trace id for this response, as this is bug that I haven't been able to reproduce, but have encountered myself. This is probably an issue somewhere between vbus an graphql, and must be investigated by Visma.
Now the request was handled. I did a new test with a new associate, to try to generate a trace id, but somehow the request was handled and the company was updated. Still some fields were blanked though.
Here is a trace-id from a previous failed update:
"vbnxt-trace-id": "0000000000000000842e01f26fd1bc9e"
Looks like that specific error was transient.
Good! Visma added some more logging after I encountered this, so now maybe they’ll be able to figure it out.
The update is successful even though the response says otherwise.
Copyright © 2022 Visma.com. All rights reserved.