1

I'm attempting to update a user's secondary information (addresses, emails, externalIds, ims, phones, organizations, relationships - the things listed on the patch page that are arrays and such). I'm successfully able to authenticate, add new information (one or more at a time) and remove one as long as it leaves one remaining (I update the JArray leaving everything but the one I want).

So, if I have 3 addresses I can remove one or two and it updates appropriately. I can easily use a patch to clear and update normal information like the first and last name, rename users and update passwords.

However, if I try to remove ALL of one type of information, nothing happens. I have tried setting the user address property to null as well as an empty List or Array. Through debugging I have verified the user patch it's sending out is accurate, but nothing seems to happen server-side.

I tried using a full Update, as well and again using null or empty lists, but the same issue happens.

What do I need to do to fully clean out one of these properties?

Example code:

//Try both a full user for an update and a blank user for a patch
User uRetrieved = directoryServiceDict[Domain].Users.Get(fullEmail).Execute();
User uPatch = new User();
uPatch.Addresses = null;
uRetrieved.Addresses = new List<Address>();
//try both a patch and update
directoryServiceDict[Domain].Users.Patch(uPatch, u.PrimaryEmail).Execute();
directoryServiceDict[Domain].Users.Update(uRetrieved, u.PrimaryEmail).Execute();

Update: I ran a series of tests, trying to pass anything I could but nothing had worked. After digging in the API source code I found that I could call the service's Serializer method to see what it's piping out, and I found that assigning it an empty string, and empty list, a list with one empty string, a new Address (for example) object and a list with an new Address object all didn't work. The serializer threw them as expected, however.

If I set it to null, though, it was ignored.

Upon reviewing the patch semantics page I see the following:

To delete a field, specify the field and set it to null. For example, "comment": null. You can also delete an entire object (if it is mutable) by setting it to null.

Looking at the given examples, they want the data to show up like {"addresses":null} and not like any of the other variations such as {"addresses":""}, {"addresses":[]} or {"addresses":[""]}.

After spending some time digging in to the API code I was able to find line 42 of the Serializer they use in which they set the serializer settings to ignore null (I assume to help keep data amounts lower for patching, for example).

This works for most things, but means when trying to Patch or Update, the serializer is ignoring the values set as null and therefore the server is not seeing that entry and won't delete it.

So, I'm working on trying to figure out the best way to fix this. There are some other posts here on StackOverflow I'm going to look at, and hopefully I'll be able to work it out (unless someone gets to it first). If I do, I'll post an answer.

Update 2: I was able to get the code to print out a serialized string including the Null by creating a new Customizer for the String type. Each time the code is run it generates a customized NullToken (basically a generated GUID that I tacked a word on to) that is set as the string value of the field I want to delete. Then when the serializer hits a string, it checks to see if the string matches the NullToken or not. If it does, I use a jsonWriter method of WriteNull(). Otherwise, I just pass it to the general WriteValue().

I then just had to customize a series of classes (the Initializer that gets passed to the Service, the NewtonsoftJsonSerializer that is part of the Initializer and the Customizer that is added to the NJS settings) and set those up to be used instead of the defaults when creating the service.

Now my code properly pumps out something like {"addresses":null} and I am happy about that.

Unfortunately it still doesn't work. I did some more testing in the API explorer and realized even though the returned object shows an update of what you put in, nothing actually is happening. I don't think it's deleting at all and therefore I have opened an issue for the API itself. If it turns out to be my own fault, I'll throw an answer here.

Summary: I'm now fairly certain the issue is in the Google API itself.

Community
  • 1
  • 1
squid808
  • 1,430
  • 2
  • 14
  • 31

4 Answers4

0
  • pass the following to the data type for example phones:

{
  "phones": "[]"
}
Ves0t
  • 1
  • 1
  • Thanks, but unfortunately anything other than null seems to not work from my end. I believe issue is the google directory api and how it serializes the data - removing the null values needed to tell the server to delete the information. I've updated the post with my past few days of findings. – squid808 Jan 07 '15 at 20:25
  • actually, have you verified that this works? I have a test user with 3 ims listed, that I get from the APIs Explorer page. If I then try to set it to `null` or `[]` (within the quotes they give), the result I get back is whatever I put in, even if it's not valid (like `[?`). If I do a Get again, nothing was updated. Do you see the same? – squid808 Jan 12 '15 at 04:34
0

I'm getting the same exact thing. Here's my code:

User queryUser = service.Users.Get("user@domain.com").Execute();

List<UserAddress> newAddresses = new List<UserAddress>();
  UserAddress newAddress = new UserAddress{};

List<UserRelation> newRelations = new List<UserRelation>();
  UserRelation newRelation = new UserRelation{};

newAddresses.Add(newAddress);
newRelations.Add(newRelation);

queryUser.Addresses = newAddresses;
queryUser.Relations = newRelations;

User _updated = service.Users.Update(queryUser,"user@domain.com").Execute();

When I insert a break-point after the "_updated" I can see Addresses and Relations as null, so far so good. I exit the application and start it up again and double check with a "Get()" on the same user account and the nulls in Addresses and Relations have disappeared and show the following:

****Please keep in mind I had previous values in these properties and now the goal is to clear the contents as they were before I assigned values, to be null.***

Addresses = {[ { "type": "custom", "customType": "" } ]}

Relations = {[ { "type": "custom" } ]}

Still waiting from a response from Google. I have stared Issue 3701: Cannot delete user properties via Directory Admin API

If can offer a suggestion I'm willing to test it.

FredNCC
  • 51
  • 1
  • 3
  • The good news is that they've at least assigned and triaged the issue, hopefully as it gains traction it will get worked out. Thanks for the input! – squid808 Apr 11 '15 at 14:36
  • Google responded on 4/13/15: Update: There is a confirmed bug with patch not working. However, since update follows patch semantics (only modifying specified properties) it should be used instead. The correct thing to pass is null. However, some client libraries have issue with null values. For Java, you need to use Data.nullOf(type) (IIRC) to create an explicit null value. Otherwise null values are ignored during JSON serialization. Not sure what the approach is for .NET. Worst case is for those cases bypass the model classes and send the raw JSON. – FredNCC Apr 15 '15 at 17:50
0

Using the suggestion construct a raw JSON and send it; below is what I came up with. It's not work but it's also not throwing an error. Maybe this is a good start for some one and they can make it work.

At the end of the "Get()" is does not show the updated value.

string uri = "https://www.googleapis.com/admin/directory/v1/users/user@domain.com";
    var request = (HttpWebRequest) HttpWebRequest.Create(uri);
    request.Method = "PATCH";
    request.Accept = "application/json";
    request.Headers.Add("Authorization", "Bearer " + GoogleToken.getToken() );
    using(StreamWriter writer = new StreamWriter(request.GetRequestStream()))
    {
    writer.Write("{ \"addresses\": [ { null } ] }");
    writer.Flush();
    writer.Close();
    }
     
    WebResponse response = request.GetResponse();
    Stream stream = response.GetResponseStream();
    string json = "";
    using (StreamReader reader = new StreamReader(stream))
    {
    while(!reader.EndOfStream)
    {
    json += reader.ReadLine() + '\n';
    }
    }
    Debug.WriteLine("JSON: " + json);
     
    User Result = GoogleToken.GoogleService().Users.Get("user@domain.com").Execute();
    Debug.WriteLine("Application Done");
FredNCC
  • 51
  • 1
  • 3
0

passing null OK too:

{
  "phones": null
}