String httpMethod = request.getHttpMethod();
if(httpMethod.equals("PATCH")) {
// only replace the properties which are present in request body
Set<String> keySet = requestBody.keySet();
for (Iterator<String> iterator = keySet.iterator(); iterator.hasNext();) {
String propertyName = (String) iterator.next();
if(propertyName.equals("PersonId")) {
continue; // ignore key field
}
existingPersonMap.replace(propertyName, requestBody.get(propertyName));
}
if(requestBody.containsKey("FirstName")) {
existingPersonMap.replace("FirstName", requestBody.get("FirstName"));
}else {
return UpdateResponse.setError(ErrorResponse.getBuilder()
.setMessage("Error: property FirstName must not be null")
.setStatusCode(400)
.response());
}
if(requestBody.containsKey("LastName")) {
existingPersonMap.replace("LastName", requestBody.get("LastName"));
}else {
existingPersonMap.replace("LastName", null);
}
if(requestBody.containsKey("Email")) {
existingPersonMap.replace("Email", requestBody.get("Email"));
}else {
existingPersonMap.replace("Email", "Unknown");
}
if(requestBody.containsKey("PersonId")) {
// do nothing
}
"On success, the response MUST be a valid success response"
Header name | Header value |
Prefer | return=representation |
Prefer | return=minimal |
You're still wondering why it is needed?
For me, the question is now: how is that solved in the SDK?
Aha, this is interesting
Note:
If UPDATE with return=representation is desired, then it is a prerequisite that the READ must be implemented
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="demo" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="Person">
<Key>
<PropertyRef Name="PersonId" />
</Key>
<Property Name="PersonId" Type="Edm.Int32"/>
<Property Name="FirstName" Type="Edm.String" Nullable="false"/>
<Property Name="LastName" Type="Edm.String"/>
<Property Name="Email" Type="Edm.String" DefaultValue="Unknown"/>
<Property Name="JobId" Type="Edm.Int32" />
</EntityType>
<EntityContainer Name="container">
<EntitySet Name="People" EntityType="demo.Person"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
package com.example.DemoService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sap.cloud.sdk.service.prov.api.operations.Query;
import com.sap.cloud.sdk.service.prov.api.operations.Read;
import com.sap.cloud.sdk.service.prov.api.operations.Update;
import com.sap.cloud.sdk.service.prov.api.request.QueryRequest;
import com.sap.cloud.sdk.service.prov.api.request.ReadRequest;
import com.sap.cloud.sdk.service.prov.api.request.UpdateRequest;
import com.sap.cloud.sdk.service.prov.api.response.ErrorResponse;
import com.sap.cloud.sdk.service.prov.api.response.QueryResponse;
import com.sap.cloud.sdk.service.prov.api.response.ReadResponse;
import com.sap.cloud.sdk.service.prov.api.response.UpdateResponse;
public class ServiceImplementation {
private static final Logger logger = LoggerFactory.getLogger(ServiceImplementation.class);
private static final List<Map<String, Object>> peopleList = new ArrayList<Map<String, Object>>();
@Query(serviceName="DemoService", entity="People")
public QueryResponse getPeople(QueryRequest request) {
List<Map<String, Object>> peopleList = getAllPeople();
return QueryResponse.setSuccess().setDataAsMap(peopleList).response();
}
@Read(serviceName="DemoService", entity="People")
public ReadResponse getPerson(ReadRequest request) {
Integer id = (Integer) request.getKeys().get("PersonId");
Map<String, Object> personMap = findPerson(id);
// check if the requested person exists
if(personMap == null) {
logger.error("Person with PersonId " + id + " doesn't exist! Service request was invoked with invalid key value!");
return ReadResponse.setError(
ErrorResponse.getBuilder()
.setMessage("Person with PersonId " + id + " doesn't exist!")
.setStatusCode(404)
.response());
}
return ReadResponse.setSuccess().setData(personMap).response();
}
@Update(serviceName = "DemoService", entity = "People")
public UpdateResponse updatePerson(UpdateRequest request) {
// retrieve from request: which person to update
Integer personIdForUpdate = (Integer)request.getKeys().get("PersonId");
// retrieve from request: the data to modify
Map<String, Object> requestBody = request.getMapData();
// retrieve from database: the person to modify
Map<String, Object> existingPersonMap = findPerson(personIdForUpdate);
// check if the requested person exists
if(existingPersonMap == null) {
logger.error("Person with PersonId " + personIdForUpdate + " doesn't exist! Service request was invoked with invalid key value!");
return UpdateResponse.setError(
ErrorResponse.getBuilder()
.setMessage("Person with PersonId " + personIdForUpdate + " doesn't exist!")
.setStatusCode(404)
.response());
}
String httpMethod = request.getHttpMethod();
if(httpMethod.equals("PATCH")) {
// only replace the properties which are present in request body
Set<String> keySet = requestBody.keySet();
for (Iterator<String> iterator = keySet.iterator(); iterator.hasNext();) {
String propertyName = (String) iterator.next();
if(propertyName.equals("PersonId")) {
continue; // ignore key field
}
existingPersonMap.replace(propertyName, requestBody.get(propertyName));
}
}else if (httpMethod.equals("PUT")) {
// in case of PUT, all properties have to be replaced, either with new value or reset
// FistName can NOT be null but also no Default value. Here it is either a service-generated value, or throw error
if(requestBody.containsKey("FirstName")) {
existingPersonMap.replace("FirstName", requestBody.get("FirstName"));
}else {
return UpdateResponse.setError(ErrorResponse.getBuilder().setMessage("Error: property FirstName must not be null").setStatusCode(400).response());
}
// LastName can be null
if(requestBody.containsKey("LastName")) {
existingPersonMap.replace("LastName", requestBody.get("LastName"));
}else {
existingPersonMap.replace("LastName", null);
}
// Email: reset to default, as specified in edmx: <Property Name="Email" ... DefaultValue="Unknown"/>
if(requestBody.containsKey("Email")) {
existingPersonMap.replace("Email", requestBody.get("Email"));
}else {
existingPersonMap.replace("Email", "Unknown");
}
// JobId can be null
if(requestBody.containsKey("JobId")) {
existingPersonMap.replace("JobId", requestBody.get("JobId"));
}else {
existingPersonMap.replace("JobId", null);
}
// PersonId is key field, don't change it, to keep database consistent
if(requestBody.containsKey("PersonId")) {
// do nothing
}
}
return UpdateResponse.setSuccess().response();
}
/* Dummy Database */
private List<Map<String, Object>> getAllPeople(){
// init the "database"
if(peopleList.isEmpty()) {
createPerson(1, "Anna", "Martinez", "Anna@mail.com", 1);
createPerson(2, "Berta", "Hughes", "Berta@webmail.com", 2);
createPerson(3, "Claudia", "Forrester", "CF@fastmail.com", 1);
createPerson(4, "Debbie", "Huffington", "debbie@dbmail.com", 2);
}
return peopleList;
}
// used for READ operation. Go through the list and find the person with requested PersonId
private Map<String, Object> findPerson(Integer requiredPersonId){
List<Map<String,Object>> peopleList = getAllPeople();
for(Map<String, Object> personMap : peopleList) {
if(requiredPersonId.equals((Integer)personMap.get("PersonId"))) {
return personMap;
}
}
return null;
}
private Map<String, Object> createPerson(Integer personId, String firstName, String lastName, String mail, Integer jobId){
Map<String, Object> personMap = new HashMap<String, Object>();
personMap.put("PersonId", personId);
personMap.put("FirstName", firstName);
personMap.put("LastName", lastName);
personMap.put("JobId", jobId);
if(mail != null) {
personMap.put("Email", mail);
}else {
personMap.put("Email", "Unknown"); //default value which we've specified in edmx file
}
peopleList.add(personMap);
return personMap;
}
}
package com.example.DemoService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sap.cloud.sdk.service.prov.api.operations.Query;
import com.sap.cloud.sdk.service.prov.api.operations.Read;
import com.sap.cloud.sdk.service.prov.api.operations.Update;
import com.sap.cloud.sdk.service.prov.api.request.QueryRequest;
import com.sap.cloud.sdk.service.prov.api.request.ReadRequest;
import com.sap.cloud.sdk.service.prov.api.request.UpdateRequest;
import com.sap.cloud.sdk.service.prov.api.response.ErrorResponse;
import com.sap.cloud.sdk.service.prov.api.response.QueryResponse;
import com.sap.cloud.sdk.service.prov.api.response.ReadResponse;
import com.sap.cloud.sdk.service.prov.api.response.UpdateResponse;
public class ServiceImplementationGeneric {
private static final List<Map<String, Object>> peopleList = new ArrayList<Map<String, Object>>();
@Query(serviceName="DemoService", entity="People")
public QueryResponse getPeople(QueryRequest request) {
List<Map<String, Object>> peopleList = getAllPeople();
return QueryResponse.setSuccess().setDataAsMap(peopleList).response();
}
@Read(serviceName="DemoService", entity="People")
public ReadResponse getPerson(ReadRequest request) {
Integer id = (Integer) request.getKeys().get("PersonId");
Map<String, Object> personMap = findPerson(id);
return ReadResponse.setSuccess().setData(personMap).response();
}
@Update(serviceName = "DemoService", entity = "People")
public UpdateResponse updatePerson(UpdateRequest request) {
Map<String, Object> requestBody = request.getMapData();
// get properties which are missing in request body. In case of PATCH: empty list.
List<String> missingProperties = propertiesMissingInRequestBody(request);
for(String missingProperty : missingProperties) {
// in case of PUT, handle those missing properties, which require special treatment
if(missingProperty.equals("PersonId")) {
continue;
}else if(missingProperty.equals("Email")) {
requestBody.put(missingProperty, "Unknown");//reset to default value
// ... etc
}else if(missingProperty.equals("FirstName")) { // handle non-nullable field
return UpdateResponse.setError(ErrorResponse.getBuilder().setMessage("Error in request: property FirstName must not be null").setStatusCode(400).response());
}else {
requestBody.put(missingProperty, null); // all others reset to null
}
}
requestBody.remove("PersonId"); // can still be there
// now that the request body is complete, we can move over to daily work
Integer personIdForUpdate = (Integer)request.getKeys().get("PersonId");
Map<String, Object> existingPersonMap = findPerson(personIdForUpdate);
// do checks on existingPerson
// do update
doUpdateInBackend(requestBody, existingPersonMap);
return UpdateResponse.setSuccess().response();
}
private void doUpdateInBackend(Map<String, Object> newData, Map<String, Object> personToBeModified) {
Set<String> newDataProperties = newData.keySet();
for (Iterator<String> iterator = newDataProperties.iterator(); iterator.hasNext();) {
String propertyName = (String) iterator.next();
personToBeModified.replace(propertyName, newData.get(propertyName)); // this will replace also values passed as null
}
}
private List<String> propertiesMissingInRequestBody(UpdateRequest request){
// as specified, in case of PATCH, missing props don't need to be handled, so return empty list
if(request.getHttpMethod().equals("PATCH")) {
return Collections.emptyList();
}
// in case of PUT, we collect the properties which are defined for the entity, but missing in the request body
List<String> missingPropertiesList = new ArrayList<String>();
List<String> allPropertiesList = request.getEntityMetadata().getElementNames();
Map<String, Object> requestBodyMap = request.getMapData();
for(String definedProperty : allPropertiesList) {
if (! requestBodyMap.containsKey(definedProperty)) {
missingPropertiesList.add(definedProperty);
}
}
return missingPropertiesList;
}
/* Dummy Database */
private List<Map<String, Object>> getAllPeople(){
// init the "database"
if(peopleList.isEmpty()) {
createPerson(1, "Anna", "Martinez", "Anna@mail.com", 1);
createPerson(2, "Berta", "Hughes", "Berta@webmail.com", 2);
createPerson(3, "Claudia", "Forrester", "CF@fastmail.com", 1);
createPerson(4, "Debbie", "Huffington", "debbie@dbmail.com", 2);
}
return peopleList;
}
// used for READ operation. Go through the list and find the person with requested PersonId
private Map<String, Object> findPerson(Integer requiredPersonId){
List<Map<String,Object>> peopleList = getAllPeople();
for(Map<String, Object> personMap : peopleList) {
if(requiredPersonId.equals((Integer)personMap.get("PersonId"))) {
return personMap;
}
}
return null;
}
private Map<String, Object> createPerson(Integer personId, String firstName, String lastName, String mail, Integer jobId){
Map<String, Object> personMap = new HashMap<String, Object>();
personMap.put("PersonId", personId);
personMap.put("FirstName", firstName);
personMap.put("LastName", lastName);
personMap.put("JobId", jobId);
if(mail != null) {
personMap.put("Email", mail);
}else {
personMap.put("Email", "Unknown"); //default value which we've specified in edmx file
}
peopleList.add(personMap);
return personMap;
}
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
13 | |
11 | |
10 | |
9 | |
9 | |
7 | |
6 | |
5 | |
5 | |
5 |