Skip to Content

/wp-content/uploads/2014/10/10_556226.png


Hi everyone,

Working with the error archive.


As discussed in the previous blog, we need to manually execute delete operations against the error archive entries to purge them. In theory it is pretty simple, you know how to delete an entity from an OData collection (covered in the blog #04).


But it actually turns out that we need to come up with a way to associate and identify each request and its error in the error archive.

/wp-content/uploads/2014/10/tag1_556227.png

There could be different approaches how to do it – and one possible tip is to use the simple technique called “custom tag”. By assigning each request a unique custom tag, it will appear in both the request object in the callback and the property of the error archive entity.

/wp-content/uploads/2014/10/tag2_556267.png

How do we add a custom tag? In the blog #04, we learned how to do the CUD operation by means of schedule<CUD>Entity methods.


For example, the Create operation is invoked with this method.

01  [store scheduleCreateEntity:entity collectionPath:@"CollectionName" delegate:self options:nil];

Alternatively, if we want to add a custom tag, we can invoke the Create operation with scheduleRequest: method.

01  SODataRequestParamSingleDefault *requestParam
02     = [[SODataRequestParamSingleDefault alloc] initWithMode:SODataRequestModeCreate
03                                                resourcePath:@"CollectionName"];
04  requestParam.payload = entity;
05  requestParam.customTag = @"myCustomTag";
06  [store scheduleRequest:requestParam delegate:self];

From the functional perspective, both do the same job – but the scheduleCreateEntity: does the better abstraction, in fact it calls very similar logic behind the scenes. In the bottom of this blog you can refer the CRUD operations code with scheduleRequest:.


Hold on, now I have a second thought with the line #05. It is a good idea to give each request some kind of unique custom tag, it can be useful for debugging.

01  requestParam.customTag = [NSString stringWithFormat:@"tag%@", [[NSUUID UUID] UUIDString]];

Now that a custom tag has been assigned to a CUD operation, how do we make use of it in the offlineStoreRequestFailed:?

01  - (void) offlineStoreRequestFailed:(SODataOfflineStore*) store
02                             request:(id<SODataRequestExecution>) requestExecution
03                               error:(NSError*) error
04  {
05    ...
06    id<SODataRequestParamSingle> request = (id<SODataRequestParamSingle>)requestParam;
07    NSString *resourcePath = [NSString stringWithFormat:@"ErrorArchive?$filter=CustomTag eq '%@'", requestParam.customTag];
08    [offlineStore scheduleReadEntityWithResourcePath:resourcePath delegate:self options:nil];
09  }

First of all, your custom tag can be referred in the request object. In #08, you see how the requestParam.customTag is obtained. It should have the assigned tag value like “tag25716A15-667A-4468-AC88-B56598D42E0D”. And it build an OData query string like “ErrorArchive?$filter=CustomTag eq tag25716A15-667A-4468-AC88-B56598D42E0D“, which let you identify the associated error item out of the error archive. With the line #08, it triggers OData query against the error archive, which locally exists with the offline store.


It will invoke requestServerResponse: method, and as it was queried, it returns the entity set – simply delete the associated error entity out of the error archive.

01  - (void) requestServerResponse:(id<SODataRequestExecution>)requestExecution
02  {
03      id<SODataRequestParam> requestParam = requestExecution.request;   
04      if ([requestParam conformsToProtocol:@protocol(SODataRequestParamSingle)]) {
05          id<SODataRequestParamSingle> request = (id<SODataRequestParamSingle>)requestParam;
06          if (request.mode == SODataRequestModeRead) {
07              id<SODataResponseSingle> responseSingle = (id<SODataResponseSingle>)requestExecution.response;
08              if ([responseSingle.payload conformsToProtocol:@protocol(SODataEntitySet)]) {
09                  id<SODataEntitySet> errors = (id<SODataEntitySet>)responseSingle.payload;
10                  for (id<SODataEntity> entity in errors.entities) {
11                      [offlineStore scheduleDeleteEntity:entity delegate:self options:nil];
12                      // purging an error item is complete
13                  }               
14              }
15      ...

That’s all about how to handle the situation in which the OData Producer fails one of the queued requests during a flush. Hope you get some solid idea how all gizmos are working in the offline scenario and how to work with them.


This is a kind of closing of the offline scenario – but not the end of the show. I’ll add some more things, which should be helpful for the actual implementation (that’s a whole idea of these blogs). Tiny but important bits and pieces. Thanks for keeping company so far.



See you in the next blog,

Ken


List of blogs


Appendix:

Create operation (= HTTP POST)

01  [store scheduleCreateEntity:entity collectionPath:@"CollectionName" delegate:self options:nil];

is equivalent to

01  SODataRequestParamSingleDefault *requestParam
02     = [[SODataRequestParamSingleDefault alloc] initWithMode:SODataRequestModeCreate
03                                                resourcePath:@"CollectionName"];
04  requestParam.payload = entity;
07  [store scheduleRequest:requestParam delegate:self];

Read operation (= HTTP GET)

01  [store scheduleReadEntityWithResourcePath:entity.resourcePath delegate:self options:nil];

is equivalent to

01  SODataRequestParamSingleDefault *requestParam
02     = [[SODataRequestParamSingleDefault alloc] initWithMode:SODataRequestModeRead
03                                                resourcePath:entity.resourcePath];
04  [store scheduleRequest:requestParam delegate:self];

Update operation (= HTTP PUT)

01  [store scheduleUpdateEntity:entity delegate:self options:nil];

is equivalent to

01  SODataRequestParamSingleDefault *requestParam
02     = [[SODataRequestParamSingleDefault alloc] initWithMode:SODataRequestModeUpdate
03                                                resourcePath:entity.editResourcePath];
04  requestParam.payload = entity;
07  [store scheduleRequest:requestParam delegate:self];

Delete operation (= HTTP DELETE)

01  [store scheduleDeleteEntity:entity delegate:self options:nil];

is equivalent to

01  SODataRequestParamSingleDefault *requestParam
02     = [[SODataRequestParamSingleDefault alloc] initWithMode:SODataRequestModeDelete
03                                                resourcePath:entity.editResourcePath];
04  [store scheduleRequest:requestParam delegate:self];
To report this post you need to login first.

10 Comments

You must be Logged on to comment or reply to a post.

    1. Kenichi Unnai Post author

      Yes you can do it by going through multiple OData requests to explicitly create the entities and then link them together. In addition to it, you can expect a new feature to simplify those steps in the upcoming SPs.

      (0) 
  1. Yohan Kariyawasan

    Thanks Ken for the quick response, i have another question in the following scenario is SMP is going to send request one after another to the GW and does it keeps the order in which i make the calls.

    [store scheduleCreateEntity:parententity collectionPath:@”SalesOrder” delegate:self options:nil];

    [store scheduleCreateEntity:childentity collectionPath:@”SalesItem” delegate:self options:nil];

    [store scheduleCreateEntity:childentity collectionPath:@”SalesItem” delegate:self options:nil];

    I am trying to link the child object to parent entity when the create call is made for the child entity.

    (0) 
    1. Kenichi Unnai Post author

      Good question. This code should work for linkage:

      SODataLink link = [[SODataLinkDefault alloc] initWithResourcePath:@”OrderItems(1)”];

      SODataRequestParamSingleDefault *requestParam

                  = [[SODataRequestParamSingleDefault alloc]

                            initWithMode:SODataRequestModeCreate

                            resourcePath:@”Order(101)/$link/OrderItems”];

      requestParam.payload = link;

      [store scheduleRequest:requestParam delegate:self];

      You can obtain both parent & child entities locally. So you can change the example value such as OrderItems(1) or Order(101) with variables such parentEntity or childEntity as written here.

      The Offline Store will keep the order of requests when it applies them to the backend.

      And you can execute all the create and link logic locally and just do one flush in the end for the entire deep insert.

      (0) 
    1. Michael Appleby

      Please create a new Discussion marked as a Question.  The Comments section of a Blog (or Document) is not the right vehicle for asking questions as the results are not easily searchable.  Once your issue is solved, a Discussion with the solution (and marked with Correct Answer) makes the results visible to others experiencing a similar problem.  If a blog or document is related, put in a link.  Read the Getting Started documents (link at the top right) including the Rules of Engagement. 

      NOTE: Getting the link is easy enough for both the author and Blog.  Simply MouseOver the item, Right Click, and select Copy Shortcut.  Paste it into your Discussion.  You can also click on the url after pasting.  Click on the A to expand the options and select T (on the right) to Auto-Title the url.

      Thanks, Mike (Moderator)

      SAP Technology RIG

      (0) 
  2. Olav Rask

    Hi,

    Have you worked with the AffectedEntity navigation property on the ErrorArchive? From the documentation it sounds like this should give a reference to the entity related to the error, but from my trial and error i am not able to expand it.

    Does anyone have experience with this?

    Br Olav

    (0) 
    1. Kenichi Unnai Post author

      Thanks for pointing out. It is a new feature of SDK SP11.

      You can use it to access entities that are in an error state due to a particular failed request, you can only access them through a specific error archive entity.

      So /ErrorArchive?$expand=AffectedEntity will throw an error

      But /ErrorArchive(1)/AffectedEntity should work fine.

      As this remark is not documented, there will be some update on the help doc soon.

      (0) 
      1. Olav Rask

        Thank you for getting back to me! It works for me the way you explain it – thats great.

        I am wondering though if something else is possible:

        In our application we have a list of items that the user is working on. Each item can be updated and we would like to show in the list with a small icon if an item has local changes and if an item has resulted in an error. For local changes we check for the instance annotation com.sap.vocabulary.Offline.v1.islocal – this works fine. But do you know if something similar exists for errors?

        I am guessing it does not, but it would be a really nice feature 🙂

        Best regards,

        Olav

        (0) 
        1. Kenichi Unnai Post author

          There is an annotation that marks entities and links that are in an error state called:

          com.sap.vocabulary.Offline.v1.inerrorstate

          (I suggest you to bring this to your forum post – the QA can be searchable)

          (0) 

Leave a Reply