My Products
Help
Jon13
CONTRIBUTOR **

Fields are cleared on update mutation

by Jon13 (Updated ‎29-05-2024 13:23 by Jon13 )

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": ""
          }
        ]
      }
    }
  }
}

 

 

 

 

 

 

14 REPLIES 14

by Marius Bancila

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

 

by Marius Bancila (Updated ‎31-05-2024 11:01 by Marius Bancila VISMA )

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?

Jon13
CONTRIBUTOR **

by Jon13

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!

by Marius Bancila

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.

omelhus
PARTNER

by omelhus

This makes sense to me.

by Marius Bancila (Updated ‎30-05-2024 12:17 by Marius Bancila VISMA )

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 "" ?

Jon13
CONTRIBUTOR **

by Jon13

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.

omelhus
PARTNER

by omelhus

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.

by Marius Bancila

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.

omelhus
PARTNER

by omelhus

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.

Jon13
CONTRIBUTOR **

by Jon13
Thanks for the reply!

 

This seems to work from the GraphiQL tool, but I am not able to get it working from code. I do use the dotnet client library. How do you serialize the entity object? I have tried to include it as a regular object, and I have tried to serialize as a string directly with the System.Text.Json serializer, but to no avail.

 

I get the error "The request could not be executed: unable to parse response data." on the request. If I afterwards retry doing a new update on the same associate in GraphiQL, I will then get the same error there.

 

This is my code:
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);

 

Do you have any suggestions?
omelhus
PARTNER

by omelhus

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.

Jon13
CONTRIBUTOR **

by Jon13 (Updated ‎30-05-2024 15:48 by Jon13 )

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.

 

omelhus
PARTNER

by omelhus

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.