fix: improve null safety in action execution analytics data collection (#40905)

## Description
Fix NPE in ActionExecutionSolutionCEImpl by replacing `Map.of()` with
explicit HashMap entries. This change improves error resilience by
adding null checks with default values for all map entries and makes the
code more maintainable by separating each property assignment into
individual operations.


Fixes #40904 

## Automation

/ok-to-test tags="@tag.Sanity"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/15552352827>
> Commit: 48335f468fb5104ff911f277d121817202ea7545
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=15552352827&attempt=2"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Sanity`
> Spec:
> <hr>Tue, 10 Jun 2025 07:43:44 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Refactor**
- Improved handling of missing or null information in analytics event
data to ensure more consistent and reliable reporting. No changes to
user-facing features or workflows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Nilansh Bansal 2025-06-10 13:15:11 +05:30 committed by GitHub
parent a88ec80bb1
commit befe9ffd64
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1127,27 +1127,21 @@ public class ActionExecutionSolutionCEImpl implements ActionExecutionSolutionCE
? ApplicationMode.PUBLISHED.toString()
: ApplicationMode.EDIT.toString();
final Map<String, Object> data = new HashMap<>(Map.of(
"username",
user.getUsername(),
"type",
pluginType,
"pluginName",
plugin.getName(),
"name",
actionDTO.getName(),
"datasource",
Map.of("name", datasourceStorage.getName()),
"workspaceId",
application.getWorkspaceId(),
"appId",
actionDTO.getApplicationId(),
FieldName.APP_MODE,
appMode,
"appName",
application.getName(),
"isExampleApp",
application.isAppIsExample()));
final Map<String, Object> data = new HashMap<>();
data.put("username", user.getUsername());
data.put("type", pluginType);
data.put("pluginName", plugin.getName());
data.put("name", actionDTO.getName());
Map<String, Object> datasourceInfo = new HashMap<>();
datasourceInfo.put("name", datasourceStorage.getName());
data.put("datasource", datasourceInfo);
data.put("workspaceId", application.getWorkspaceId());
data.put("appId", actionDTO.getApplicationId());
data.put(FieldName.APP_MODE, appMode);
data.put("appName", application.getName());
data.put("isExampleApp", application.isAppIsExample());
String dsCreatedAt = "";
if (datasourceStorage.getCreatedAt() != null) {
@ -1160,37 +1154,28 @@ public class ActionExecutionSolutionCEImpl implements ActionExecutionSolutionCE
List<String> executionParams =
paramsList.stream().map(param -> param.getValue()).collect(Collectors.toList());
data.putAll(Map.of(
"request",
request,
data.put("request", request);
data.put(
"isSuccessfulExecution",
ObjectUtils.defaultIfNull(actionExecutionResult.getIsExecutionSuccess(), false),
"statusCode",
ObjectUtils.defaultIfNull(actionExecutionResult.getStatusCode(), ""),
"timeElapsed",
timeElapsed,
"actionCreated",
DateUtils.ISO_FORMATTER.format(actionDTO.getCreatedAt()),
"actionId",
ObjectUtils.defaultIfNull(actionDTO.getId(), "")));
data.putAll(Map.of(
ObjectUtils.defaultIfNull(actionExecutionResult.getIsExecutionSuccess(), false));
data.put("statusCode", ObjectUtils.defaultIfNull(actionExecutionResult.getStatusCode(), ""));
data.put("timeElapsed", timeElapsed);
data.put("actionCreated", DateUtils.ISO_FORMATTER.format(actionDTO.getCreatedAt()));
data.put("actionId", ObjectUtils.defaultIfNull(actionDTO.getId(), ""));
data.put(
FieldName.ACTION_EXECUTION_REQUEST_PARAMS_SIZE,
executeActionDto.getTotalReadableByteCount(),
FieldName.ACTION_EXECUTION_REQUEST_PARAMS_COUNT,
executionParams.size()));
executeActionDto.getTotalReadableByteCount());
data.put(FieldName.ACTION_EXECUTION_REQUEST_PARAMS_COUNT, executionParams.size());
setContextSpecificProperties(data, actionDTO, pageName);
ActionExecutionResult.PluginErrorDetails pluginErrorDetails =
actionExecutionResult.getPluginErrorDetails();
data.putAll(Map.of("pluginErrorDetails", ObjectUtils.defaultIfNull(pluginErrorDetails, "")));
data.put("pluginErrorDetails", ObjectUtils.defaultIfNull(pluginErrorDetails, ""));
if (pluginErrorDetails != null) {
data.putAll(Map.of(
"appsmithErrorCode", pluginErrorDetails.getAppsmithErrorCode(),
"appsmithErrorMessage", pluginErrorDetails.getAppsmithErrorMessage(),
"errorType", pluginErrorDetails.getErrorType()));
data.put("appsmithErrorCode", pluginErrorDetails.getAppsmithErrorCode());
data.put("appsmithErrorMessage", pluginErrorDetails.getAppsmithErrorMessage());
data.put("errorType", pluginErrorDetails.getErrorType());
}
data.putAll(DatasourceAnalyticsUtils.getAnalyticsPropertiesWithStorageOnActionExecution(
@ -1219,15 +1204,15 @@ public class ActionExecutionSolutionCEImpl implements ActionExecutionSolutionCE
actionExecutionResult.getRequest().getQuery();
}
final Map<String, Object> eventData = new HashMap<>(Map.of(
FieldName.ACTION, actionDTO,
FieldName.DATASOURCE, datasourceStorage,
FieldName.APP_MODE, appMode,
FieldName.ACTION_EXECUTION_RESULT, actionExecutionResult,
FieldName.ACTION_EXECUTION_TIME, timeElapsed,
FieldName.ACTION_EXECUTION_QUERY, executionRequestQuery,
FieldName.APPLICATION, application,
FieldName.PLUGIN, plugin));
final Map<String, Object> eventData = new HashMap<>();
eventData.put(FieldName.ACTION, actionDTO);
eventData.put(FieldName.DATASOURCE, datasourceStorage);
eventData.put(FieldName.APP_MODE, appMode);
eventData.put(FieldName.ACTION_EXECUTION_RESULT, actionExecutionResult);
eventData.put(FieldName.ACTION_EXECUTION_TIME, timeElapsed);
eventData.put(FieldName.ACTION_EXECUTION_QUERY, executionRequestQuery);
eventData.put(FieldName.APPLICATION, application);
eventData.put(FieldName.PLUGIN, plugin);
if (executeActionDto.getTotalReadableByteCount() <= Constraint.MAX_ANALYTICS_SIZE_BYTES) {
// Only send params info if total size is less than 5 MB
@ -1259,7 +1244,8 @@ public class ActionExecutionSolutionCEImpl implements ActionExecutionSolutionCE
}
protected void setContextSpecificProperties(Map<String, Object> data, ActionDTO actionDTO, String contextName) {
data.putAll(Map.of("pageId", ObjectUtils.defaultIfNull(actionDTO.getPageId(), ""), "pageName", contextName));
data.put("pageId", ObjectUtils.defaultIfNull(actionDTO.getPageId(), ""));
data.put("pageName", ObjectUtils.defaultIfNull(contextName, ""));
}
protected Mono<ActionDTO> setAutoGeneratedHeaders(Plugin plugin, ActionDTO actionDTO, HttpHeaders httpHeaders) {