-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
added code formatter #79
Conversation
WalkthroughThe changes primarily focus on code formatting and readability improvements across various classes in the project. Some notable updates include adding a new dependency and plugin to the Maven configuration, CORS configuration for localhost:3000, and modifications to methods in the Changes
TipsChat with CodeRabbit Bot (
|
private CreateTaskFormatterDto parseResponse(String createTaskResponse) { | ||
Util util = new Util(); | ||
JsonNode rootNode = util.getJsonNode(createTaskResponse); | ||
|
||
logger.info("parsing response from salesforce"); | ||
|
||
JsonNode createTaskCompositeResponse = rootNode.get("compositeResponse").get(0); | ||
String createTaskStatusCode = createTaskCompositeResponse.get("httpStatusCode").asText(); | ||
|
||
if (!createTaskStatusCode.equals("200") && !createTaskStatusCode.equals("201")) { | ||
String errorBody = createTaskCompositeResponse.get("body").asText(); | ||
|
||
throw new CustomException(new ErrorObject("l_ca_ct_cst_1", "internal_server_error", errorBody)); | ||
} | ||
|
||
JsonNode createTaskNodeResponseBody = rootNode.get("compositeResponse").get(0).get("body"); | ||
|
||
ObjectMapper mapper = new ObjectMapper(); | ||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); | ||
SalesforceCreateTaskDto salesforceCreateTaskDto = mapper.convertValue(createTaskNodeResponseBody, | ||
SalesforceCreateTaskDto.class); | ||
|
||
CreateTaskFormatterDto createTaskFormatterDto = new CreateTaskFormatterDto(); | ||
createTaskFormatterDto.setTaskId(salesforceCreateTaskDto.getId()); | ||
|
||
return createTaskFormatterDto; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the parseResponse
method, you are creating a new instance of Util
every time the method is called. If Util
is stateless, consider making its methods static or autowiring a single instance of Util
to improve performance.
- Util util = new Util();
+ @Autowired
+ private Util util;
private String getTaskSubjectFromDescription(CreateAccountTaskDto task) { | ||
logger.info("getting task subject from description"); | ||
if (task.getDescription().length() < 60) { | ||
return task.getDescription(); | ||
} | ||
|
||
return task.getDescription().substring(0, 60); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The getTaskSubjectFromDescription
method is correctly implemented. It checks if the description length is less than 60 characters and returns the appropriate string. However, it would be better to handle null descriptions to avoid NullPointerExceptions.
- if (task.getDescription().length() < 60) {
+ if (task.getDescription() != null && task.getDescription().length() < 60) {
public void deleteAccountTask(User user, String accountId, String taskId) { | ||
logger.info("Delete Salesforce Account Task called"); | ||
|
||
String salesforceUserId = user.getExternalUserId(); | ||
|
||
String url = salesforceConstants.salesforceDeleteAccountTaskUrl(taskId); | ||
|
||
CompositeRequestDto compositeReq = new CompositeRequestDto("DELETE", url, "DeleteTask"); | ||
|
||
List<CompositeRequestDto> compositeRequests = new ArrayList<CompositeRequestDto>(); | ||
compositeRequests.add(compositeReq); | ||
|
||
HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); | ||
|
||
parseResponse(response.getResponseBody()); | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The deleteAccountTask
method creates a new list of CompositeRequestDto
for each call. If this method is called frequently, it could lead to unnecessary memory allocation and garbage collection. Consider reusing the list if possible or using a pool of lists.
- List<CompositeRequestDto> compositeRequests = new ArrayList<CompositeRequestDto>();
+ List<CompositeRequestDto> compositeRequests = getCompositeRequestList();
In the above code, getCompositeRequestList()
should be a method that manages a pool of CompositeRequestDto
lists.
private void parseResponse(String responseBody) { | ||
logger.info("Parsing response body"); | ||
Util util = new Util(); | ||
JsonNode rootNode = util.getJsonNode(responseBody); | ||
|
||
JsonNode deleteNoteCompositeResponse = rootNode.get("compositeResponse").get(0); | ||
Integer deleteNoteStatusCode = deleteNoteCompositeResponse.get("httpStatusCode").asInt(); | ||
|
||
if (deleteNoteStatusCode != 200 && deleteNoteStatusCode != 201 && deleteNoteStatusCode != 204) { | ||
String errorBody = deleteNoteCompositeResponse.get("body").asText(); | ||
|
||
// MALFORMED_ID or NOT_FOUND | ||
if (deleteNoteStatusCode == 400 || deleteNoteStatusCode == 404) { | ||
|
||
throw new CustomException( | ||
new ParamErrorObject("l_ca_dan_dasn_pr_1", errorBody, Arrays.asList("invalid_task_id"))); | ||
} | ||
else { | ||
throw new CustomException(new ErrorObject("l_ca_dan_dasn_pr_2", "something_went_wrong", errorBody)); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parseResponse
method creates a new Util
object for each call. If this method is called frequently, it could lead to unnecessary memory allocation and garbage collection. Consider making the Util
class methods static or creating a single instance of Util
and reusing it.
- Util util = new Util();
+ Util util = getUtilInstance();
In the above code, getUtilInstance()
should be a method that returns a singleton instance of Util
.
private HttpClient.HttpResponse notesResponse(String noteId, String salesforceUserId) { | ||
SalesforceQueryBuilder salesforceLib = new SalesforceQueryBuilder(); | ||
String notesQuery = salesforceLib.getNoteDetailsUrl(noteId); | ||
|
||
String notesUrl = salesforceConstants.queryUrlPath() + notesQuery; | ||
|
||
CompositeRequestDto noteCompositeRequest = new CompositeRequestDto("GET", notesUrl, "GetNotesList"); | ||
|
||
List<CompositeRequestDto> compositeRequests = new ArrayList<CompositeRequestDto>(); | ||
compositeRequests.add(noteCompositeRequest); | ||
|
||
HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); | ||
|
||
return response; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method notesResponse
is making a POST request using makeCompositeRequest.makePostRequest
. However, the HTTP method specified in noteCompositeRequest
is GET. This could lead to unexpected behavior. Please ensure that the correct HTTP method is used for this request.
- HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId);
+ HttpClient.HttpResponse response = makeCompositeRequest.makeGetRequest(compositeRequests, salesforceUserId);
private GetNoteDetailsFormatterDto parseResponse(String noteDetailsResponse, String noteContentResponse) { | ||
NoteDetailEntity noteDetailEntity = new NoteDetailEntity(); | ||
try { | ||
Util util = new Util(); | ||
JsonNode rootNode = util.getJsonNode(noteDetailsResponse); | ||
JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); | ||
|
||
for (JsonNode recordNode : recordsNode) { | ||
ObjectMapper mapper = new ObjectMapper(); | ||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); | ||
SalesforceGetNoteDetailsDto salesforceGetNotesList = mapper.convertValue(recordNode, | ||
SalesforceGetNoteDetailsDto.class); | ||
noteDetailEntity = salesforceGetNotesList.noteDetailEntity(noteContentResponse); | ||
} | ||
if (recordsNode.size() == 0) { | ||
throw new CustomException(new ErrorObject("l_c_gnd_gsnd_1", "something_went_wrong", "Note not found")); | ||
} | ||
} | ||
catch (Exception e) { | ||
throw new CustomException(new ErrorObject("l_c_gnd_gsnd_2", "something_went_wrong", e.getMessage())); | ||
} | ||
|
||
GetNoteDetailsFormatterDto getNoteDetailsFormatterDto = new GetNoteDetailsFormatterDto(noteDetailEntity); | ||
|
||
return getNoteDetailsFormatterDto; | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the parseResponse
method, the ObjectMapper instance is created inside the loop which is not efficient. It's better to create it once outside the loop and reuse it. Also, disabling DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
can be done at the time of creation.
- for (JsonNode recordNode : recordsNode) {
- ObjectMapper mapper = new ObjectMapper();
- mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
- SalesforceGetNoteDetailsDto salesforceGetNotesList = mapper.convertValue(recordNode,
- SalesforceGetNoteDetailsDto.class);
- noteDetailEntity = salesforceGetNotesList.noteDetailEntity(noteContentResponse);
- }
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ for (JsonNode recordNode : recordsNode) {
+ SalesforceGetNoteDetailsDto salesforceGetNotesList = mapper.convertValue(recordNode,
+ SalesforceGetNoteDetailsDto.class);
+ noteDetailEntity = salesforceGetNotesList.noteDetailEntity(noteContentResponse);
+ }
public GetAccountsFormatterDto getAccounts(User user, String searchTerm) { | ||
String salesforceUserId = user.getExternalUserId(); | ||
|
||
SalesforceQueryBuilder salesforceQuery = new SalesforceQueryBuilder(); | ||
String query = salesforceQuery.getAccountsQuery(searchTerm); | ||
|
||
String url = salesforceConstants.queryUrlPath() + query; | ||
|
||
CompositeRequestDto compositeReq = new CompositeRequestDto("GET", url, "getAccounts"); | ||
|
||
List<CompositeRequestDto> compositeRequests = new ArrayList<CompositeRequestDto>(); | ||
compositeRequests.add(compositeReq); | ||
|
||
HttpClient.HttpResponse response = makeCompositeRequest.makePostRequest(compositeRequests, salesforceUserId); | ||
|
||
return parseResponse(response.getResponseBody()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The getAccounts
method constructs a Salesforce query, sends a GET request to the Salesforce API, and parses the response. The HTTP method is hardcoded as "GET" in the CompositeRequestDto
object creation. Consider making this a constant or configurable if it's likely to change or be reused elsewhere.
public GetAccountsFormatterDto parseResponse(String responseBody) { | ||
|
||
List<String> accountIds = new ArrayList<String>(); | ||
Map<String, AccountEntity> accountIdToEntityMap = new HashMap<>(); | ||
|
||
Util util = new Util(); | ||
JsonNode rootNode = util.getJsonNode(responseBody); | ||
|
||
JsonNode httpStatusCodeNode = rootNode.get("compositeResponse").get(0).get("httpStatusCode"); | ||
|
||
if (httpStatusCodeNode.asInt() != 200 && httpStatusCodeNode.asInt() != 201) { | ||
throw new CustomException(new ErrorObject("l_ca_ga_gsa_pr_1", "something_went_wrong", | ||
"Error in fetching accounts from salesforce")); | ||
} | ||
|
||
JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); | ||
|
||
for (JsonNode recordNode : recordsNode) { | ||
ObjectMapper mapper = new ObjectMapper(); | ||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); | ||
SalesforceAccountDto salesforceAccount = mapper.convertValue(recordNode, SalesforceAccountDto.class); | ||
AccountEntity accountEntity = salesforceAccount.getAccountEntity(); | ||
|
||
accountIds.add(accountEntity.getId()); | ||
accountIdToEntityMap.put(accountEntity.getId(), accountEntity); | ||
} | ||
|
||
GetAccountsFormatterDto getAccountsResponse = new GetAccountsFormatterDto(); | ||
getAccountsResponse.setAccountMapById(accountIdToEntityMap); | ||
getAccountsResponse.setAccountIds(accountIds); | ||
|
||
return getAccountsResponse; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the parseResponse
method, an ObjectMapper instance is created for each record in the loop (line 86). This can be inefficient as ObjectMapper is thread-safe and can be reused. Consider moving the ObjectMapper instantiation outside of the loop.
- for (JsonNode recordNode : recordsNode) {
- ObjectMapper mapper = new ObjectMapper();
- mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
- SalesforceAccountDto salesforceAccount = mapper.convertValue(recordNode, SalesforceAccountDto.class);
- AccountEntity accountEntity = salesforceAccount.getAccountEntity();
-
- accountIds.add(accountEntity.getId());
- accountIdToEntityMap.put(accountEntity.getId(), accountEntity);
- }
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ for (JsonNode recordNode : recordsNode) {
+ SalesforceAccountDto salesforceAccount = mapper.convertValue(recordNode, SalesforceAccountDto.class);
+ AccountEntity accountEntity = salesforceAccount.getAccountEntity();
+
+ accountIds.add(accountEntity.getId());
+ accountIdToEntityMap.put(accountEntity.getId(), accountEntity);
+ }
@Override | ||
public String toString() { | ||
return "ErrorInfo{" + "http_code='" + httpCode + '\'' + ", code='" + code + '\'' + ", message='" + message | ||
+ '\'' + '}'; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The toString()
method has been reformatted for better readability. However, the string concatenation operation in this method could be improved by using StringBuilder
or String.format()
, which are more efficient and readable.
@Override
public String toString() {
return String.format("ErrorInfo{http_code='%s', code='%s', message='%s'}", httpCode, code, message);
}
public GetTasksListFormatterDto parseResponse(String responseBody) { | ||
|
||
List<String> taskIds = new ArrayList<String>(); | ||
Map<String, TaskEntity> taskIdToEntityMap = new HashMap<>(); | ||
|
||
Util util = new Util(); | ||
JsonNode rootNode = util.getJsonNode(responseBody); | ||
|
||
JsonNode getTasksCompositeResponse = rootNode.get("compositeResponse").get(0); | ||
Integer getTasksStatusCode = getTasksCompositeResponse.get("httpStatusCode").asInt(); | ||
|
||
if (getTasksStatusCode != 200 && getTasksStatusCode != 201) { | ||
String errorBody = getTasksCompositeResponse.get("body").asText(); | ||
|
||
if (getTasksStatusCode == 400) { | ||
throw new CustomException( | ||
new ParamErrorObject("l_ca_gatl_gsatl_pr_1", errorBody, Arrays.asList("invalid_account_id"))); | ||
} | ||
else { | ||
throw new CustomException(new ErrorObject("l_ca_gatl_gsatl_pr_2", "something_went_wrong", errorBody)); | ||
} | ||
} | ||
|
||
JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); | ||
; | ||
|
||
for (JsonNode recordNode : recordsNode) { | ||
ObjectMapper mapper = new ObjectMapper(); | ||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); | ||
SalesforceGetTasksListDto salesforceGetTasksListDto = mapper.convertValue(recordNode, | ||
SalesforceGetTasksListDto.class); | ||
TaskEntity taskEntity = salesforceGetTasksListDto.taskEntity(); | ||
|
||
taskIds.add(taskEntity.getId()); | ||
taskIdToEntityMap.put(taskEntity.getId(), taskEntity); | ||
} | ||
|
||
GetTasksListFormatterDto getTasksListFormatterDto = new GetTasksListFormatterDto(); | ||
getTasksListFormatterDto.setTaskMapById(taskIdToEntityMap); | ||
getTasksListFormatterDto.setTaskIds(taskIds); | ||
|
||
return getTasksListFormatterDto; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the parseResponse
method, an instance of ObjectMapper
is created inside the loop that iterates over the recordsNode
. Creating a new ObjectMapper
for each iteration can be inefficient and may impact performance. Consider moving the creation of the ObjectMapper
outside of the loop.
- for (JsonNode recordNode : recordsNode) {
- ObjectMapper mapper = new ObjectMapper();
- mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
- SalesforceGetTasksListDto salesforceGetTasksListDto = mapper.convertValue(recordNode,
- SalesforceGetTasksListDto.class);
- TaskEntity taskEntity = salesforceGetTasksListDto.taskEntity();
-
- taskIds.add(taskEntity.getId());
- taskIdToEntityMap.put(taskEntity.getId(), taskEntity);
- }
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ for (JsonNode recordNode : recordsNode) {
+ SalesforceGetTasksListDto salesforceGetTasksListDto = mapper.convertValue(recordNode,
+ SalesforceGetTasksListDto.class);
+ TaskEntity taskEntity = salesforceGetTasksListDto.taskEntity();
+
+ taskIds.add(taskEntity.getId());
+ taskIdToEntityMap.put(taskEntity.getId(), taskEntity);
+ }
@@ -5,9 +5,10 @@ | |||
@Component | |||
public class CookieConstants { | |||
|
|||
public static final String LATEST_VERSION = "1"; | |||
public static final String LATEST_VERSION = "1"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The LATEST_VERSION
constant is set to "1". If this value is expected to change over time, consider moving it to a configuration file or environment variable to avoid hardcoding.
|
||
public static final String USER_LOGIN_COOKIE_NAME = "ulcn"; | ||
public static final String USER_LOGIN_COOKIE_NAME = "ulcn"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The USER_LOGIN_COOKIE_NAME
is hardcoded as "ulcn". Consider moving this to a configuration file or environment variable for flexibility and security reasons.
public static final String USER_LOGIN_COOKIE_NAME = "ulcn"; | ||
public static final String USER_LOGIN_COOKIE_NAME = "ulcn"; | ||
|
||
public static final Integer USER_LOGIN_COOKIE_EXPIRY_IN_SEC = 30 * 60 * 60 * 24; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The USER_LOGIN_COOKIE_EXPIRY_IN_SEC
is hardcoded as 30 * 60 * 60 * 24 (equivalent to 30 days). If this value is expected to change based on different environments or requirements, consider moving it to a configuration file or environment variable.
@@ -5,11 +5,16 @@ | |||
@Component | |||
public class CacheConstants { | |||
|
|||
public static final String SalesSparrowPrefix = "ss_"; | |||
public static final String SalesSparrowPrefix = "ss_"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SalesSparrowPrefix
constant is public and static but not final, which means it can be modified. It's a good practice to make such constants immutable by declaring them as final.
- public static String SalesSparrowPrefix = "ss_";
+ public static final String SalesSparrowPrefix = "ss_";
public static final Integer SS_SALESFORCE_OAUTH_TOKEN_CACHE_EXP = 30 * 24 * 60 * 60; // 30 | ||
// days |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comments explaining the calculation for cache expiration are split across two lines. This could lead to confusion or misinterpretation. It would be better to keep the comment on the same line as the code it refers to.
- public static final Integer SS_SALESFORCE_USER_CACHE_EXP = 30 * 24 * 60 * 60; // 30
- // days
+ public static final Integer SS_SALESFORCE_USER_CACHE_EXP = 30 * 24 * 60 * 60; // 30 days
- public static final Integer SS_SALESFORCE_OAUTH_TOKEN_CACHE_EXP = 30 * 24 * 60 * 60; // 30
- // days
+ public static final Integer SS_SALESFORCE_OAUTH_TOKEN_CACHE_EXP = 30 * 24 * 60 * 60; // 30 days
private GetCrmOrganizationUsersFormatterDto parseResponse(String responseBody) { | ||
List<String> crmOrganizationUserIds = new ArrayList<String>(); | ||
Map<String, CrmOrganizationUserEntity> crmOrganizationUserMap = new HashMap<String, CrmOrganizationUserEntity>(); | ||
|
||
logger.info("Parsing response from salesforce"); | ||
|
||
Util util = new Util(); | ||
JsonNode rootNode = util.getJsonNode(responseBody); | ||
|
||
JsonNode httpStatusCodeNode = rootNode.get("compositeResponse").get(0).get("httpStatusCode"); | ||
|
||
if (httpStatusCodeNode.asInt() != 200 && httpStatusCodeNode.asInt() != 201) { | ||
throw new CustomException( | ||
new ErrorObject("l_ca_ga_gsa_pr_1", "bad_request", "Error in fetching accounts from salesforce")); | ||
} | ||
|
||
JsonNode recordsNode = rootNode.get("compositeResponse").get(0).get("body").get("records"); | ||
|
||
for (JsonNode recordNode : recordsNode) { | ||
ObjectMapper mapper = new ObjectMapper(); | ||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); | ||
SalesforceCrmOrganizationUserDto salesforceCrmOrganization = mapper.convertValue(recordNode, | ||
SalesforceCrmOrganizationUserDto.class); | ||
CrmOrganizationUserEntity accountCrmOrganizationUser = salesforceCrmOrganization | ||
.getCrmOrganizationUserEntity(); | ||
|
||
crmOrganizationUserIds.add(accountCrmOrganizationUser.getId()); | ||
crmOrganizationUserMap.put(accountCrmOrganizationUser.getId(), accountCrmOrganizationUser); | ||
} | ||
|
||
GetCrmOrganizationUsersFormatterDto getCrmOrganizationUsersResponse = new GetCrmOrganizationUsersFormatterDto(); | ||
getCrmOrganizationUsersResponse.setCrmOrganizationUserIds(crmOrganizationUserIds); | ||
getCrmOrganizationUsersResponse.setCrmOrganizationUserMapById(crmOrganizationUserMap); | ||
|
||
return getCrmOrganizationUsersResponse; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block of code parses the response from Salesforce. It assumes that the response will always have certain fields (compositeResponse
, httpStatusCode
, body
, records
). If any of these fields are missing in the response, it could lead to a NullPointerException
. Consider adding null checks or try-catch blocks to handle potential exceptions gracefully.
Overall, the changes look good and improve the readability and maintainability of the codebase.
public String chatCompletionUrl() { | ||
return "https://api.openai.com/v1/chat/completions"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The URL "https://api.openai.com/v1/chat/completions" is hardcoded. It would be better to move this to a configuration file or environment variable, so it can be changed without modifying the code.
- public String chatCompletionUrl() {
- return "https://api.openai.com/v1/chat/completions";
- }
+ public String chatCompletionUrl() {
+ return System.getenv("CHAT_COMPLETION_URL");
+ }
public Integer timeoutMillis() { | ||
return 20000; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timeout value of 20000 milliseconds is also hardcoded. This should also be moved to a configuration file or environment variable for flexibility.
- public Integer timeoutMillis() {
- return 20000;
- }
+ public Integer timeoutMillis() {
+ return Integer.parseInt(System.getenv("TIMEOUT_MILLIS"));
+ }
public static HttpResponse makeGetRequest(String url, Map<String, String> headers, int timeoutMillis) { | ||
|
||
ResponseEntity<String> responseEntity = responseMono.block(Duration.ofMillis(timeoutMillis)); | ||
WebClient webClient = WebClient.builder().build(); | ||
|
||
int statusCode = responseEntity.getStatusCode().value(); | ||
String responseBody = responseEntity.getBody(); | ||
Map<String, List<String>> responseHeaders = new HashMap<>(responseEntity.getHeaders()); | ||
String contentType = responseEntity.getHeaders().getContentType().toString(); | ||
WebClient.RequestHeadersSpec<?> request = webClient.get().uri(url); | ||
|
||
return new HttpResponse(statusCode, responseBody, responseHeaders, contentType); | ||
} | ||
if (headers != null) { | ||
request.headers(httpHeaders -> { | ||
headers.forEach(httpHeaders::set); | ||
}); | ||
} | ||
|
||
public static HttpResponse makePostRequest(String url, Map<String, String> headers, Object requestBody, | ||
int timeoutMillis) { | ||
WebClient webClient = WebClient.builder().build(); | ||
Mono<ResponseEntity<String>> responseMono = request.retrieve().toEntity(String.class); | ||
|
||
WebClient.RequestHeadersSpec<?> request = webClient.post() | ||
.uri(url) | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.bodyValue(requestBody); | ||
ResponseEntity<String> responseEntity = responseMono.block(Duration.ofMillis(timeoutMillis)); | ||
|
||
if (headers != null) { | ||
request.headers(httpHeaders -> { | ||
headers.forEach(httpHeaders::set); | ||
}); | ||
} | ||
int statusCode = responseEntity.getStatusCode().value(); | ||
String responseBody = responseEntity.getBody(); | ||
Map<String, List<String>> responseHeaders = new HashMap<>(responseEntity.getHeaders()); | ||
String contentType = responseEntity.getHeaders().getContentType().toString(); | ||
|
||
Mono<ResponseEntity<String>> responseMono = request | ||
.retrieve() | ||
.toEntity(String.class); | ||
return new HttpResponse(statusCode, responseBody, responseHeaders, contentType); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The WebClient instance is being created for each request. This is not efficient as the creation of WebClient is expensive and it's designed to be reused across multiple requests. Consider creating a single WebClient instance and reusing it for all requests.
- WebClient webClient = WebClient.builder().build();
+ private static final WebClient webClient = WebClient.builder().build();
public static HttpResponse makePostRequest(String url, Map<String, String> headers, Object requestBody, | ||
int timeoutMillis) { | ||
WebClient webClient = WebClient.builder().build(); | ||
|
||
int statusCode = responseEntity.getStatusCode().value(); | ||
String responseBody = responseEntity.getBody(); | ||
Map<String, List<String>> responseHeaders = new HashMap<>(responseEntity.getHeaders()); | ||
String contentType = responseEntity.getHeaders().getContentType().toString(); | ||
WebClient.RequestHeadersSpec<?> request = webClient.post() | ||
.uri(url) | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.bodyValue(requestBody); | ||
|
||
if (headers != null) { | ||
request.headers(httpHeaders -> { | ||
headers.forEach(httpHeaders::set); | ||
}); | ||
} | ||
|
||
Mono<ResponseEntity<String>> responseMono = request.retrieve().toEntity(String.class); | ||
|
||
ResponseEntity<String> responseEntity = responseMono.block(Duration.ofMillis(timeoutMillis)); | ||
|
||
int statusCode = responseEntity.getStatusCode().value(); | ||
String responseBody = responseEntity.getBody(); | ||
Map<String, List<String>> responseHeaders = new HashMap<>(responseEntity.getHeaders()); | ||
String contentType = responseEntity.getHeaders().getContentType().toString(); | ||
|
||
return new HttpResponse(statusCode, responseBody, responseHeaders, contentType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the above comment, the WebClient instance should be reused here as well.
- WebClient webClient = WebClient.builder().build();
+ // Use the existing WebClient instance
@@ -6,15 +6,16 @@ | |||
@Component | |||
public class dynamoBeeConfigConstants { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The class name dynamoBeeConfigConstants
does not follow Java's naming conventions. Class names should be in PascalCase. Consider renaming it to DynamoBeeConfigConstants
.
- public class dynamoBeeConfigConstants {
+ public class DynamoBeeConfigConstants {
public String authorName() { | ||
return CoreConstants.environment() + "_salessparrow_api"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These methods are returning hard-coded strings and string concatenations directly. This can make the code harder to maintain if these values need to change in the future. Consider defining these as constants at the top of your class.
+ private static final String CHANGELOG_SCAN_PACKAGE = "com.salessparrow.api.changelogs";
+ private static final String CHANGELOG_TABLE_NAME_SUFFIX = "_changelog";
+ private static final String AUTHOR_NAME_SUFFIX = "_salessparrow_api";
- public String getChangeLogScanPackage() {
- return "com.salessparrow.api.changelogs";
- }
+ public String getChangeLogScanPackage() {
+ return CHANGELOG_SCAN_PACKAGE;
+ }
- public String getChangelogTableName() {
- return CoreConstants.environment() + "_changelog";
- }
+ public String getChangelogTableName() {
+ return CoreConstants.environment() + CHANGELOG_TABLE_NAME_SUFFIX;
+ }
- public String authorName() {
- return CoreConstants.environment() + "_salessparrow_api";
- }
+ public String authorName() {
+ return CoreConstants.environment() + AUTHOR_NAME_SUFFIX;
+ }
@Autowired | ||
private OpenAiConstants openAiConstants; | ||
|
||
private Logger logger = org.slf4j.LoggerFactory.getLogger(SuggestionsController.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logger is currently using SuggestionsController.class
for its name, which seems incorrect as this is the OpenAiRequest
class. This could lead to confusion when reading the logs. The logger should be named after the class it's used in.
- private Logger logger = org.slf4j.LoggerFactory.getLogger(SuggestionsController.class);
+ private Logger logger = org.slf4j.LoggerFactory.getLogger(OpenAiRequest.class);
public HttpClient.HttpResponse makeRequest(Object payload) { | ||
String httpReqUrl = openAiConstants.chatCompletionUrl(); | ||
|
||
Map<String, String> headers = new HashMap<>(); | ||
headers.put("Authorization", "Bearer " + CoreConstants.openAiApiKey()); | ||
|
||
Integer timeoutMillis = openAiConstants.timeoutMillis(); | ||
try { | ||
logger.info("Making request to OpenAI API"); | ||
HttpClient.HttpResponse response = HttpClient.makePostRequest(httpReqUrl, headers, payload, timeoutMillis); | ||
|
||
return response; | ||
} | ||
catch (WebClientResponseException e) { | ||
if (e.getStatusCode().value() == 401) { | ||
throw new CustomException( | ||
new ErrorObject("l_o_a_oar_mr_1", "something_went_wrong", "Invalid OpenAI API key")); | ||
} | ||
else if (e.getStatusCode().value() == 400) { | ||
throw new CustomException( | ||
new ErrorObject("l_o_a_oar_mr_2", "something_went_wrong", "Invalid request payload")); | ||
} | ||
|
||
throw new CustomException(new ErrorObject("l_o_a_oar_mr_3", "something_went_wrong", e.getMessage())); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method makeRequest(Object payload)
does not validate the input payload before making the request. It's a good practice to validate inputs before processing them further. Consider adding a validation step for the payload before making the request to the OpenAI API. If the payload is invalid, you can throw an appropriate exception.
if (e.getStatusCode().value() == 401) { | ||
throw new CustomException( | ||
new ErrorObject("l_o_a_oar_mr_1", "something_went_wrong", "Invalid OpenAI API key")); | ||
} | ||
else if (e.getStatusCode().value() == 400) { | ||
throw new CustomException( | ||
new ErrorObject("l_o_a_oar_mr_2", "something_went_wrong", "Invalid request payload")); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error messages thrown in the exceptions are generic ("something_went_wrong"). It would be more helpful to provide specific error messages that indicate what exactly went wrong. This would make debugging easier and improve the user experience.
public String payloadForCrmActionsSuggestions(String text) { | ||
|
||
return "{\n" + " \"model\": \"gpt-3.5-turbo-0613\",\n" + " \"messages\": [\n" + " {\n" | ||
+ " \"role\": \"user\",\n" | ||
+ " \"content\": \"You are an AI assistant which gives suggestion on creating task in crm using the input message.Only use the functions you have been provided with. \\nInput message: \\n" | ||
+ text + "\\n\"\n" + " }\n" + " ],\n" + " \"functions\": [\n" + " {\n" | ||
+ " \"name\": \"suggest_actions\",\n" | ||
+ " \"description\": \"This is function for suggesting actions in crm(example salesforce, freshsales) based on input message.\",\n" | ||
+ " \"parameters\": {\n" + " \"type\": \"object\",\n" + " \"properties\": {\n" | ||
+ " \"add_task\": {\n" + " \"name\": \"add_task\",\n" | ||
+ " \"description\": \"Tasks using input message.\",\n" | ||
+ " \"type\": \"array\",\n" + " \"items\": {\n" | ||
+ " \"type\": \"object\",\n" + " \"properties\": {\n" | ||
+ " \"description\": {\n" + " \"type\": \"string\",\n" | ||
+ " \"description\": \"Description for task to add. This is mandatory\"\n" | ||
+ " },\n" + " \"due_date\": {\n" | ||
+ " \"type\": \"string\",\n" | ||
+ " \"description\": \"Due date for task in YYYY-MM-DD format. Today's date is " | ||
+ getTodaysDate() + ". This is mandatory\"\n" + " }\n" + " },\n" | ||
+ " \"required\": [\"description\", \"due_date\"]\n" + " }\n" + " }\n" | ||
+ " }\n" + " }\n" + " }\n" + " ]\n" + "}"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method payloadForCrmActionsSuggestions
constructs a JSON string manually, which is error-prone and hard to maintain. Consider using a library like Jackson or Gson to create the JSON object. This will make your code more readable and less prone to errors.
public String payloadForCrmActionsSuggestions(String text) {
JsonObject payload = new JsonObject();
payload.addProperty("model", "gpt-3.5-turbo-0613");
JsonArray messages = new JsonArray();
JsonObject message = new JsonObject();
message.addProperty("role", "user");
message.addProperty("content", "You are an AI assistant which gives suggestion on creating task in crm using the input message.Only use the functions you have been provided with. \\nInput message: \\n" + text + "\\n");
messages.add(message);
payload.add("messages", messages);
// Continue building the rest of the JSON object...
return payload.toString();
}
public String getTodaysDate() { | ||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); | ||
return sdf.format(new Date()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method uses SimpleDateFormat
which is not thread-safe. If multiple threads access this method simultaneously, it could lead to incorrect results. Consider using DateTimeFormatter
from the Java 8 Date/Time API which is thread-safe.
public String getTodaysDate() {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
return dtf.format(LocalDate.now());
}
public String getSalesforceOrganizationId() { | ||
String[] idParts = id.split("/"); | ||
return idParts[4]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method getSalesforceOrganizationId()
assumes that the id
will always have at least 5 parts when split by "/". If this is not the case, an ArrayIndexOutOfBoundsException will be thrown. Consider adding a check to ensure that the array has enough elements before accessing them.
public String getSalesforceOrganizationId() {
String[] idParts = id.split("/");
+ if (idParts.length < 5) {
+ return null; // or throw an appropriate exception
+ }
return idParts[4];
}
public static String getSecret(String key) { | ||
String secretJson = ""; | ||
|
||
if (CoreConstants.isDevEnvironment()) { | ||
secretJson = getLocalEnvVars("secrets.json"); | ||
} | ||
else if (CoreConstants.isLocalTestEnvironment()) { | ||
secretJson = getLocalEnvVars("test.secrets.json"); | ||
} | ||
else { | ||
SecretCache cache = new SecretCache(secretsManagerClientBuilder); | ||
String secretId = getSecretId(); | ||
secretJson = cache.getSecretString(secretId); | ||
cache.close(); | ||
} | ||
|
||
ObjectMapper objectMapper = new ObjectMapper(); | ||
String specificValue = ""; | ||
try { | ||
JsonNode jsonNode = objectMapper.readTree(secretJson); | ||
specificValue = jsonNode.get(key).asText(); | ||
} | ||
catch (Exception e) { | ||
throw new CustomException(new ErrorObject("l_gc_s_gs_1", "something_went_wrong", e.getMessage())); | ||
} | ||
|
||
return specificValue; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The getSecret()
method is doing too much work. It's responsible for determining the environment, reading local files, fetching secrets from AWS, parsing JSON, and extracting specific values. Consider breaking this method down into smaller, more focused methods to improve readability and maintainability.
SecretCache cache = new SecretCache(secretsManagerClientBuilder); | ||
String secretId = getSecretId(); | ||
secretJson = cache.getSecretString(secretId); | ||
cache.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SecretCache
object is created and closed within each call to getSecret()
. If getSecret()
is called frequently, this could lead to performance issues due to the overhead of creating and closing the SecretCache
repeatedly. Consider reusing the SecretCache
across multiple calls to getSecret()
.
JsonNode jsonNode = objectMapper.readTree(secretJson); | ||
specificValue = jsonNode.get(key).asText(); | ||
} | ||
catch (Exception e) { | ||
throw new CustomException(new ErrorObject("l_gc_s_gs_1", "something_went_wrong", e.getMessage())); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exception handling here is too broad, catching all exceptions and wrapping them in a CustomException
. This could make debugging difficult because it hides the original type of the exception. Consider catching more specific exception types (like IOException
, JsonProcessingException
) and handling them appropriately.
private static String getLocalEnvVars(String filename) { | ||
try (FileReader fileReader = new FileReader(filename)) { | ||
int ch; | ||
StringBuilder secretJsonBuilder = new StringBuilder(); | ||
|
||
while ((ch = fileReader.read()) != -1) { | ||
secretJsonBuilder.append((char) ch); | ||
} | ||
|
||
return secretJsonBuilder.toString(); | ||
} | ||
catch (FileNotFoundException e) { | ||
throw new CustomException(new ErrorObject("l_gc_s_glev_1", "something_went_wrong", e.getMessage())); | ||
} | ||
catch (IOException e) { | ||
throw new CustomException(new ErrorObject("l_gc_s_glev_2", "something_went_wrong", e.getMessage())); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The getLocalEnvVars()
method reads the entire file into a string using a FileReader
and a loop. This can be simplified and made more efficient by using Files.readString(Path)
.
- try (FileReader fileReader = new FileReader(filename)) {
- int ch;
- StringBuilder secretJsonBuilder = new StringBuilder();
- while ((ch = fileReader.read()) != -1) {
- secretJsonBuilder.append((char) ch);
- }
- return secretJsonBuilder.toString();
- } catch (FileNotFoundException e) {
- throw new CustomException(new ErrorObject("l_gc_s_glev_1", "something_went_wrong", e.getMessage()));
- } catch (IOException e) {
- throw new CustomException(new ErrorObject("l_gc_s_glev_2", "something_went_wrong", e.getMessage()));
- }
+ try {
+ return Files.readString(Paths.get(filename));
+ } catch (IOException e) {
+ throw new CustomException(new ErrorObject("l_gc_s_glev_2", "something_went_wrong", e.getMessage()));
+ }
|
||
HttpResponse response = null; | ||
try { | ||
response = HttpClient.makeGetRequest(salesforceIdentityEndpoint, headers, 10000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider extracting the magic number 10000
into a constant with a meaningful name. This will make the code more readable and easier to maintain.
- response = HttpClient.makeGetRequest(salesforceIdentityEndpoint, headers, 10000);
+ private static final int TIMEOUT = 10000;
+ response = HttpClient.makeGetRequest(salesforceIdentityEndpoint, headers, TIMEOUT);
public TaskEntity taskEntity() { | ||
Util util = new Util(); | ||
|
||
TaskEntity taskEntity = new TaskEntity(); | ||
taskEntity.setId(this.id); | ||
taskEntity.setCreatorName(this.createdBy.name); | ||
taskEntity.setDescription(this.description); | ||
taskEntity.setCrmOrganizationUserName(this.owner.name); | ||
taskEntity.setLastModifiedTime(this.lastModifiedDate); | ||
|
||
String dueDate = util.getDateFormatFromDatetime(this.activityDate); | ||
|
||
taskEntity.setDueDate(dueDate); | ||
return taskEntity; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Util
object is being created every time the taskEntity()
method is called. This could be a performance issue if this method is called frequently. Consider making Util
a class-level field and reusing it, or make the getDateFormatFromDatetime
method static if it doesn't depend on any instance state.
- public TaskEntity taskEntity() {
- Util util = new Util();
+ private final Util util = new Util();
+
+ public TaskEntity taskEntity() {
TaskEntity taskEntity = new TaskEntity();
taskEntity.setId(this.id);
taskEntity.setCreatorName(this.createdBy.name);
taskEntity.setDescription(this.description);
taskEntity.setCrmOrganizationUserName(this.owner.name);
taskEntity.setLastModifiedTime(this.lastModifiedDate);
String dueDate = util.getDateFormatFromDatetime(this.activityDate);
taskEntity.setDueDate(dueDate);
return taskEntity;
}
public HttpClient.HttpResponse getNoteContent(String noteId, String salesforceUserId) { | ||
Integer timeoutMillis = salesforceConstants.timeoutMillis(); | ||
|
||
SalesforceRequestInterface<HttpClient.HttpResponse> request = (token, instanceUrl) -> { | ||
String noteContentQuery = salesforceConstants.salesforceNotesContentUrl(instanceUrl, noteId); | ||
|
||
Map<String, String> headers = new HashMap<>(); | ||
headers.put("Authorization", "Bearer " + token); | ||
|
||
HttpClient.HttpResponse response = HttpClient.makeGetRequest(noteContentQuery, headers, timeoutMillis); | ||
|
||
return response; | ||
}; | ||
|
||
HttpClient.HttpResponse response = salesforceOauthRequest.makeRequest(salesforceUserId, request); | ||
return response; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no error handling in the getNoteContent
method. If the makeRequest
or makeGetRequest
methods throw an exception, it will propagate up the call stack. It would be better to catch any exceptions here and handle them appropriately, possibly by logging an error message and returning a suitable response.
try {
HttpClient.HttpResponse response = salesforceOauthRequest.makeRequest(salesforceUserId, request);
return response;
} catch (Exception e) {
// Log error message and return suitable response
}
String requestBody = "grant_type=" + salesforceConstants.refreshTokenGrantType() + "&client_id=" | ||
+ CoreConstants.salesforceClientId() + "&client_secret=" + CoreConstants.salesforceClientSecret() | ||
+ "&refresh_token=" + decryptedRefreshToken; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using StringBuilder
or String.format()
for constructing the requestBody
. This would improve readability and performance, especially when dealing with large strings.
String requestBody = String.format("grant_type=%s&client_id=%s&client_secret=%s&refresh_token=%s",
salesforceConstants.refreshTokenGrantType(),
CoreConstants.salesforceClientId(),
CoreConstants.salesforceClientSecret(),
decryptedRefreshToken);
Map<String, String> headers = new HashMap<>(); | ||
headers.put("content-type", "application/x-www-form-urlencoded"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The content type "application/x-www-form-urlencoded" is used frequently in HTTP requests. Consider defining it as a constant to avoid potential typos and enhance maintainability.
public static final String CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded";
...
headers.put("content-type", CONTENT_TYPE_FORM_URLENCODED);
Map<String, String> headers = new HashMap<>(); | ||
headers.put("content-type", "application/x-www-form-urlencoded"); | ||
|
||
HttpClient.HttpResponse response = HttpClient.makePostRequest(url, headers, requestBody, 5000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timeout value 5000
is hardcoded. It's better to define it as a configurable property or at least as a constant at the top of your class. This will make it easier to adjust the timeout value if needed without having to search through the code.
public static final int TIMEOUT = 5000;
...
HttpClient.HttpResponse response = HttpClient.makePostRequest(url, headers, requestBody, TIMEOUT);
@@ -13,11 +13,11 @@ | |||
|
|||
public class RedirectUriValidator implements ConstraintValidator<ValidRedirectUri, String> { | |||
|
|||
private List<String> ALLOWED_URIS = new ArrayList<>( | |||
Arrays.asList(CoreConstants.getWhitelistedRedirectUris())); | |||
private List<String> ALLOWED_URIS = new ArrayList<>(Arrays.asList(CoreConstants.getWhitelistedRedirectUris())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ALLOWED_URIS
variable is not final, but it's used like a constant. It would be better to make this variable final to prevent accidental modification.
- private List<String> ALLOWED_URIS = new ArrayList<>(Arrays.asList(CoreConstants.getWhitelistedRedirectUris()));
+ private final List<String> ALLOWED_URIS = new ArrayList<>(Arrays.asList(CoreConstants.getWhitelistedRedirectUris()));
|
||
@Override | ||
public boolean isValid(String value, ConstraintValidatorContext context) { | ||
return value != null && ALLOWED_URIS.contains(value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The isValid
method checks if the value is in the ALLOWED_URIS
list. This operation can be costly if the list is large because it needs to iterate over all elements. Consider using a HashSet
instead of a List
for ALLOWED_URIS
to improve performance as lookup operations in a HashSet
are faster (O(1) complexity).
- private final List<String> ALLOWED_URIS = new ArrayList<>(Arrays.asList(CoreConstants.getWhitelistedRedirectUris()));
+ private final Set<String> ALLOWED_URIS = new HashSet<>(Arrays.asList(CoreConstants.getWhitelistedRedirectUris()));
public <T> T makeRequest(String salesforceUserId, SalesforceRequestInterface<T> request) { | ||
SalesforceOauthToken sfOAuthToken = salesforceOauthTokenRepository | ||
.getSalesforceOauthTokenByExternalUserId(salesforceUserId); | ||
|
||
String decryptedAccessToken = getAccessTokenService.fetchAccessToken(sfOAuthToken); | ||
|
||
try { | ||
return request.execute(decryptedAccessToken, sfOAuthToken.getInstanceUrl()); | ||
} | ||
catch (WebClientResponseException e) { | ||
if (e.getStatusCode().value() == 401) { | ||
try { | ||
decryptedAccessToken = getAccessTokenService.updateAndGetRefreshedAccessToken(sfOAuthToken); | ||
return request.execute(decryptedAccessToken, sfOAuthToken.getInstanceUrl()); | ||
} | ||
catch (Exception e1) { | ||
throw new CustomException(new ErrorObject("l_s_h_sr_mr_1", "something_went_wrong", e.getMessage())); | ||
} | ||
} | ||
throw new CustomException(new ErrorObject("l_s_h_sr_mr_2", "something_went_wrong", e.getMessage())); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The makeRequest
method has been updated to handle a WebClientResponseException
and refresh the access token if the status code is 401. However, there's a potential issue here. If the second request (line 43) also fails with a 401 status code, the method will not attempt to refresh the token again and will instead throw an exception. This could lead to unnecessary failures if the first token refresh didn't work for some reason (e.g., temporary network issue). Consider adding a retry mechanism to handle this scenario.
try {
return executeRequest(request, sfOAuthToken);
}
catch (WebClientResponseException e) {
if (e.getStatusCode().value() == 401) {
for (int i = 0; i < MAX_RETRIES; i++) {
try {
decryptedAccessToken = getAccessTokenService.updateAndGetRefreshedAccessToken(sfOAuthToken);
return executeRequest(request, sfOAuthToken);
}
catch (WebClientResponseException e1) {
if (e1.getStatusCode().value() != 401 || i == MAX_RETRIES - 1) {
throw new CustomException(new ErrorObject("l_s_h_sr_mr_1", "something_went_wrong", e.getMessage()));
}
}
}
}
throw new CustomException(new ErrorObject("l_s_h_sr_mr_2", "something_went_wrong", e.getMessage()));
}
}
private <T> T executeRequest(SalesforceRequestInterface<T> request, SalesforceOauthToken sfOAuthToken) {
return request.execute(decryptedAccessToken, sfOAuthToken.getInstanceUrl());
}
In this suggested change, I've extracted the request execution into a separate method (executeRequest
) for better readability and maintainability. The retry mechanism is implemented in a loop that attempts to refresh the token and execute the request up to MAX_RETRIES
times. If all retries fail with a 401 status code, or if any other exception occurs, a CustomException
is thrown.
String requestBody = "grant_type=" + salesforceConstants.authorizationCodeGrantType() + "&client_id=" | ||
+ CoreConstants.salesforceClientId() + "&client_secret=" + CoreConstants.salesforceClientSecret() | ||
+ "&code=" + code + "&redirect_uri=" + redirectUri; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The construction of the request body is done by concatenating strings, which can be inefficient and error-prone. Consider using StringBuilder
or String.format()
for better performance and readability. Also, it's important to URL encode the parameters to ensure that special characters are correctly interpreted.
- String requestBody = "grant_type=" + salesforceConstants.authorizationCodeGrantType() + "&client_id="
- + CoreConstants.salesforceClientId() + "&client_secret=" + CoreConstants.salesforceClientSecret()
- + "&code=" + code + "&redirect_uri=" + redirectUri;
+ String requestBody = String.format("grant_type=%s&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s",
+ URLEncoder.encode(salesforceConstants.authorizationCodeGrantType(), StandardCharsets.UTF_8),
+ URLEncoder.encode(CoreConstants.salesforceClientId(), StandardCharsets.UTF_8),
+ URLEncoder.encode(CoreConstants.salesforceClientSecret(), StandardCharsets.UTF_8),
+ URLEncoder.encode(code, StandardCharsets.UTF_8),
+ URLEncoder.encode(redirectUri, StandardCharsets.UTF_8));
|
||
HttpResponse response = null; | ||
try { | ||
response = HttpClient.makePostRequest(salesforceOAuthEndpoint, headers, requestBody, 10000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timeout value for the HTTP request is hardcoded as 10000
. It would be better to move this to a configuration file or constant so that it can be easily adjusted without modifying the code.
- response = HttpClient.makePostRequest(salesforceOAuthEndpoint, headers, requestBody, 10000);
+ response = HttpClient.makePostRequest(salesforceOAuthEndpoint, headers, requestBody, CoreConstants.httpTimeout());
catch (Exception e) { | ||
List<String> paramErrorIdentifiers = new ArrayList<>(); | ||
paramErrorIdentifiers.add("invalid_code"); | ||
|
||
throw new CustomException(new ParamErrorObject("l_s_w_sgt_gt_1", e.getMessage(), paramErrorIdentifiers)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exception handling here only covers generic exceptions. It would be beneficial to handle specific exceptions (like IOException
, HttpException
) separately to provide more detailed error messages and potentially recover from certain errors.
try {
response = HttpClient.makePostRequest(salesforceOAuthEndpoint, headers, requestBody, CoreConstants.httpTimeout());
} catch (IOException e) {
throw new CustomException(new ParamErrorObject("l_s_w_sgt_gt_io", e.getMessage(), Collections.singletonList("network_error")));
} catch (HttpException e) {
throw new CustomException(new ParamErrorObject("l_s_w_sgt_gt_http", e.getMessage(), Collections.singletonList("http_error")));
} catch (Exception e) {
throw new CustomException(new ParamErrorObject("l_s_w_sgt_gt_1", e.getMessage(), Collections.singletonList("invalid_code")));
}
if (searchTerm == "") { | ||
query = "SELECT Id, Name FROM Account ORDER BY LastModifiedDate DESC LIMIT 20"; | ||
} | ||
else { | ||
query = "SELECT Id, Name FROM Account WHERE Name LIKE '%" + searchTerm | ||
+ "%' ORDER BY LastModifiedDate DESC LIMIT 20"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check for an empty string should use .equals("")
instead of ==
. The ==
operator checks if two references point to the same object, while .equals()
checks if the contents of the strings are equal.
- if (searchTerm == "") {
+ if (searchTerm.equals("")) {
if (searchTerm == "") { | ||
query = "SELECT Id, Name FROM User ORDER BY LastModifiedDate DESC LIMIT 20"; | ||
} | ||
else { | ||
query = "SELECT Id, Name FROM User WHERE Name LIKE '%" + searchTerm | ||
+ "%' ORDER BY LastModifiedDate DESC LIMIT 20"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as above. Use .equals("")
instead of ==
.
- if (searchTerm == "") {
+ if (searchTerm.equals("")) {
StringBuilder queryBuilder = new StringBuilder( | ||
"SELECT Id, Title, TextPreview, CreatedBy.Name, LastModifiedDate FROM ContentNote WHERE Id IN ("); | ||
|
||
for (int i = 0; i < documentIds.size(); i++) { | ||
if (i > 0) { | ||
queryBuilder.append(", "); | ||
} | ||
|
||
String documentId = Util.escapeSpecialChars(documentIds.get(i)); | ||
queryBuilder.append("'").append(documentId).append("'"); | ||
} | ||
queryBuilder.append(") ORDER BY LastModifiedDate DESC LIMIT 5"); | ||
|
||
return Util.urlEncoder(queryBuilder.toString()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using Java 8's String.join() method to concatenate the document IDs. This will make the code more readable and maintainable.
- StringBuilder queryBuilder = new StringBuilder(
- "SELECT Id, Title, TextPreview, CreatedBy.Name, LastModifiedDate FROM ContentNote WHERE Id IN (");
- for (int i = 0; i < documentIds.size(); i++) {
- if (i > 0) {
- queryBuilder.append(", ");
- }
- String documentId = Util.escapeSpecialChars(documentIds.get(i));
- queryBuilder.append("'").append(documentId).append("'");
- }
- queryBuilder.append(") ORDER BY LastModifiedDate DESC LIMIT 5");
+ List<String> escapedDocumentIds = documentIds.stream()
+ .map(id -> "'" + Util.escapeSpecialChars(id) + "'")
+ .collect(Collectors.toList());
+ String query = String.format(
+ "SELECT Id, Title, TextPreview, CreatedBy.Name, LastModifiedDate FROM ContentNote WHERE Id IN (%s) ORDER BY LastModifiedDate DESC LIMIT 5",
+ String.join(", ", escapedDocumentIds));
if (value == null) { | ||
return false; | ||
} | ||
|
||
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); | ||
sdf.setLenient(false); | ||
|
||
try { | ||
Date parsedDate = sdf.parse(value); | ||
String dateString = sdf.format(parsedDate); | ||
return dateString.equals(value.toString()); | ||
} | ||
catch (Exception ex) { | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exception handling in this code is too broad. Catching Exception
will catch all exceptions, including those that you might not expect and want to handle differently. It's better to catch specific exceptions that you expect can be thrown in the try block. In this case, it would be ParseException
. This way, unexpected exceptions won't be silently ignored but will cause a meaningful crash that you can fix.
- catch (Exception ex) {
+ catch (ParseException ex) {
try { | ||
Date parsedDate = sdf.parse(value); | ||
String dateString = sdf.format(parsedDate); | ||
return dateString.equals(value.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need to call toString()
on value
because it's already a string.
- return dateString.equals(value.toString());
+ return dateString.equals(value);
public HttpClient.HttpResponse makePostRequest(List<CompositeRequestDto> compositeRequests, | ||
String salesforceUserId) { | ||
Map<String, List<CompositeRequestDto>> compositeRequestsMap = new HashMap<>(); | ||
compositeRequestsMap.put("compositeRequest", compositeRequests); | ||
|
||
Integer timeoutMillis = salesforceConstants.timeoutMillis(); | ||
|
||
SalesforceRequestInterface<HttpClient.HttpResponse> request = (token, instanceUrl) -> { | ||
String httpReqUrl = salesforceConstants.salesforceCompositeUrl(instanceUrl); | ||
|
||
Map<String, String> headers = new HashMap<>(); | ||
headers.put("Authorization", "Bearer " + token); | ||
|
||
HttpClient.HttpResponse response = HttpClient.makePostRequest(httpReqUrl, headers, compositeRequestsMap, | ||
timeoutMillis); | ||
return response; | ||
}; | ||
|
||
HttpClient.HttpResponse response = null; | ||
|
||
logger.info("making composite request to salesforce"); | ||
|
||
response = salesforceOauthRequest.makeRequest(salesforceUserId, request); | ||
return response; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The makePostRequest
method could be improved by handling potential exceptions that might occur during the execution of the HTTP request. Currently, if an exception occurs (for example, a timeout or a network error), it will propagate up the call stack and potentially crash the application. It would be better to catch these exceptions and handle them gracefully, perhaps by logging an error message and returning a suitable error response.
Here's how you can do this:
public HttpClient.HttpResponse makePostRequest(List<CompositeRequestDto> compositeRequests,
String salesforceUserId) {
Map<String, List<CompositeRequestDto>> compositeRequestsMap = new HashMap<>();
compositeRequestsMap.put("compositeRequest", compositeRequests);
Integer timeoutMillis = salesforceConstants.timeoutMillis();
SalesforceRequestInterface<HttpClient.HttpResponse> request = (token, instanceUrl) -> {
String httpReqUrl = salesforceConstants.salesforceCompositeUrl(instanceUrl);
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + token);
HttpClient.HttpResponse response = null;
try {
response = HttpClient.makePostRequest(
httpReqUrl,
headers,
compositeRequestsMap,
timeoutMillis);
} catch (Exception e) {
logger.error("Error occurred while making composite request to Salesforce", e);
// return a suitable error response or rethrow the exception
}
return response;
};
HttpClient.HttpResponse response = null;
logger.info("making composite request to salesforce");
try {
response = salesforceOauthRequest.makeRequest(salesforceUserId, request);
} catch (Exception e) {
logger.error("Error occurred while making request to Salesforce", e);
// return a suitable error response or rethrow the exception
}
return response;
}
Logger logger = LoggerFactory.getLogger(MakeCompositeRequest.class); | ||
|
||
@Autowired | ||
private SalesforceRequest salesforceOauthRequest; | ||
|
||
@Autowired | ||
private SalesforceConstants salesforceConstants; | ||
|
||
/** | ||
* Make composite post request to the Salesforce API. | ||
* @param compositeRequests | ||
* @param salesforceUserId | ||
* @return HttpClient.HttpResponse | ||
**/ | ||
public HttpClient.HttpResponse makePostRequest(List<CompositeRequestDto> compositeRequests, | ||
String salesforceUserId) { | ||
Map<String, List<CompositeRequestDto>> compositeRequestsMap = new HashMap<>(); | ||
compositeRequestsMap.put("compositeRequest", compositeRequests); | ||
|
||
Integer timeoutMillis = salesforceConstants.timeoutMillis(); | ||
|
||
SalesforceRequestInterface<HttpClient.HttpResponse> request = (token, instanceUrl) -> { | ||
String httpReqUrl = salesforceConstants.salesforceCompositeUrl(instanceUrl); | ||
|
||
Map<String, String> headers = new HashMap<>(); | ||
headers.put("Authorization", "Bearer " + token); | ||
|
||
HttpClient.HttpResponse response = HttpClient.makePostRequest(httpReqUrl, headers, compositeRequestsMap, | ||
timeoutMillis); | ||
return response; | ||
}; | ||
|
||
HttpClient.HttpResponse response = null; | ||
|
||
logger.info("making composite request to salesforce"); | ||
|
||
response = salesforceOauthRequest.makeRequest(salesforceUserId, request); | ||
return response; | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The class MakeCompositeRequest
is not thread-safe because it uses instance variables (salesforceOauthRequest
, salesforceConstants
) that are shared among multiple threads. If two threads call the makePostRequest
method at the same time, they may interfere with each other's operations, leading to inconsistent results or errors. To fix this, consider making the makePostRequest
method synchronized or using local variables instead of instance variables where possible.
public void deleteAccountNote(HttpServletRequest request, String accountId, String noteId) { | ||
logger.info("Delete Account Note Service called"); | ||
|
||
User currentUser = (User) request.getAttribute("current_user"); | ||
|
||
deleteAccountNoteFactory.deleteAccountNote(currentUser, noteId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method deleteAccountNote
takes an accountId
parameter but it's not used in the method body. If this parameter is not needed, consider removing it from the method signature. If it's needed, ensure that it's used appropriately within the method.
- public void deleteAccountNote(HttpServletRequest request, String accountId, String noteId) {
+ public void deleteAccountNote(HttpServletRequest request, String noteId) {
Other than this, the changes look good and improve the readability of the code.
@Autowired | ||
private CreateNoteFactory createNoteFactory; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using constructor injection instead of field injection for CreateNoteFactory
. This is a recommended practice as it makes your classes easier to test, makes dependencies explicit and reduces the risk of NullPointerExceptions.
- @Autowired
- private CreateNoteFactory createNoteFactory;
+ private final CreateNoteFactory createNoteFactory;
+ @Autowired
+ public CreateAccountNoteService(CreateNoteFactory createNoteFactory) {
+ this.createNoteFactory = createNoteFactory;
+ }
*/ | ||
public CreateNoteFormatterDto createNote(HttpServletRequest request, String accountId, NoteDto note) { | ||
|
||
SalesforceUser currentUser = (SalesforceUser) request.getAttribute("current_user"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no null check before casting the attribute "current_user" to SalesforceUser. If the attribute doesn't exist or isn't of the correct type, this will throw a ClassCastException. Consider adding a null check or try-catch block to handle potential exceptions.
- SalesforceUser currentUser = (SalesforceUser) request.getAttribute("current_user");
+ Object userObject = request.getAttribute("current_user");
+ if(userObject instanceof SalesforceUser){
+ SalesforceUser currentUser = (SalesforceUser) userObject;
+ } else {
+ // Handle error case here
+ }
public GetNoteDetailsFormatterDto getNoteDetails(HttpServletRequest request, String noteId) { | ||
User currentUser = (User) request.getAttribute("current_user"); | ||
|
||
return getNoteDetailsFactory.getNoteDetails(currentUser, noteId); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update the JavaDoc comment to accurately reflect the method parameters.
- /**
- * Get the details of a note
- * @param accountId
- * @param noteId
- * @return GetNoteDetailsFormatterDto
- */
+ /**
+ * Get the details of a note
+ * @param request
+ * @param noteId
+ * @return GetNoteDetailsFormatterDto
+ */
public GetNoteDetailsFormatterDto getNoteDetails(HttpServletRequest request, String noteId) {
User currentUser = (User) request.getAttribute("current_user");
return getNoteDetailsFactory.getNoteDetails(currentUser, noteId);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Summary by CodeRabbit
Status
enum inSalesforceOauthToken.java
to use a static map for value lookup, improving performance.DynamoDBConfiguration.java
to use builder classes for creating AmazonDynamoDB and DynamoDBMapperConfig objects.SameSiteCookieFilter
class to set the SameSite attribute for cookies in HTTP responses.LOCAL_SETUP.md
, providing updated environment variable values and formatting guidelines.