Request factory is sending back the entire List<EntityProxy> even if only one element is altered - gwt

When I load an entity proxy which has a nested collection of entity proxies (eg: AddressBook entity proxy containing a list of Contact proxies) and if I make changes to the inner list ( the List of Contacts) like removal of an item, and call a persist on it, Request Factory is sending the entire list of contacts. Is this the expected behaviour, or is it supposed to send only a command to delete the item on the server also?
The question is, does request factory send deltas just for field level changes, or does it calculate deltas for collections also?
ContactProxy
interface ContactProxy extends EntityProxy {
...
//Getters and setters for firstName, lastName, phoneNumber etc...
...
}
AddressBookProxy
interface AddressBookProxy extends EntityProxy {
...
List<ContactProxy> getContacts();
void setContacts(List<ContactProxy> contacts);
...
}
Focus code:
//Assume I received an abProxy from a previous context.
AddressBookRequestContext context = requestFactory.requestContext();
abProxy = context.edit(abProxy);
abProxy.getContacts().remove(0);
context.persist().using(abProxy).fire();
The above piece of code is sending the entire list of contacts received in the previous context, but I expected to send only a delete command to the server. Am I doing something wrong?
Now when I make a change to a single contact in the AddressBook entity proxy and make a call to persist, it is still sending the entire list of contacts. What is the workaround to get deltas working for these kind of collection level changes.

You're modifying the list from a list of 10 elements to a list of 9 elements, so RF sends the new list of 9 elements (and will call the setter with the new list on the server side).
However, it only sends the IDs of the contacts, not their properties, because those haven't changed (and yes, it means all contacts will have to be retrieved on the server-side, in order to populate the new list of 9 elements, before setting it into the address book).
There's probably room for improvement in RF though: when you edit() a proxy, it automatically edits all the proxies it references (recursively), so all 10 contacts are edit()ed, and thus all 10 contacts IDs are sent to the server, an all 10 contacts are retrieved from the database and validated, even though only 9 of them are used afterwards. As a result (and that could be seen as a feature) in the event the removed contact has been updated since originally retrieved on the client, the server will send an EntityProxyChange for the contact to the client in the response.
In a few words: there's no magic, everything comes to a cost, so be careful when designing your "APIs"; you might want to add a removeContact method to your RequestContext instead of modifying the list (and retrieve the address book again –batched in the same RequestContext– to get the update on the client-side)

Related

Update nested object using REST API

I have resource Student and 4 API endpoints:
POST: /students
GET: /students/{id}
PUT: /students/{id}
DELETE: /students/{id}
Now, I want to add new property Address as an object to Student:
class Student
{
public int Id;
public string Name;
public Address Address;
}
Address has property Street as an object too.
My question is, if I want to update only the address or street, would it be better to:
Use update student API: provide all student properties even if they are not changed
Create new endpoints PUT: /students/{id}/address and PUT: /students/{id}/address/street
It depends how client friendly you want the API to be. I've recently seen an API that returns an object but in order to update a single field in the object the client has to call another endpoint to get extra information to add to the original object in order to PUT the updated one. I regard that as client unfriendly.
If a client GETs a Student and they get the full object Id, Name, Address, Street then I would expect PUT: /students/{id} to be able to handle a full object, updating fields that are different from the one in the database or wherever the object is stored. If the API gives the client a full object then the API should be client friendly and be able to accept a full object back rather than making the client deconstruct the object.
I wouldn't expect a client to have to extract an Address from the Student, then extract the Street from the Address and send that to PUT: /students/{id}/address/street if the API gave the client a full Student object in the first place.
The only downside to this approach is bandwidth. If a single letter in the Street changes the client has to PUT the entire Student object. So it really depends what the client is and what network it operates on. A REST client on the internet? Accept full objects. An embedded client on a resource constrained device? It may need to send fragments of the object but that leads to the REST endpoints matching the Student object hierarchy which to me is close coupling and not really recommended.

Acumatica REST - CustomerLocation entity does not return records

Using REST API, able to pull down customers, contacts, and addresses via the Customer entity, however, when I try to get CustomerLocation entities, I am just getting an empty set.
[]
Using latest version as of the writing of this question (2018R1 dated something like Aug 17 2018).
I've tried the following:
CustomerLocation?$expand=LocationContact
CustomerLocation?$expand=LocationContact,LocationContact/Address
Neither of them return any data.
The CustomerLocation entity is linked to a Generic Inquiry that is defined to allow creation of new records, so it was causing an error trying to persist the data when trying to do a Put to it since I was not supplying a body or a valid structure.
How I got this to work was to create my own Generic Inquiry, linking it to an entity in my extended endpoint, and adding a Detail property within the entity that would serve as the collection of detail records returned by the Generic Inquiry. Then put all of the fields from the Generic Inquiry within the Results Fields.
Now, I can get the records from the Generic Inquiry by doing a Put request via my endpoint entity:
AICustomerLocationGI?$expand=Results
Note: It's important to do a Put instead of a Get if you want to avoid getting BQL Delegate errors on some DACs.
That returned all records at once, but got me where I initially needed to be. Next, I added a parameter, Greater Than condition, and sorting on Address ID to the Generic Inquiry and defined the generic inquiry to return the top 100 records. By passing the last Address ID of the previous batch of records in the body of the Put request, this gave me a paging mechanism for returning the records.

CloudKit, join or efficient way to add an item to list

I'm using CloudKit to manage a list of messages (record type Message with a field title and body). All messages are public and I want to maintain which Message the user has read using the mobile app.
The app can have thousands of users and messages. And I use swift3.
I've think of different way to do it but they seems quite poor in term of performance:
add a field 'readers' to Message which is a list of string corresponding of user Id. The problem is that if I want to add a new user ID I must load all the list. This is problematic for a mobile app in case of a lot of users have read the message. Can I lazy fetch a list field and add a value to it without downloading all the list (like in classic Orm)?
add another record type 'Reader' which has two fields: a user ID and a message ID. I can't find a way to join Message and Reader in a predicate to download only Message that the user hasn't read. Is that possible?
As suggested by Matthew: add a record type ReadArticle in the private database that stores only a CKReference to a message. The problem is that we need to download all message ID before sending them in a NOT predicate.
I don't know how to solve this problem with a database like CloudKit.
Any advice ?
Readers field
This approach is not ideal for the exact reasons you pointed out, and it additionally is unsafe as a String could theoretically contain anything.
Reader object
Even if there was a way to get this to work you'd be storing user ids in the public database. That's probably not a privacy-conscious thing to do.
ReadMessage Record in Private Database
Suppose you had a Record Type called ReadArticle. This object would contain exactly one field, a CKReference to a Message record.
Then, when someone reads a Message, you take the recordId of that Message, create a CKReference from it, and place that reference in the "message" field of a new CKRecord object of type ReadMessage. Then, you save it to the user's private database. Because it's a CKReference, it won't actually take up hardly any space in iCloud because it's just a pointer, and because you'd use the user's private database there's no need to explicitly identify the user.
Then when you want all unread messages, fetch the ReadMessage record, and create a NOT predicate to receive all Message records where the record id is not any of the read ones. CloudKit definitely supports NOT predicates, but if it happens to not support NOT predicates specifying record ids, than you could use some other unique field on message instead.

Breeze dataservice abstractrest - with sparse save response

I'm working with breeze labs AbstractRestDataServiceAdapter. In our data service adapter implementation's _createSaveRequest method our state.isModified branch emulates your oData adapters and only sends modified fields in the save request.
My issue is that our REST server returns a sparse response, i.e. input data and any fields on the entity that were updated. The result is that from a client perspective fields not returned in the saved entity are being wiped out.
I had seen merge logic in prior debugging sessions, so I initially thought I might be able to influence the save response processing via MergeStrategy, but it appears MergeStrategy doesn't apply in a save scenario. It appears AbstractRestDataServiceAdapter assumes the server is returning the full entity in a save response.
What options do we have for managing a sparse response from the server that preserves the state of fields not returned in the save response?
Is there a particular AbstractRestDataServiceAdapter method that we should override to manage merging the save response?
Take a look at the changeRequestSucceeded method of the breeze.labs.dataservice.abstractrest.js adapter which processes each entity-specific response in particular the lines at the top:
var saved = saveContext.adapter._getResponseData(response);
if (saved && typeof saved === 'object') {
// Have "saved entity" data; add its type (for JsonResultsAdapter) & KeyMapping
saved.$entityType = saveContext.originalEntities[index].entityType;
addKeyMapping();
} else {
...
}
saveContext.saveResult.entities.push(saved);
return saved;
Notice the references to saveContext.originalEntities[index].
Suppose you know that the data in the saved object represent just the specific properties that you need to merge into the entity in cache.
Well you're in excellent position here, in YOUR version of this method in YOUR concrete implementation of this adapter, to combine the property values of saved and the property values of saveContext.originalEntities[index] before pushing that merged result into saveContext.saveResult.entities.
There is no requirement to actually return entities from the server on a save. Both the breeze.dataService.odata and the breeze.dataservice.mongo adapter have this issue where the server only returns some or parts of the saved entites. The only requirement is that the dataService adapter saveChanges method return an object with two properties, i.e. something like
return { entities: entities, keyMappings: keyMappings };
If you actually have just the modified fields returned from the server then will have to manage the merge yourself, but this isn't really that complex. For each entity 'saved' simply locate it in the cache, set the changed properties and call acceptChanges and the return the 'located' entity in the result shown above.

Why JsonStore provides a commitchanges method

The extjs Jsonstore class has a method commitchanges().
Now considering that the javascript code will send an AJAX request to a servlet and not to a db directly, what do we need a commitchanges() method?
Whenever user changes anything in the form panel's or UI components that data is changed in record, but at this time record will maintain a list of modified properties in the record and store maintains a list of modified records in the store
So whenever you send a request to save the data to server, after successful save you will get back a success response, on this response store need to make sure that the saved record will be removed from the list of records that are modified in the store and also remove list of modifications from the record instance to indicate that save operation was successful
So the operations of removing list of modified properties from record instance is performed by record.commit method and operations of removing saved record from list of modified records from store is performed by store.commitChanges method which in turn will call record.commit for every record which is saved correctly on server