Skip to content

Commit 1ce33ac

Browse files
KB-11624 | BE | DEV | API for Creating consent acknowledgement and reading the consent details. (#101)
* KB-11624 | BE | DEV | API for Creating consent acknowledgement and reading the consent details. 1. Created two new APIS - consent acknowledgement and consent read. 2. Implemented cassandra insertrecord method based on the primarykey and composite key. 3. Implemented the test cases. * Updated the response structure * Test cases updated. * Code review comments fixed. 1. Prepared Statement is being replaced with the SImpleStatement. 2. Version of the Controller mappings. 3. When there is an exception validate the return response. 4. Updated the test cases. * Consent Acknowledge read API added. 1. added new API for consent acknowledge read API. 2. Added test cases.
1 parent 32a1328 commit 1ce33ac

File tree

10 files changed

+835
-7
lines changed

10 files changed

+835
-7
lines changed

src/main/java/com/igot/cb/cassandra/CassandraOperation.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,22 @@ public Map<String, Object> updateRecord(String keyspaceName, String tableName,
3434

3535
public void deleteRecord(String keyspaceName, String tableName, Map<String, Object> keyMap);
3636

37+
/**
38+
* Inserts a record into Cassandra with a composite primary key.
39+
*
40+
* @param keyspaceName The name of the keyspace containing the table.
41+
* @param tableName The name of the table into which to insert the record.
42+
* @param primaryKeyColumn The name of the primary key column.
43+
* @param primaryKeyValue The value of the primary key.
44+
* @param compositeKey A map representing the composite key fields and their values.
45+
* @param otherFields A map representing other fields and their values to be inserted.
46+
* @return An object representing the result of the insertion operation.
47+
*/
48+
Object insertRecord(
49+
String keyspaceName,
50+
String tableName,
51+
String primaryKeyColumn,
52+
String primaryKeyValue,
53+
Map<String, Object> compositeKey,
54+
Map<String, Object> otherFields);
3755
}

src/main/java/com/igot/cb/cassandra/CassandraOperationImpl.java

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import com.datastax.oss.driver.api.core.cql.*;
66
import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
77
import com.datastax.oss.driver.api.querybuilder.delete.Delete;
8-
import com.datastax.oss.driver.api.querybuilder.delete.DeleteSelection;
98
import com.datastax.oss.driver.api.querybuilder.relation.Relation;
109
import com.datastax.oss.driver.api.querybuilder.select.Select;
1110
import com.datastax.oss.driver.api.querybuilder.term.Term;
@@ -19,14 +18,10 @@
1918

2019
import org.apache.commons.collections.CollectionUtils;
2120
import org.apache.commons.collections.MapUtils;
22-
import org.slf4j.Logger;
23-
import org.slf4j.LoggerFactory;
2421
import org.springframework.beans.factory.annotation.Autowired;
2522
import org.springframework.stereotype.Component;
2623

27-
import java.text.MessageFormat;
2824
import java.util.*;
29-
import java.util.Map.Entry;
3025
import java.util.stream.Collectors;
3126

3227

@@ -199,4 +194,51 @@ public void deleteRecord(String keyspaceName, String tableName, Map<String, Obje
199194
}
200195
}
201196

197+
/**
198+
* Inserts a record into Cassandra with a composite primary key.
199+
*
200+
* @param keyspaceName The name of the keyspace containing the table.
201+
* @param tableName The name of the table into which to insert the record.
202+
* @param primaryKeyColumn The name of the primary key column.
203+
* @param primaryKeyValue The value of the primary key.
204+
* @param compositeKey A map representing the composite key fields and their values.
205+
* @param otherFields A map representing other fields and their values to be inserted.
206+
* @return An object representing the result of the insertion operation.
207+
*/
208+
@Override
209+
public Object insertRecord(
210+
String keyspaceName,
211+
String tableName,
212+
String primaryKeyColumn,
213+
String primaryKeyValue,
214+
Map<String, Object> compositeKey,
215+
Map<String, Object> otherFields) {
216+
ApiResponse response = new ApiResponse();
217+
try {
218+
Map<String, Object> request = new LinkedHashMap<>();
219+
request.put(primaryKeyColumn, primaryKeyValue);
220+
if (MapUtils.isNotEmpty(compositeKey)) {
221+
request.putAll(compositeKey);
222+
}
223+
if (MapUtils.isNotEmpty(otherFields)) {
224+
request.putAll(otherFields);
225+
}
226+
String query = CassandraUtil.getPreparedStatement(keyspaceName, tableName, request);
227+
CqlSession session = connectionManager.getSession(keyspaceName);
228+
SimpleStatement simpleStatement = SimpleStatement.builder(query)
229+
.addPositionalValues(request.values())
230+
.build();
231+
session.execute(simpleStatement);
232+
response.put(Constants.RESPONSE, Constants.SUCCESS);
233+
} catch (Exception e) {
234+
String errMsg = String.format(
235+
"Exception occurred while inserting record into %s. Error: %s",
236+
tableName, e.getMessage());
237+
log.error("Error inserting record into {}: {}", tableName, e.getMessage(), e);
238+
response.put(Constants.RESPONSE, Constants.FAILED);
239+
response.put(Constants.ERROR_MESSAGE, errMsg);
240+
}
241+
return response;
242+
}
243+
202244
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.igot.cb.consentacknowledge;
2+
3+
import com.igot.cb.model.ApiResponse;
4+
import com.igot.cb.util.Constants;
5+
import org.springframework.http.ResponseEntity;
6+
import org.springframework.web.bind.annotation.*;
7+
8+
import java.util.Map;
9+
10+
@RestController
11+
@RequestMapping("/v1/consent")
12+
public class ConsentAcknowledgeController {
13+
14+
private final IConsentAcknowledgeService acknowledgeService;
15+
16+
public ConsentAcknowledgeController(IConsentAcknowledgeService acknowledgeService) {
17+
this.acknowledgeService = acknowledgeService;
18+
}
19+
20+
@PostMapping("/acknowledge")
21+
public ResponseEntity<ApiResponse> acknowledgeDeclaration(
22+
@RequestHeader(value = Constants.X_AUTH_TOKEN) String authToken,
23+
@RequestBody Map<String, Object> request) {
24+
ApiResponse response = acknowledgeService.acknowledgeDeclaration(request, authToken);
25+
return new ResponseEntity<>(response, response.getResponseCode());
26+
}
27+
28+
29+
@GetMapping("/details/{consentId}")
30+
public ResponseEntity<ApiResponse> getConsentDetails(@PathVariable String consentId,
31+
@RequestHeader(value = Constants.X_AUTH_TOKEN) String authToken) {
32+
ApiResponse response = acknowledgeService.getConsentDetails(consentId, authToken);
33+
return new ResponseEntity<>(response, response.getResponseCode());
34+
}
35+
36+
37+
@GetMapping("/acknowledge/read/{contentId}/{consentId}")
38+
public ResponseEntity<ApiResponse> getConsentAcknowledgementDetails(@PathVariable(Constants.CONSENT_ID) String consentId,
39+
@PathVariable(Constants.CONTENT_ID) String contentId,
40+
@RequestHeader(value = Constants.X_AUTH_TOKEN) String authToken) {
41+
ApiResponse response = acknowledgeService.getConsentAcknowledgementDetails(contentId, consentId, authToken);
42+
return new ResponseEntity<>(response, response.getResponseCode());
43+
}
44+
45+
}
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package com.igot.cb.consentacknowledge;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.core.type.TypeReference;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.igot.cb.cassandra.CassandraOperation;
7+
import com.igot.cb.model.ApiResponse;
8+
import com.igot.cb.util.AccessTokenValidator;
9+
import com.igot.cb.util.Constants;
10+
import com.igot.cb.util.ProjectUtil;
11+
import org.apache.commons.collections.MapUtils;
12+
import org.apache.commons.lang3.StringUtils;
13+
import org.slf4j.Logger;
14+
import org.slf4j.LoggerFactory;
15+
import org.springframework.http.HttpStatus;
16+
import org.springframework.stereotype.Service;
17+
18+
import java.time.Instant;
19+
import java.time.ZoneId;
20+
import java.time.ZonedDateTime;
21+
import java.time.format.DateTimeFormatter;
22+
import java.util.Arrays;
23+
import java.util.HashMap;
24+
import java.util.List;
25+
import java.util.Map;
26+
27+
@Service
28+
public class ConsentAcknowledgeServiceImpl implements IConsentAcknowledgeService {
29+
30+
private final Logger logger = LoggerFactory.getLogger(ConsentAcknowledgeServiceImpl.class);
31+
32+
33+
private final AccessTokenValidator accessTokenValidator;
34+
private final CassandraOperation cassandraOperation;
35+
private final ObjectMapper objectMapper;
36+
37+
public ConsentAcknowledgeServiceImpl(AccessTokenValidator accessTokenValidator, CassandraOperation cassandraOperation, ObjectMapper objectMapper) {
38+
this.cassandraOperation = cassandraOperation;
39+
this.accessTokenValidator = accessTokenValidator;
40+
this.objectMapper = objectMapper;
41+
}
42+
43+
/**
44+
* Method to acknowledge declaration
45+
* Required fields in request body : contentId, consentId
46+
* Optional fields in request body : additionalData
47+
*/
48+
@Override
49+
public ApiResponse acknowledgeDeclaration(Map<String, Object> requestDataBody, String authToken) {
50+
ApiResponse response = ProjectUtil.createDefaultResponse("api.consent.acknowledge.create");
51+
Map<String, Object> requestDataMap = (Map<String, Object>) requestDataBody.get(Constants.REQUEST);
52+
String userId = accessTokenValidator.fetchUserIdFromAccessToken(authToken, response);
53+
if (StringUtils.isEmpty(userId)) {
54+
return response;
55+
}
56+
String errMsg = validateAcknowledgeDeclarationRequest(requestDataMap);
57+
if (!StringUtils.isEmpty(errMsg)) {
58+
ProjectUtil.errorResponse(response, errMsg, HttpStatus.BAD_REQUEST);
59+
return response;
60+
}
61+
String contentId = (String) requestDataMap.get(Constants.CONTENT_ID);
62+
String consentId = (String) requestDataMap.get(Constants.CONSENT_ID);
63+
Map<String, Object> additionalData = (Map<String, Object>) requestDataMap.get(Constants.ADDITIONAL_ATTRIBUTES);
64+
String additionalDataStr = null;
65+
try {
66+
additionalDataStr = objectMapper.writeValueAsString(additionalData);
67+
} catch (JsonProcessingException e) {
68+
logger.error("Error while converting additionalData to string", e);
69+
ProjectUtil.errorResponse(response,"Failed to Parse the additional attributes", HttpStatus.BAD_REQUEST);
70+
return response;
71+
}
72+
Map<String, Object> compositeKeyMap = new HashMap<>();
73+
compositeKeyMap.put(Constants.USER_ID, userId);
74+
compositeKeyMap.put(Constants.CONSENT_ID, consentId);
75+
Map<String, Object> declarationDataMap = new HashMap<>();
76+
ZoneId zoneId = ZoneId.of("Asia/Kolkata");
77+
ZonedDateTime submittedAt = ZonedDateTime.ofInstant(Instant.now(), zoneId);
78+
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
79+
declarationDataMap.put(Constants.SUBMITTED_BY, userId);
80+
declarationDataMap.put(Constants.ADDITIONAL_ATTRIBUTES, additionalDataStr);
81+
declarationDataMap.put(Constants.SUBMITTED_AT, submittedAt.format(formatter));
82+
try {
83+
response = (ApiResponse) cassandraOperation.insertRecord(Constants.KEYSPACE_SUNBIRD, Constants.TABLE_DECLARATION_ACKNOWLEDGMENT, Constants.CONTENT_ID, contentId, compositeKeyMap, declarationDataMap);
84+
if (Constants.FAILED.equals(response.get(Constants.RESPONSE))) {
85+
logger.error("Error while inserting declaration acknowledgment record in DB: {}",
86+
response.get(Constants.ERROR_MESSAGE));
87+
ProjectUtil.errorResponse(
88+
response,
89+
"Failed to acknowledge declaration. Please try again later.",
90+
HttpStatus.INTERNAL_SERVER_ERROR
91+
);
92+
return response;
93+
}
94+
} catch (Exception e) {
95+
logger.error("Error while inserting declaration acknowledgment record in DB", e);
96+
ProjectUtil.errorResponse(response, "Failed to acknowledge declaration. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR);
97+
return response;
98+
}
99+
response.getParams().setStatus(Constants.OK);
100+
response.setResponseCode(HttpStatus.OK);
101+
Map<String, Object> result = new HashMap<>();
102+
Map<String, Object> consentAckDetailsMap = new HashMap<>();
103+
consentAckDetailsMap.put(Constants.CONTENT_ID, contentId);
104+
consentAckDetailsMap.put(Constants.CONSENT_ID, consentId);
105+
consentAckDetailsMap.put(Constants.USER_ID, userId);
106+
consentAckDetailsMap.put(Constants.MESSAGE, "Declaration acknowledged successfully");
107+
result.put(Constants.RESPONSE,consentAckDetailsMap);
108+
response.getResult().putAll(result);
109+
return response;
110+
}
111+
112+
/**
113+
* Method to validate acknowledge declaration request
114+
*/
115+
private String validateAcknowledgeDeclarationRequest(Map<String, Object> requestData) {
116+
if (requestData == null) {
117+
return "Request data is missing.";
118+
}
119+
if (!requestData.containsKey(Constants.CONTENT_ID)
120+
|| StringUtils.isEmpty(requestData.get(Constants.CONTENT_ID).toString())) {
121+
return "Missing or invalid contentId.";
122+
}
123+
if (!requestData.containsKey(Constants.CONSENT_ID)
124+
|| StringUtils.isEmpty(requestData.get(Constants.CONSENT_ID).toString())) {
125+
return "Missing or invalid consentId.";
126+
}
127+
if (requestData.containsKey(Constants.ADDITIONAL_ATTRIBUTES)) {
128+
Map<String, Object> additionalData = (Map<String, Object>) requestData.get(Constants.ADDITIONAL_ATTRIBUTES);
129+
if (MapUtils.isEmpty(additionalData)) {
130+
return "Missing or invalid additionalData.";
131+
}
132+
}
133+
return "";
134+
}
135+
136+
137+
/**
138+
* Method to get consent details by consentId
139+
*/
140+
@Override
141+
public ApiResponse getConsentDetails(String consentId, String authToken) {
142+
ApiResponse response = ProjectUtil.createDefaultResponse("api.consent.read");
143+
String userId = accessTokenValidator.fetchUserIdFromAccessToken(authToken, response);
144+
if (StringUtils.isEmpty(userId)) {
145+
return response;
146+
}
147+
Map<String, Object> propertyMap = new HashMap<>();
148+
propertyMap.put(Constants.CONSENT_ID, consentId);
149+
List<Map<String, Object>> consentDetails;
150+
try {
151+
consentDetails = cassandraOperation
152+
.getRecordsByProperties(Constants.KEYSPACE_SUNBIRD, Constants.TABLE_CONSENT_DETAILS, propertyMap, Arrays.asList(Constants.CONSENT_ID, Constants.ADDITIONAL_DATA, Constants.DESCRIPTION), null);
153+
} catch (Exception e) {
154+
logger.error("Error while fetching consent details from DB", e);
155+
ProjectUtil.errorResponse(response, "Failed to fetch consent details. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR);
156+
return response;
157+
}
158+
Map<String, Object> consentDetailsMap = consentDetails.get(0);
159+
response.getParams().setStatus(Constants.OK);
160+
response.setResponseCode(HttpStatus.OK);
161+
Map<String, Object> result = new HashMap<>();
162+
result.put(Constants.RESPONSE, consentDetailsMap);
163+
response.getResult().putAll(result);
164+
return response;
165+
}
166+
167+
168+
/**
169+
* Method to get consent acknowledgement details by contentId and consentId
170+
*/
171+
@Override
172+
public ApiResponse getConsentAcknowledgementDetails(String contentId, String consentId, String authToken) {
173+
ApiResponse response = ProjectUtil.createDefaultResponse("api.consent.acknowledgement.read");
174+
String userId = accessTokenValidator.fetchUserIdFromAccessToken(authToken, response);
175+
if (StringUtils.isEmpty(userId)) {
176+
return response;
177+
}
178+
Map<String, Object> propertyMap = new HashMap<>();
179+
propertyMap.put(Constants.CONSENT_ID, consentId);
180+
propertyMap.put(Constants.CONTENT_ID, contentId);
181+
propertyMap.put(Constants.USER_ID, userId);
182+
List<Map<String, Object>> consentAcknowledgementDetailsList;
183+
try {
184+
consentAcknowledgementDetailsList = cassandraOperation
185+
.getRecordsByProperties(Constants.KEYSPACE_SUNBIRD, Constants.TABLE_DECLARATION_ACKNOWLEDGMENT, propertyMap, Arrays.asList(Constants.CONTENT_ID, Constants.CONSENT_ID, Constants.USER_ID, Constants.ADDITIONAL_ATTRIBUTES), null);
186+
} catch (Exception e) {
187+
logger.error("Error while fetching consent details from DB", e);
188+
ProjectUtil.errorResponse(response, "Failed to fetch consent Acknowledgement details. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR);
189+
return response;
190+
}
191+
Map<String, Object> consentAcknowledgementDetailsMap = consentAcknowledgementDetailsList.get(0);
192+
String additionAttributesStr = (String) consentAcknowledgementDetailsMap.get(Constants.ADDITIONAL_ATTRIBUTES);
193+
Map<String, Object> additionalAttributesMap = null;
194+
try {
195+
additionalAttributesMap = objectMapper.readValue(additionAttributesStr, new TypeReference<>() {
196+
});
197+
} catch (JsonProcessingException e) {
198+
logger.error("Error while parsing additionalAttributes from string to map", e);
199+
ProjectUtil.errorResponse(response, "Failed to Parse the additional attributes", HttpStatus.INTERNAL_SERVER_ERROR);
200+
return response;
201+
}
202+
consentAcknowledgementDetailsMap.put(Constants.ADDITIONAL_ATTRIBUTES, additionalAttributesMap);
203+
response.getParams().setStatus(Constants.OK);
204+
response.setResponseCode(HttpStatus.OK);
205+
Map<String, Object> result = new HashMap<>();
206+
result.put(Constants.RESPONSE, consentAcknowledgementDetailsMap);
207+
response.getResult().putAll(result);
208+
return response;
209+
}
210+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.igot.cb.consentacknowledge;
2+
3+
import com.igot.cb.model.ApiResponse;
4+
5+
import java.util.Map;
6+
7+
public interface IConsentAcknowledgeService {
8+
9+
/**
10+
* Acknowledge a declaration based on the provided request data and authentication token.
11+
*
12+
* @param request A map containing the request data for acknowledging the declaration.
13+
* @param authToken The authentication token for validating the request.
14+
* @return An ApiResponse object containing the result of the acknowledgment operation.
15+
*/
16+
ApiResponse acknowledgeDeclaration(Map<String, Object> request, String authToken);
17+
18+
/**
19+
* Retrieve consent details based on the provided consent ID and authentication token.
20+
*
21+
* @param consentId The ID of the consent to retrieve details for.
22+
* @param authToken The authentication token for validating the request.
23+
* @return An ApiResponse object containing the consent details.
24+
*/
25+
ApiResponse getConsentDetails(String consentId, String authToken);
26+
27+
28+
/**
29+
* Retrieve consent acknowledgement details based on the provided content ID, consent ID, and authentication token.
30+
*
31+
* @param contentId The ID of the content to retrieve details for.
32+
* @param consentId The ID of the consent to retrieve details for.
33+
* @param authToken The authentication token for validating the request.
34+
* @return An ApiResponse object containing the consent acknowledgement details.
35+
*/
36+
ApiResponse getConsentAcknowledgementDetails(String contentId, String consentId, String authToken);
37+
}

0 commit comments

Comments
 (0)