chore: Add metrics to newRelic for update JSobject Collection (#35947)

## Description

- Add newRelic spans to track performance of process involved in update
JSObject collection

Fixes #36049 

## Automation

/test js

### 🔍 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/10704346206>
> Commit: 5bf63bb0c04957493d40373b40f6ab46ed447103
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10704346206&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.JS`
> Spec:
> <hr>Wed, 04 Sep 2024 15:29:57 UTC
<!-- end of auto-generated comment: Cypress test results  -->


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


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

## Summary by CodeRabbit

- **New Features**
- Introduced enhanced observability for action collections and layout
updates, allowing better tracking and monitoring of actions within the
application.
- Added new constants related to action collections and layout updates
for improved functionality and clarity.

- **Bug Fixes**
- Improved the reliability of action collection retrieval and layout
updates through enhanced observability features.

- **Tests**
- Updated tests to incorporate observability metrics, ensuring better
monitoring of actions during testing.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Rishabh Rathod 2024-09-05 16:46:25 +05:30 committed by GitHub
parent 440ff139e9
commit b447b0f29e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 209 additions and 37 deletions

View File

@ -0,0 +1,5 @@
package com.appsmith.external.constants.spans;
import com.appsmith.external.constants.spans.ce.ActionCollectionSpanCE;
public class ActionCollectionSpan extends ActionCollectionSpanCE {}

View File

@ -0,0 +1,5 @@
package com.appsmith.external.constants.spans;
import com.appsmith.external.constants.spans.ce.LayoutSpanCE;
public class LayoutSpan extends LayoutSpanCE {}

View File

@ -0,0 +1,21 @@
package com.appsmith.external.constants.spans.ce;
import static com.appsmith.external.constants.spans.BaseSpan.APPSMITH_SPAN_PREFIX;
/**
* Please make sure that all span names start with `appsmith.` because span with any other naming format would get
* dropped / ignored as defined in TracingConfig.java
*/
public class ActionCollectionSpanCE {
// Action Collection spans
public static final String ACTION_COLLECTION_UPDATE = APPSMITH_SPAN_PREFIX + "update.actionCollection";
public static final String GENERATE_ACTION_COLLECTION_BY_VIEW_MODE =
APPSMITH_SPAN_PREFIX + "generate.actionCollectionByViewMode";
public static final String POPULATE_ACTION_COLLECTION_BY_VIEW_MODE =
APPSMITH_SPAN_PREFIX + "populate.actionCollectionByViewMode";
public static final String SAVE_ACTION_COLLECTION_LAST_EDIT_INFO =
APPSMITH_SPAN_PREFIX + "save.actionCollectionLastEditInfo";
// Getter spans
public static final String GET_ACTION_COLLECTION_BY_ID = APPSMITH_SPAN_PREFIX + "get.actionCollectionById";
}

View File

@ -29,4 +29,12 @@ public class ActionSpanCE {
public static final String VIEW_MODE_SET_PLUGIN_ID_AND_TYPE_ACTION = ACTIONS_VIEW_MODE_PREFIX + "set_action"; public static final String VIEW_MODE_SET_PLUGIN_ID_AND_TYPE_ACTION = ACTIONS_VIEW_MODE_PREFIX + "set_action";
public static final String VIEW_MODE_FETCH_PLUGIN_FROM_DB = ACTIONS_VIEW_MODE_PREFIX + "plugindb"; public static final String VIEW_MODE_FETCH_PLUGIN_FROM_DB = ACTIONS_VIEW_MODE_PREFIX + "plugindb";
public static final String VIEW_MODE_FETCH_ACTIONS_FROM_DB = ACTIONS_VIEW_MODE_PREFIX + "fetchactions"; public static final String VIEW_MODE_FETCH_ACTIONS_FROM_DB = ACTIONS_VIEW_MODE_PREFIX + "fetchactions";
public static final String GET_ACTION_BY_ID = APPSMITH_SPAN_PREFIX + "get.actionById";
// Action creation, update and delete spans
public static final String UPDATE_SINGLE_ACTION = APPSMITH_SPAN_PREFIX + "update.single.action";
public static final String UPDATE_ACTION_BASED_ON_CONTEXT = APPSMITH_SPAN_PREFIX + "update.action.context";
public static final String CREATE_ACTION = APPSMITH_SPAN_PREFIX + "create.action";
public static final String UPDATE_ACTION = APPSMITH_SPAN_PREFIX + "update.action";
public static final String DELETE_ACTION = APPSMITH_SPAN_PREFIX + "delete.action";
} }

View File

@ -0,0 +1,18 @@
package com.appsmith.external.constants.spans.ce;
import static com.appsmith.external.constants.spans.BaseSpan.APPSMITH_SPAN_PREFIX;
public class LayoutSpanCE {
public static final String UPDATE_LAYOUT = "updateLayout.";
public static final String UPDATE_PAGE_LAYOUT_BY_PAGE_ID = APPSMITH_SPAN_PREFIX + UPDATE_LAYOUT + "pageId";
public static final String UPDATE_LAYOUT_METHOD = APPSMITH_SPAN_PREFIX + UPDATE_LAYOUT + "method";
public static final String UPDATE_LAYOUT_DSL_METHOD = APPSMITH_SPAN_PREFIX + UPDATE_LAYOUT + "dsl.method";
public static final String UPDATE_LAYOUT_BASED_ON_CONTEXT = APPSMITH_SPAN_PREFIX + UPDATE_LAYOUT + "context";
public static final String FIND_ALL_ON_LOAD_EXECUTABLES =
APPSMITH_SPAN_PREFIX + "onLoadExecutablesUtil.findAllOnLoadExecutables";
public static final String UPDATE_EXECUTABLES_EXECUTE_ONLOAD =
APPSMITH_SPAN_PREFIX + "onLoadExecutablesUtil.updateExecutablesExecuteOnLoad";
public static final String FIND_AND_UPDATE_LAYOUT =
APPSMITH_SPAN_PREFIX + "onLoadExecutablesUtil.findAndUpdateLayout";
}

View File

@ -1,5 +1,7 @@
package com.appsmith.external.constants.spans.ce; package com.appsmith.external.constants.spans.ce;
import static com.appsmith.external.constants.spans.BaseSpan.APPSMITH_SPAN_PREFIX;
public class PageSpanCE { public class PageSpanCE {
public static final String PAGES = "pages."; public static final String PAGES = "pages.";
public static final String GET_PAGE = PAGES + "getpage"; public static final String GET_PAGE = PAGES + "getpage";
@ -11,4 +13,6 @@ public class PageSpanCE {
public static final String MARK_RECENTLY_ACCESSED_RESOURCES_PAGES = PAGES + "update_recently_accessed_pages"; public static final String MARK_RECENTLY_ACCESSED_RESOURCES_PAGES = PAGES + "update_recently_accessed_pages";
public static final String PREPARE_APPLICATION_PAGES_DTO_FROM_PAGES = PAGES + "generate_app_pages_dto"; public static final String PREPARE_APPLICATION_PAGES_DTO_FROM_PAGES = PAGES + "generate_app_pages_dto";
public static final String MIGRATE_DSL = PAGES + "migrate_dsl"; public static final String MIGRATE_DSL = PAGES + "migrate_dsl";
public static final String GET_PAGE_BY_ID = APPSMITH_SPAN_PREFIX + "get.page.unpublished";
} }

View File

@ -20,6 +20,7 @@ import com.appsmith.server.services.AnalyticsService;
import com.appsmith.server.services.BaseService; import com.appsmith.server.services.BaseService;
import com.appsmith.server.solutions.ActionPermission; import com.appsmith.server.solutions.ActionPermission;
import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.ApplicationPermission;
import io.micrometer.observation.ObservationRegistry;
import jakarta.validation.Validator; import jakarta.validation.Validator;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
@ -28,6 +29,7 @@ import org.springframework.data.domain.Sort;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import reactor.core.observability.micrometer.Micrometer;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -41,6 +43,7 @@ import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import static com.appsmith.external.constants.spans.ActionCollectionSpan.GET_ACTION_COLLECTION_BY_ID;
import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNewFieldValuesIntoOldObject; import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNewFieldValuesIntoOldObject;
import static java.lang.Boolean.TRUE; import static java.lang.Boolean.TRUE;
@ -53,6 +56,7 @@ public class ActionCollectionServiceCEImpl extends BaseService<ActionCollectionR
private final ApplicationService applicationService; private final ApplicationService applicationService;
private final ApplicationPermission applicationPermission; private final ApplicationPermission applicationPermission;
private final ActionPermission actionPermission; private final ActionPermission actionPermission;
private final ObservationRegistry observationRegistry;
@Autowired @Autowired
public ActionCollectionServiceCEImpl( public ActionCollectionServiceCEImpl(
@ -63,7 +67,8 @@ public class ActionCollectionServiceCEImpl extends BaseService<ActionCollectionR
PolicyGenerator policyGenerator, PolicyGenerator policyGenerator,
ApplicationService applicationService, ApplicationService applicationService,
ApplicationPermission applicationPermission, ApplicationPermission applicationPermission,
ActionPermission actionPermission) { ActionPermission actionPermission,
ObservationRegistry observationRegistry) {
super(validator, repository, analyticsService); super(validator, repository, analyticsService);
this.newActionService = newActionService; this.newActionService = newActionService;
@ -71,6 +76,7 @@ public class ActionCollectionServiceCEImpl extends BaseService<ActionCollectionR
this.applicationService = applicationService; this.applicationService = applicationService;
this.applicationPermission = applicationPermission; this.applicationPermission = applicationPermission;
this.actionPermission = actionPermission; this.actionPermission = actionPermission;
this.observationRegistry = observationRegistry;
} }
@Override @Override
@ -372,7 +378,10 @@ public class ActionCollectionServiceCEImpl extends BaseService<ActionCollectionR
@Override @Override
public Mono<ActionCollection> findById(String id, AclPermission aclPermission) { public Mono<ActionCollection> findById(String id, AclPermission aclPermission) {
return repository.findById(id, aclPermission); return repository
.findById(id, aclPermission)
.name(GET_ACTION_COLLECTION_BY_ID)
.tap(Micrometer.observation(observationRegistry));
} }
@Override @Override

View File

@ -7,6 +7,7 @@ import com.appsmith.server.repositories.ActionCollectionRepository;
import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.AnalyticsService;
import com.appsmith.server.solutions.ActionPermission; import com.appsmith.server.solutions.ActionPermission;
import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.ApplicationPermission;
import io.micrometer.observation.ObservationRegistry;
import jakarta.validation.Validator; import jakarta.validation.Validator;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -22,7 +23,8 @@ public class ActionCollectionServiceImpl extends ActionCollectionServiceCEImpl i
PolicyGenerator policyGenerator, PolicyGenerator policyGenerator,
ApplicationService applicationService, ApplicationService applicationService,
ApplicationPermission applicationPermission, ApplicationPermission applicationPermission,
ActionPermission actionPermission) { ActionPermission actionPermission,
ObservationRegistry observationRegistry) {
super( super(
validator, validator,
repository, repository,
@ -31,6 +33,7 @@ public class ActionCollectionServiceImpl extends ActionCollectionServiceCEImpl i
policyGenerator, policyGenerator,
applicationService, applicationService,
applicationPermission, applicationPermission,
actionPermission); actionPermission,
observationRegistry);
} }
} }

View File

@ -25,12 +25,14 @@ import com.appsmith.server.services.SessionUserService;
import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.PagePermission;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.observation.ObservationRegistry;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject; import net.minidev.json.JSONObject;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import reactor.core.observability.micrometer.Micrometer;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -46,6 +48,12 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.appsmith.external.constants.spans.LayoutSpan.FIND_ALL_ON_LOAD_EXECUTABLES;
import static com.appsmith.external.constants.spans.LayoutSpan.FIND_AND_UPDATE_LAYOUT;
import static com.appsmith.external.constants.spans.LayoutSpan.UPDATE_EXECUTABLES_EXECUTE_ONLOAD;
import static com.appsmith.external.constants.spans.LayoutSpan.UPDATE_LAYOUT_DSL_METHOD;
import static com.appsmith.external.constants.spans.LayoutSpan.UPDATE_LAYOUT_METHOD;
import static com.appsmith.external.constants.spans.PageSpan.GET_PAGE_BY_ID;
import static com.appsmith.server.constants.CommonConstants.EVALUATION_VERSION; import static com.appsmith.server.constants.CommonConstants.EVALUATION_VERSION;
import static java.lang.Boolean.FALSE; import static java.lang.Boolean.FALSE;
@ -60,8 +68,8 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
private final AnalyticsService analyticsService; private final AnalyticsService analyticsService;
private final PagePermission pagePermission; private final PagePermission pagePermission;
private final ApplicationService applicationService; private final ApplicationService applicationService;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
private final ObservationRegistry observationRegistry;
private final String layoutOnLoadActionErrorToastMessage = private final String layoutOnLoadActionErrorToastMessage =
"A cyclic dependency error has been encountered on current page, \nqueries on page load will not run. \n Please check debugger and Appsmith documentation for more information"; "A cyclic dependency error has been encountered on current page, \nqueries on page load will not run. \n Please check debugger and Appsmith documentation for more information";
@ -122,6 +130,7 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
try { try {
dsl = extractAllWidgetNamesAndDynamicBindingsFromDSL( dsl = extractAllWidgetNamesAndDynamicBindingsFromDSL(
dsl, widgetNames, widgetDynamicBindingsMap, creatorId, layoutId, escapedWidgetNames, creatorType); dsl, widgetNames, widgetDynamicBindingsMap, creatorId, layoutId, escapedWidgetNames, creatorType);
} catch (Throwable t) { } catch (Throwable t) {
return sendUpdateLayoutAnalyticsEvent(creatorId, layoutId, dsl, false, t, creatorType) return sendUpdateLayoutAnalyticsEvent(creatorId, layoutId, dsl, false, t, creatorType)
.then(Mono.error(t)); .then(Mono.error(t));
@ -155,6 +164,8 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
flatmapOnLoadExecutables, flatmapOnLoadExecutables,
executablesUsedInDSL, executablesUsedInDSL,
creatorType) creatorType)
.name(FIND_ALL_ON_LOAD_EXECUTABLES)
.tap(Micrometer.observation(observationRegistry))
.onErrorResume(AppsmithException.class, error -> { .onErrorResume(AppsmithException.class, error -> {
log.info(error.getMessage()); log.info(error.getMessage());
validOnLoadExecutables.set(FALSE); validOnLoadExecutables.set(FALSE);
@ -181,6 +192,8 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
return onLoadExecutablesUtil return onLoadExecutablesUtil
.updateExecutablesExecuteOnLoad( .updateExecutablesExecuteOnLoad(
flatmapOnLoadExecutables, creatorId, executableUpdatesRef, messagesRef, creatorType) flatmapOnLoadExecutables, creatorId, executableUpdatesRef, messagesRef, creatorType)
.name(UPDATE_EXECUTABLES_EXECUTE_ONLOAD)
.tap(Micrometer.observation(observationRegistry))
.thenReturn(allOnLoadExecutables); .thenReturn(allOnLoadExecutables);
}) })
// Now update the page layout with the page load executables and the graph. // Now update the page layout with the page load executables and the graph.
@ -192,7 +205,10 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
// valid when last stored in the database. // valid when last stored in the database.
layout.setValidOnPageLoadActions(validOnLoadExecutables.get()); layout.setValidOnPageLoadActions(validOnLoadExecutables.get());
return onLoadExecutablesUtil.findAndUpdateLayout(creatorId, creatorType, layoutId, layout); return onLoadExecutablesUtil
.findAndUpdateLayout(creatorId, creatorType, layoutId, layout)
.name(FIND_AND_UPDATE_LAYOUT)
.tap(Micrometer.observation(observationRegistry));
}) })
.map(savedLayout -> { .map(savedLayout -> {
savedLayout.setDsl(this.unescapeMongoSpecialCharacters(savedLayout)); savedLayout.setDsl(this.unescapeMongoSpecialCharacters(savedLayout));
@ -219,7 +235,8 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
if (evaluationVersion == null) { if (evaluationVersion == null) {
evaluationVersion = EVALUATION_VERSION; evaluationVersion = EVALUATION_VERSION;
} }
return updateLayoutDsl(pageId, layoutId, layout, evaluationVersion, CreatorContextType.PAGE); return updateLayoutDsl(pageId, layoutId, layout, evaluationVersion, CreatorContextType.PAGE)
.name(UPDATE_LAYOUT_DSL_METHOD);
}); });
} }
@ -276,13 +293,17 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
return Mono.justOrEmpty(pageId) return Mono.justOrEmpty(pageId)
// fetch the unpublished page // fetch the unpublished page
.flatMap(id -> newPageService.findPageById(id, pagePermission.getEditPermission(), false)) .flatMap(id -> newPageService.findPageById(id, pagePermission.getEditPermission(), false))
.name(GET_PAGE_BY_ID)
.tap(Micrometer.observation(observationRegistry))
.flatMapMany(page -> { .flatMapMany(page -> {
if (page.getLayouts() == null) { if (page.getLayouts() == null) {
return Mono.empty(); return Mono.empty();
} }
return Flux.fromIterable(page.getLayouts()).flatMap(layout -> { return Flux.fromIterable(page.getLayouts()).flatMap(layout -> {
layout.setDsl(this.unescapeMongoSpecialCharacters(layout)); layout.setDsl(this.unescapeMongoSpecialCharacters(layout));
return this.updateLayout(page.getId(), page.getApplicationId(), layout.getId(), layout); return this.updateLayout(page.getId(), page.getApplicationId(), layout.getId(), layout)
.name(UPDATE_LAYOUT_METHOD)
.tap(Micrometer.observation(observationRegistry));
}); });
}) })
.collectList() .collectList()
@ -324,7 +345,8 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
AtomicReference<Boolean> validOnLoadExecutables = new AtomicReference<>(Boolean.TRUE); AtomicReference<Boolean> validOnLoadExecutables = new AtomicReference<>(Boolean.TRUE);
// setting the layoutOnLoadActionActionErrors to empty to remove the existing errors before new DAG calculation. // setting the layoutOnLoadActionActionErrors to empty to remove the existing
// errors before new DAG calculation.
layout.setLayoutOnLoadActionErrors(new ArrayList<>()); layout.setLayoutOnLoadActionErrors(new ArrayList<>());
return onLoadExecutablesUtil return onLoadExecutablesUtil
@ -364,13 +386,15 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
String widgetType = dsl.getAsString(FieldName.WIDGET_TYPE); String widgetType = dsl.getAsString(FieldName.WIDGET_TYPE);
if (widgetType.equals(FieldName.TABLE_WIDGET)) { if (widgetType.equals(FieldName.TABLE_WIDGET)) {
// UnEscape Table widget keys // UnEscape Table widget keys
// Since this is a table widget, it wouldnt have children. We can safely return from here with updated // Since this is a table widget, it wouldnt have children. We can safely return
// from here with updated
// dsl // dsl
return WidgetSpecificUtils.unEscapeTableWidgetPrimaryColumns(dsl); return WidgetSpecificUtils.unEscapeTableWidgetPrimaryColumns(dsl);
} }
} }
// Fetch the children of the current node in the DSL and recursively iterate over them to extract bindings // Fetch the children of the current node in the DSL and recursively iterate
// over them to extract bindings
ArrayList<Object> children = (ArrayList<Object>) dsl.get(FieldName.CHILDREN); ArrayList<Object> children = (ArrayList<Object>) dsl.get(FieldName.CHILDREN);
ArrayList<Object> newChildren = new ArrayList<>(); ArrayList<Object> newChildren = new ArrayList<>();
if (children != null) { if (children != null) {
@ -392,9 +416,11 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
/** /**
* Walks the DSL and extracts all the widget names from it. * Walks the DSL and extracts all the widget names from it.
* A widget is expected to have a few properties defining its own behaviour, with any mustache bindings present * A widget is expected to have a few properties defining its own behaviour,
* with any mustache bindings present
* in them aggregated in the field dynamicBindingsPathList. * in them aggregated in the field dynamicBindingsPathList.
* A widget may also have other widgets as children, each of which will follow the same structure * A widget may also have other widgets as children, each of which will follow
* the same structure
* Refer to FieldName.DEFAULT_PAGE_LAYOUT for a template * Refer to FieldName.DEFAULT_PAGE_LAYOUT for a template
* *
* @param dsl * @param dsl
@ -423,27 +449,33 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
String widgetId = dsl.getAsString(FieldName.WIDGET_ID); String widgetId = dsl.getAsString(FieldName.WIDGET_ID);
String widgetType = dsl.getAsString(FieldName.WIDGET_TYPE); String widgetType = dsl.getAsString(FieldName.WIDGET_TYPE);
// Since we are parsing this widget in this, add it to the global set of widgets found so far in the DSL. // Since we are parsing this widget in this, add it to the global set of widgets
// found so far in the DSL.
widgetNames.add(widgetName); widgetNames.add(widgetName);
// Start by picking all fields where we expect to find dynamic bindings for this particular widget // Start by picking all fields where we expect to find dynamic bindings for this
// particular widget
ArrayList<Object> dynamicallyBoundedPathList = (ArrayList<Object>) dsl.get(FieldName.DYNAMIC_BINDING_PATH_LIST); ArrayList<Object> dynamicallyBoundedPathList = (ArrayList<Object>) dsl.get(FieldName.DYNAMIC_BINDING_PATH_LIST);
// Widgets will not have FieldName.DYNAMIC_BINDING_PATH_LIST if there are no bindings in that widget. // Widgets will not have FieldName.DYNAMIC_BINDING_PATH_LIST if there are no
// bindings in that widget.
// Hence we skip over the extraction of the bindings from that widget. // Hence we skip over the extraction of the bindings from that widget.
if (dynamicallyBoundedPathList != null) { if (dynamicallyBoundedPathList != null) {
// Each of these might have nested structures, so we iterate through them to find the leaf node for each // Each of these might have nested structures, so we iterate through them to
// find the leaf node for each
for (Object x : dynamicallyBoundedPathList) { for (Object x : dynamicallyBoundedPathList) {
final String fieldPath = String.valueOf(((Map) x).get(FieldName.KEY)); final String fieldPath = String.valueOf(((Map) x).get(FieldName.KEY));
String[] fields = fieldPath.split("[].\\[]"); String[] fields = fieldPath.split("[].\\[]");
// For nested fields, the parent dsl to search in would shift by one level every iteration // For nested fields, the parent dsl to search in would shift by one level every
// iteration
Object parent = dsl; Object parent = dsl;
Iterator<String> fieldsIterator = Arrays.stream(fields) Iterator<String> fieldsIterator = Arrays.stream(fields)
.filter(fieldToken -> !fieldToken.isBlank()) .filter(fieldToken -> !fieldToken.isBlank())
.iterator(); .iterator();
boolean isLeafNode = false; boolean isLeafNode = false;
Object oldParent; Object oldParent;
// This loop will end at either a leaf node, or the last identified JSON field (by throwing an // This loop will end at either a leaf node, or the last identified JSON field
// (by throwing an
// exception) // exception)
// Valid forms of the fieldPath for this search could be: // Valid forms of the fieldPath for this search could be:
// root.field.list[index].childField.anotherList.indexWithDotOperator.multidimensionalList[index1][index2] // root.field.list[index].childField.anotherList.indexWithDotOperator.multidimensionalList[index1][index2]
@ -510,7 +542,8 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
// Only extract mustache keys from leaf nodes // Only extract mustache keys from leaf nodes
if (isLeafNode) { if (isLeafNode) {
// We found the path. But if the path does not have any mustache bindings, throw the error // We found the path. But if the path does not have any mustache bindings, throw
// the error
if (!MustacheHelper.laxIsBindingPresentInString((String) parent)) { if (!MustacheHelper.laxIsBindingPresentInString((String) parent)) {
try { try {
String bindingAsString = objectMapper.writeValueAsString(parent); String bindingAsString = objectMapper.writeValueAsString(parent);
@ -551,7 +584,8 @@ public class UpdateLayoutServiceCEImpl implements UpdateLayoutServiceCE {
// Escape the widget keys if required and update dsl and escapedWidgetNames // Escape the widget keys if required and update dsl and escapedWidgetNames
removeSpecialCharactersFromKeys(dsl, escapedWidgetNames); removeSpecialCharactersFromKeys(dsl, escapedWidgetNames);
// Fetch the children of the current node in the DSL and recursively iterate over them to extract bindings // Fetch the children of the current node in the DSL and recursively iterate
// over them to extract bindings
ArrayList<Object> children = (ArrayList<Object>) dsl.get(FieldName.CHILDREN); ArrayList<Object> children = (ArrayList<Object>) dsl.get(FieldName.CHILDREN);
ArrayList<Object> newChildren = new ArrayList<>(); ArrayList<Object> newChildren = new ArrayList<>();
if (children != null) { if (children != null) {

View File

@ -7,6 +7,7 @@ import com.appsmith.server.services.AnalyticsService;
import com.appsmith.server.services.SessionUserService; import com.appsmith.server.services.SessionUserService;
import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.PagePermission;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@ -19,7 +20,8 @@ public class UpdateLayoutServiceImpl extends UpdateLayoutServiceCEImpl implement
AnalyticsService analyticsService, AnalyticsService analyticsService,
PagePermission pagePermission, PagePermission pagePermission,
ApplicationService applicationService, ApplicationService applicationService,
ObjectMapper objectMapper) { ObjectMapper objectMapper,
ObservationRegistry observationRegistry) {
super( super(
onLoadExecutablesUtil, onLoadExecutablesUtil,
sessionUserService, sessionUserService,
@ -27,6 +29,7 @@ public class UpdateLayoutServiceImpl extends UpdateLayoutServiceCEImpl implement
analyticsService, analyticsService,
pagePermission, pagePermission,
applicationService, applicationService,
objectMapper); objectMapper,
observationRegistry);
} }
} }

View File

@ -8,6 +8,7 @@ import com.appsmith.server.refactors.applications.RefactoringService;
import com.appsmith.server.services.ce.LayoutActionServiceCEImpl; import com.appsmith.server.services.ce.LayoutActionServiceCEImpl;
import com.appsmith.server.solutions.ActionPermission; import com.appsmith.server.solutions.ActionPermission;
import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.PagePermission;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -24,7 +25,9 @@ public class LayoutActionServiceImpl extends LayoutActionServiceCEImpl implement
UpdateLayoutService updateLayoutService, UpdateLayoutService updateLayoutService,
DatasourceService datasourceService, DatasourceService datasourceService,
PagePermission pagePermission, PagePermission pagePermission,
ActionPermission actionPermission) { ActionPermission actionPermission,
ObservationRegistry observationRegistry) {
super( super(
analyticsService, analyticsService,
newPageService, newPageService,
@ -34,6 +37,7 @@ public class LayoutActionServiceImpl extends LayoutActionServiceCEImpl implement
updateLayoutService, updateLayoutService,
datasourceService, datasourceService,
pagePermission, pagePermission,
actionPermission); actionPermission,
observationRegistry);
} }
} }

View File

@ -9,6 +9,7 @@ import com.appsmith.server.repositories.ActionCollectionRepository;
import com.appsmith.server.services.ce.LayoutCollectionServiceCEImpl; import com.appsmith.server.services.ce.LayoutCollectionServiceCEImpl;
import com.appsmith.server.solutions.ActionPermission; import com.appsmith.server.solutions.ActionPermission;
import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.PagePermission;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -26,7 +27,8 @@ public class LayoutCollectionServiceImpl extends LayoutCollectionServiceCEImpl i
AnalyticsService analyticsService, AnalyticsService analyticsService,
ActionCollectionRepository actionCollectionRepository, ActionCollectionRepository actionCollectionRepository,
PagePermission pagePermission, PagePermission pagePermission,
ActionPermission actionPermission) { ActionPermission actionPermission,
ObservationRegistry observationRegistry) {
super( super(
newPageService, newPageService,
layoutActionService, layoutActionService,
@ -37,6 +39,7 @@ public class LayoutCollectionServiceImpl extends LayoutCollectionServiceCEImpl i
analyticsService, analyticsService,
actionCollectionRepository, actionCollectionRepository,
pagePermission, pagePermission,
actionPermission); actionPermission,
observationRegistry);
} }
} }

View File

@ -23,14 +23,20 @@ import com.appsmith.server.services.AnalyticsService;
import com.appsmith.server.services.CollectionService; import com.appsmith.server.services.CollectionService;
import com.appsmith.server.solutions.ActionPermission; import com.appsmith.server.solutions.ActionPermission;
import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.PagePermission;
import io.micrometer.observation.ObservationRegistry;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import reactor.core.observability.micrometer.Micrometer;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2; import reactor.util.function.Tuple2;
import static com.appsmith.external.constants.spans.ActionSpan.GET_ACTION_BY_ID;
import static com.appsmith.external.constants.spans.ActionSpan.UPDATE_ACTION_BASED_ON_CONTEXT;
import static com.appsmith.external.constants.spans.ActionSpan.UPDATE_SINGLE_ACTION;
import static com.appsmith.external.constants.spans.LayoutSpan.UPDATE_PAGE_LAYOUT_BY_PAGE_ID;
import static java.util.stream.Collectors.toSet; import static java.util.stream.Collectors.toSet;
@Slf4j @Slf4j
@ -47,6 +53,7 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE {
private final DatasourceService datasourceService; private final DatasourceService datasourceService;
private final PagePermission pagePermission; private final PagePermission pagePermission;
private final ActionPermission actionPermission; private final ActionPermission actionPermission;
private final ObservationRegistry observationRegistry;
/** /**
* Called by Action controller to create Action * Called by Action controller to create Action
@ -214,7 +221,11 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE {
public Mono<ActionDTO> updateNewActionByBranchedId(String branchedId, ActionDTO actionDTO) { public Mono<ActionDTO> updateNewActionByBranchedId(String branchedId, ActionDTO actionDTO) {
return newActionService return newActionService
.findById(branchedId, actionPermission.getEditPermission()) .findById(branchedId, actionPermission.getEditPermission())
.flatMap(newAction -> updateActionBasedOnContextType(newAction, actionDTO)); .name(GET_ACTION_BY_ID)
.tap(Micrometer.observation(observationRegistry))
.flatMap(newAction -> updateActionBasedOnContextType(newAction, actionDTO)
.name(UPDATE_ACTION_BASED_ON_CONTEXT)
.tap(Micrometer.observation(observationRegistry)));
} }
/** /**
@ -227,8 +238,13 @@ public class LayoutActionServiceCEImpl implements LayoutActionServiceCE {
action.setApplicationId(null); action.setApplicationId(null);
action.setPageId(null); action.setPageId(null);
return updateSingleAction(newAction.getId(), action) return updateSingleAction(newAction.getId(), action)
.flatMap(updatedAction -> .name(UPDATE_SINGLE_ACTION)
updateLayoutService.updatePageLayoutsByPageId(pageId).thenReturn(updatedAction)) .tap(Micrometer.observation(observationRegistry))
.flatMap(updatedAction -> updateLayoutService
.updatePageLayoutsByPageId(pageId)
.name(UPDATE_PAGE_LAYOUT_BY_PAGE_ID)
.tap(Micrometer.observation(observationRegistry))
.thenReturn(updatedAction))
.zipWhen(actionDTO -> newPageService.findPageById(pageId, pagePermission.getEditPermission(), false)) .zipWhen(actionDTO -> newPageService.findPageById(pageId, pagePermission.getEditPermission(), false))
.map(tuple2 -> { .map(tuple2 -> {
ActionDTO actionDTO = tuple2.getT1(); ActionDTO actionDTO = tuple2.getT1();

View File

@ -23,9 +23,11 @@ import com.appsmith.server.services.AnalyticsService;
import com.appsmith.server.services.LayoutActionService; import com.appsmith.server.services.LayoutActionService;
import com.appsmith.server.solutions.ActionPermission; import com.appsmith.server.solutions.ActionPermission;
import com.appsmith.server.solutions.PagePermission; import com.appsmith.server.solutions.PagePermission;
import io.micrometer.observation.ObservationRegistry;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import reactor.core.observability.micrometer.Micrometer;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@ -36,6 +38,14 @@ import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.appsmith.external.constants.spans.ActionCollectionSpan.ACTION_COLLECTION_UPDATE;
import static com.appsmith.external.constants.spans.ActionCollectionSpan.GENERATE_ACTION_COLLECTION_BY_VIEW_MODE;
import static com.appsmith.external.constants.spans.ActionCollectionSpan.POPULATE_ACTION_COLLECTION_BY_VIEW_MODE;
import static com.appsmith.external.constants.spans.ActionCollectionSpan.SAVE_ACTION_COLLECTION_LAST_EDIT_INFO;
import static com.appsmith.external.constants.spans.ActionSpan.CREATE_ACTION;
import static com.appsmith.external.constants.spans.ActionSpan.DELETE_ACTION;
import static com.appsmith.external.constants.spans.ActionSpan.UPDATE_ACTION;
import static com.appsmith.external.constants.spans.LayoutSpan.UPDATE_LAYOUT_BASED_ON_CONTEXT;
import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNewFieldValuesIntoOldObject; import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNewFieldValuesIntoOldObject;
import static com.appsmith.server.helpers.ContextTypeUtils.isPageContext; import static com.appsmith.server.helpers.ContextTypeUtils.isPageContext;
import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toMap;
@ -55,6 +65,7 @@ public class LayoutCollectionServiceCEImpl implements LayoutCollectionServiceCE
private final ActionCollectionRepository actionCollectionRepository; private final ActionCollectionRepository actionCollectionRepository;
private final PagePermission pagePermission; private final PagePermission pagePermission;
private final ActionPermission actionPermission; private final ActionPermission actionPermission;
private final ObservationRegistry observationRegistry;
@Override @Override
public Mono<ActionCollectionDTO> createCollection(ActionCollection actionCollection) { public Mono<ActionCollectionDTO> createCollection(ActionCollection actionCollection) {
@ -325,7 +336,10 @@ public class LayoutCollectionServiceCEImpl implements LayoutCollectionServiceCE
actionDTO.setPluginId(actionCollectionDTO.getPluginId()); actionDTO.setPluginId(actionCollectionDTO.getPluginId());
actionDTO.setBranchName(branchedActionCollection.getBranchName()); actionDTO.setBranchName(branchedActionCollection.getBranchName());
// actionCollectionService is a new action, we need to create one // actionCollectionService is a new action, we need to create one
return layoutActionService.createSingleAction(actionDTO, Boolean.TRUE); return layoutActionService
.createSingleAction(actionDTO, Boolean.TRUE)
.name(CREATE_ACTION)
.tap(Micrometer.observation(observationRegistry));
} else { } else {
actionDTO.setCollectionId(null); actionDTO.setCollectionId(null);
// Client only knows about the default action ID, fetch branched action id to update the // Client only knows about the default action ID, fetch branched action id to update the
@ -333,7 +347,10 @@ public class LayoutCollectionServiceCEImpl implements LayoutCollectionServiceCE
String branchedActionId = actionDTO.getId(); String branchedActionId = actionDTO.getId();
actionDTO.setId(null); actionDTO.setId(null);
actionDTO.setBaseId(null); actionDTO.setBaseId(null);
return layoutActionService.updateNewActionByBranchedId(branchedActionId, actionDTO); return layoutActionService
.updateNewActionByBranchedId(branchedActionId, actionDTO)
.name(UPDATE_ACTION)
.tap(Micrometer.observation(observationRegistry));
} }
}) })
.collect(toMap(actionDTO -> actionDTO.getBaseId(), ActionDTO::getId))); .collect(toMap(actionDTO -> actionDTO.getBaseId(), ActionDTO::getId)));
@ -354,7 +371,9 @@ public class LayoutCollectionServiceCEImpl implements LayoutCollectionServiceCE
log.error(throwable.getMessage()); log.error(throwable.getMessage());
return Mono.empty(); return Mono.empty();
})) }))
.collectList(); .collectList()
.name(DELETE_ACTION)
.tap(Micrometer.observation(observationRegistry));
return deleteNonExistingActionMono return deleteNonExistingActionMono
.then(newValidActionIdsMono) .then(newValidActionIdsMono)
@ -373,17 +392,27 @@ public class LayoutCollectionServiceCEImpl implements LayoutCollectionServiceCE
}); });
}) })
.flatMap(actionCollection -> actionCollectionService.update(actionCollection.getId(), actionCollection)) .flatMap(actionCollection -> actionCollectionService.update(actionCollection.getId(), actionCollection))
.name(ACTION_COLLECTION_UPDATE)
.tap(Micrometer.observation(observationRegistry))
.flatMap(actionCollectionRepository::setUserPermissionsInObject) .flatMap(actionCollectionRepository::setUserPermissionsInObject)
.flatMap(savedActionCollection -> .flatMap(savedActionCollection -> updateLayoutBasedOnContext(savedActionCollection)
updateLayoutBasedOnContext(savedActionCollection).thenReturn(savedActionCollection)) .name(UPDATE_LAYOUT_BASED_ON_CONTEXT)
.tap(Micrometer.observation(observationRegistry))
.thenReturn(savedActionCollection))
.flatMap(savedActionCollection -> analyticsService.sendUpdateEvent( .flatMap(savedActionCollection -> analyticsService.sendUpdateEvent(
savedActionCollection, actionCollectionService.getAnalyticsProperties(savedActionCollection))) savedActionCollection, actionCollectionService.getAnalyticsProperties(savedActionCollection)))
.flatMap(actionCollection -> actionCollectionService .flatMap(actionCollection -> actionCollectionService
.generateActionCollectionByViewMode(actionCollection, false) .generateActionCollectionByViewMode(actionCollection, false)
.name(GENERATE_ACTION_COLLECTION_BY_VIEW_MODE)
.tap(Micrometer.observation(observationRegistry))
.flatMap(actionCollectionDTO1 -> actionCollectionService .flatMap(actionCollectionDTO1 -> actionCollectionService
.populateActionCollectionByViewMode(actionCollection.getUnpublishedCollection(), false) .populateActionCollectionByViewMode(actionCollection.getUnpublishedCollection(), false)
.name(POPULATE_ACTION_COLLECTION_BY_VIEW_MODE)
.tap(Micrometer.observation(observationRegistry))
.flatMap(actionCollectionDTO2 -> actionCollectionService .flatMap(actionCollectionDTO2 -> actionCollectionService
.saveLastEditInformationInParent(actionCollectionDTO2) .saveLastEditInformationInParent(actionCollectionDTO2)
.name(SAVE_ACTION_COLLECTION_LAST_EDIT_INFO)
.tap(Micrometer.observation(observationRegistry))
.thenReturn(actionCollectionDTO2)))) .thenReturn(actionCollectionDTO2))))
.flatMap(branchedActionCollection -> sendErrorReportsFromPageToCollection(branchedActionCollection)); .flatMap(branchedActionCollection -> sendErrorReportsFromPageToCollection(branchedActionCollection));
} }

View File

@ -28,6 +28,7 @@ import com.appsmith.server.solutions.PagePermission;
import com.appsmith.server.solutions.PagePermissionImpl; import com.appsmith.server.solutions.PagePermissionImpl;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.observation.ObservationRegistry;
import jakarta.validation.Validator; import jakarta.validation.Validator;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
@ -86,6 +87,9 @@ public class ActionCollectionServiceImplTest {
PagePermission pagePermission; PagePermission pagePermission;
ActionPermission actionPermission; ActionPermission actionPermission;
@MockBean
ObservationRegistry observationRegistry;
@MockBean @MockBean
private Validator validator; private Validator validator;
@ -108,7 +112,8 @@ public class ActionCollectionServiceImplTest {
policyGenerator, policyGenerator,
applicationService, applicationService,
applicationPermission, applicationPermission,
actionPermission); actionPermission,
observationRegistry);
layoutCollectionService = new LayoutCollectionServiceImpl( layoutCollectionService = new LayoutCollectionServiceImpl(
newPageService, newPageService,
@ -120,7 +125,8 @@ public class ActionCollectionServiceImplTest {
analyticsService, analyticsService,
actionCollectionRepository, actionCollectionRepository,
pagePermission, pagePermission,
actionPermission); actionPermission,
observationRegistry);
Mockito.when(analyticsService.sendCreateEvent(Mockito.any())) Mockito.when(analyticsService.sendCreateEvent(Mockito.any()))
.thenAnswer( .thenAnswer(
@ -149,6 +155,10 @@ public class ActionCollectionServiceImplTest {
Mockito.when(analyticsService.sendArchiveEvent(Mockito.any(), Mockito.any())) Mockito.when(analyticsService.sendArchiveEvent(Mockito.any(), Mockito.any()))
.thenAnswer( .thenAnswer(
invocationOnMock -> Mono.justOrEmpty(invocationOnMock.getArguments()[0])); invocationOnMock -> Mono.justOrEmpty(invocationOnMock.getArguments()[0]));
ObservationRegistry.ObservationConfig mockObservationConfig =
Mockito.mock(ObservationRegistry.ObservationConfig.class);
Mockito.when(observationRegistry.observationConfig()).thenReturn(mockObservationConfig);
} }
@Test @Test