Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
kenichi_unnai
Advisor
Advisor
2,454


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.

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.

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];
10 Comments