Rollback using the .NET SDK

Version 1
    This document was generated from CDN thread

    Created by: Stephan Steiner on 09-10-2013 08:59:32 AM
    I'm using the CUPI .NET SDK as part of my provisioning solution for the entire UC range. My top level operations - add, update, delete - are meant to be atomic, so either an update works or it doesn't... anything in the middle should be rolled back. So I follow the "extract an object, clone it, make necessary changes, send update" approach, and rollback for every passed operation would then simply perform another update but sending the cloned (unmodified object).
    Unfortunately, that approach won't work with Jeff's class library - objects are not datacontracts, not serializable, and properties aren't all public, so the usual clone subjects all fall flat. So I went ahead and wrote extension methods, but again - with certain properties being read only you run into trouble. Plus there's change tracking meaning if you have a copy of the current state before update, there are no changes to be made.

    So I'm wondering what other approaches others may have taken to the same problem. Of course I could add serializable tags to the objects (which is what I ended up doing since my approach to copy properties lead to problems since not all properties are publicly accessible, meaning I had to change even more code, plus I ran afould of change tracking since my cloned object would show all properties as changed).
    I figure I could load the objects twice, make changes on one, and set the same properties using their original values on my object that I'm using for rollback - but that means more object loading than necessary which means more traffic, more load and more operations.

    Also, as a sidenote, shouldn't change tracking actually track if a value has been changed in its setter?

    Subject: RE: Rollback using the .NET SDK
    Replied by: Jeff Lindborg on 09-10-2013 01:13:54 PM
    Couple things 
     
    1. if a property is not public that usually means the API does not allow for setting it via REST – hence the reason why you aren’t allowed to change it on an object via the SDK.  For instance you can’t change the ObjectId of a user or the like.  If you have some specific example where that’s not the case, let me know.
    2. You can always iterate over the changed properties on an object (it’s a simple name/value pair list you can walk) for reviewing which properties are pending on an object.  Perhaps I’m not understanding what you’re trying to accomplish but when I use the SDK it’s easier to just leverage this mechanism directly rather than trying to overlay your own on top of it – unless I’m not getting what you’re trying to do.
    3. When an update fails via a REST call with multiple properties, all properties don’t get changed – for instance if you pass an illegal value for a phone number to a transfer rule along with a number of rings and other items, nothing is applied – it doesn’t make changes to some properties and only hold back those that are illegal, it’s an all or nothing thing – from your comments it seems like you’re implying you can’t tell what got committed and what didn’t – again, unless I’m not understanding your issue.
    If you have more details of what you’re trying to do that you’re having difficulty with I’d like to hear about it.

    Subject: RE: Rollback using the .NET SDK
    Replied by: Jeff Lindborg on 09-10-2013 02:40:20 PM
    ah... so you want to have your user (or transfer rule or call handler or whatever) object i the SDK to be clonable so you can keep the original copy and the one that has your changes (in the change list) around seperately Although that seems a bit rough...  maybe a better (easier/cleaner) approach would be for me to tie the change list to a corresponding "original value" list - both name/value pairs - when you change a property the change list and original value list both get updated with that property get updated.  Making the objects clonable is also possible but the rollback process would be pretty manual - with an "original value" list to work with you can simply apply those values again.  I'd have to think about the implementation a bit (i.e. you wouldn't want it to flush like you do the change list after a successful update) but this seems doable and possibly handy.  Am I understanding the nature of your challange properly here?

    Subject: RE: Rollback using the .NET SDK
    Replied by: Stephan Steiner on 09-10-2013 02:14:39 PM
    Let's say my admin edits an existing user. The user has a phone on CUCM, a mailbox on connection, and is an agent on IPCC Express. The admin performs the following changes that are submitted as one:
    Change the directory number
    Assign a new skill

    So, my solution goes to CUCM and changes the DN, line appearance, primary extension on device/line/directorynumber/enduser in CUCM which all works. Then I go to connection, extract the appropriate box, change the DtmfAccessId. And then I go to IPCC Express, which refuses the change. Or let's say it goes down just at the moment I'm trying to send my command.

    Since the changes are coming in as a packet that cannot be split up into single operations, I now have to roll back, telling the user "please try again later", rather than leave a half done state. So I need to roll back the changes made. With CUCM, since AXL is so messed up (still.. been complaining about that since CCM 4.0), we have our own objects that can be translated into soap and back... so before I make the change (e.g. no the enduser)... I clone it, and then the number change if successful stores a rollback operation that performs a reverse update (just sending the object state before the change).

    With connection, I'm doing pretty much what you suggested.. Say I have UserBase myMailbox. I then create UserBase clone = new UserBase(); Then fill all the properties of clone. Then change myMailbox.DtmfAccessId to the new value and perform myMailbox.Update(); If I need to roll back the successful operation, I compare every (writeable) property of myMailbox with clone, and whatever is different, I update. Then call myMailbox.Update() which will roll back the change I made (that also takes care of the change list being updated if a property is written to, but with the same value).

    Things get even trickier if you try to roll back a delete (and I'm fully aware you're going to be left with a less than fully restored box if you delete it... content, greetings, the likes is gone, but it's less relevant). Say you have a box X and you need to c reate a box that's just like that. In my AXL API... I take the object. call myObject.Update().. done (it sends all the properties minus the objectid (uuid in axl) this giving me a perfect replica.. the only thing I cannot restore is pin/password for obvious reasons).

    Subject: RE: Rollback using the .NET SDK
    Replied by: Stephan Steiner on 09-10-2013 02:47:23 PM
    By the way...with regards to your point 1... you can update an ObjectID (though if there's an existing value it is then blocked... I just ran full steam into that wall twice until I figured I'd step into the property to have a closer look)... and more importantly, you can update the name of an existing notification device, but if you try, the server will respond with:
    <ErrorDetails>
      <errors>
        <code>INVALID_PARAMETER</code>
        <message>property is read only: name=devicename</message>
      </errors>
    </ErrorDetails>

    Subject: RE: Rollback using the .NET SDK
    Replied by: Stephan Steiner on 09-10-2013 03:01:07 PM
    Jeff Lindborg:
    ah... so you want to have your user (or transfer rule or call handler or
    whatever) object i the SDK to be clonable so you can keep the original
    copy and the one that has your changes (in the change list) around
    seperately

    Yes, that's what I'm currently doing.

     maybe a better (easier/cleaner) approach would be for me to tie the change list to a corresponding "original value" list - both name/value pairs - when you change a property the change list and original value list both get updated with that property get updated.  Making the objects clonable is also possible but the rollback process would be pretty manual - with an "original value" list to work with you can simply apply those values again.  I'd have to think about the implementation a bit (i.e. you wouldn't want it to flush like you do the change list after a successful update) but this seems doable and possibly handy.  Am I understanding the nature of your challange properly here?
    If there'd be a method on the object (like Rollback.. Revert) that would apply the original value, that would make things easier. It could get tricky though with dependant objects - I'm currently looking at notification devices.

    Is there a way to restore full objects by the way? suppose I do a full load of a Mailbox - is there a way for me to restore it all? The AddUser method doesn't take a UserFull (not even a UserBase, the way I read the source code the ConnectionPropertyList takes only properties that apply to a single object, not dependant objects (say notification devices of a mailbox). My workaround consisted of creating the base mailbox, reading the entire box, comparing + updating all properties that are different, and do an update.

    Subject: RE: Rollback using the .NET SDK
    Replied by: Jeff Lindborg on 09-10-2013 04:15:20 PM
    ok - first let me impelement cloning (I'll need to do deep cloneing as simply implementing IClonable isn't going to cut it with referenced objects potentially being shared) - not too tricky but a bit tedious - I'll take a look at that next week as I'm in the middle of a move right now (I'm checking mail on my laptop on a folding table sitting on a box at the moment) - I should be up and fully functional next week.  After that I can do some thinking about how to implement a workable "revert" option - there's definitely some tricky edge cases that give me pause there.  And no, there's no way to restore full objects - adding a user takes a template - you'd have to add all properties to an update after create off a user full instance.  I can imagine a "reapply all values" that could be implemented that would automatically flag all editable properties as dirty and when you do the update every single property would be updated on an object (but not any sub objects - so a user's top level properties would update but not their notification devices - each object would have to be applied seperately).  Doing a recursive "roll" through all dependant object references would be ambitious - could be done using reflection and a bit of elbow grease but I'm not sure I'd want to take that on...

    Subject: RE: Rollback using the .NET SDK
    Replied by: Stephan Steiner on 11-10-2013 06:34:55 AM
    Is there a particular reason why you're not exposing the ChangedPropList in UserBase? You do change tracking after all, there's an Update and Clear method just like for other objects, but the changelist isn't exposed.
    Also.. speaking of the change list... if you set the same property multiple times, e.g.
    NotificationDevice device = new NotificationDevice();
    device.SmtpAddress = "email@email.com";
    device.SmtpAddress = "email2@email.com"

    then device.ChangeList will contain an element with key SmtpAddress twice. And the way I read the update method, we'd be sending two SmtpAddress which may not be what the server likes. Shouldn't the add method of ConnectionPropertyList check if the key already exists, and if so, replace rather than just add to the existing list?
    The easy way to get around that is check in ConnectionPropertyList.Add, but I think it might be better to check in the actual setters and only Add when the value has actually changed (as it's being done for certain properties that may only be set once... e.g. ObjectId - but instead of comparing to null, compare to whatever value is currently set. The way it is set up now, you'd perform updates even if there's nothing to be changed in case you set a value to what it is already set to.

    Subject: RE: Rollback using the .NET SDK
    Replied by: Jeff Lindborg on 11-10-2013 06:19:05 PM
    1. the way the full inherits from the base class means I needed to use internal instead of private for the change list structure for that – just added that functionality.
    2. I disagree a bit on the change list logic – I just changed the ConnectionPropertyList class to behave such that if the property name already exists in the list to simply update the value for that property to the most recent change (last write wins) rather than rejecting the change – this makes more sense to me and follows conventions I’ve seen elsewhere for similar property handling.
    3. Continuing to work on the clone – have several things I’m supposed to be working on but I should be able to get a version checked in early next week and update NuGet after that – you can get updated source before I update the NuGet binaries – I’ll ping you when the updated library is checked in.

    Subject: RE: Rollback using the .NET SDK
    Replied by: Jeff Lindborg on 11-10-2013 08:30:49 PM
    Ok – version 3.0.29 has several changes in it that should help.
    All public classes (non static) are now marked for serialization.
    All public classes support the “Clone()” method to make a full (deep clone) copy – this is a little pokey with the use of reflection and walking all sub objects and such but it’s not too bad.
    The change list for userFull and userBrief is public
    The logic for changelist now prevents duplicates and uses a “last value wins” model if the same property name is updated multiple times.
    Every public class supports a FlagAllPropertiesForUpdate() method  - it will put any public (writable) property for a class instance into the change list if it’s not empty (null or blank string) – the API does not handle empty string setting in many cases (some of these will be fixed for the 10.0 release, some will not) so I steered clear of them – will revisit that again later.
    Hopefully that gives you something to work with – let me know if that’s what you were looking for or not.
    This is only available via source from the SubVersion repository  - I need to add unit tests and some integration tests to cover these and update the developers guide first before I can push it to NuGet.

    Subject: RE: Rollback using the .NET SDK
    Replied by: Stephan Steiner on 12-10-2013 10:08:27 AM
    Jeff Lindborg:
    1. the way the full inherits from the base class means I needed to use internal instead of private for the change list structure for that – just added that functionality.
    2. I disagree a bit on the change list logic – I just changed the ConnectionPropertyList class to behave such that if the property name already exists in the list to simply update the value for that property to the most recent change (last write wins) rather than rejecting the change – this makes more sense to me and follows conventions I’ve seen elsewhere for similar property handling.
    3. Continuing to work on the clone – have several things I’m supposed to be working on but I should be able to get a version checked in early next week and update NuGet after that – you can get updated source before I update the NuGet binaries – I’ll ping you when the updated library is checked in.
    1) But why internal and not protected? I understand that the the full user needs to inherit from UserBase - but internal exposes the changelist to the entire namespace.
    2) I think we are on the same page, but I just expressed myself badly. Last one wins is good... I merely meant there's no need to send anything in this scenario:
    Load an object, set myObject.Property = myObject.Property (so not changing the value).. that would add an entry to the change list with key = Property, value = myObject.Property .And now since the changelist count is > 0, calling myObject.Update() would send something to the server, even though it is not necessary. So, inside every property, I'd do something like if (m_property != value) { m_property = value; changelist.Add("propertyname", value);}  So you can always overwrite, but if you overwrite with the current value, no change is generated because there's no change to be sent to the server.