Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public enum FeatureFlagEnum {
release_git_autocommit_feature_enabled,
release_git_autocommit_eligibility_enabled,
release_dynamodb_connection_time_to_live_enabled,
release_reactive_actions_enabled,

// Add EE flags below this line, to avoid conflicts.
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
*/
public enum RunBehaviourEnum {
MANUAL, // Action will only run when manually triggered
ON_PAGE_LOAD // Action will run when the page loads
ON_PAGE_LOAD, // Action will run when the page loads
AUTOMATIC // Action will run automatically (When any of its dependencies change) if the feature flag is enabled
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.appsmith.external.dtos.DslExecutableDTO;
import com.appsmith.external.dtos.LayoutExecutableUpdateDTO;
import com.appsmith.external.enums.FeatureFlagEnum;
import com.appsmith.external.helpers.MustacheHelper;
import com.appsmith.external.models.ActionDTO;
import com.appsmith.external.models.CreatorContextType;
Expand All @@ -15,9 +16,11 @@
import com.appsmith.server.domains.NewPage;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.CollectionUtils;
import com.appsmith.server.helpers.ObservationHelperImpl;
import com.appsmith.server.onload.executables.ExecutableOnLoadService;
import com.appsmith.server.services.AstService;
import com.appsmith.server.services.FeatureFlagService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.observation.ObservationRegistry;
Expand Down Expand Up @@ -90,6 +93,7 @@ public class OnLoadExecutablesUtilCEImpl implements OnLoadExecutablesUtilCE {
private final Set<String> APPSMITH_GLOBAL_VARIABLES = Set.of();
private final ObservationRegistry observationRegistry;
private final ObservationHelperImpl observationHelper;
private final FeatureFlagService featureFlagService;

/**
* This function computes the sequenced on page load executables.
Expand Down Expand Up @@ -320,103 +324,117 @@ public Mono<Boolean> updateExecutablesRunBehaviour(

return existingOnLoadExecutablesMono
.zipWith(creatorContextExecutablesFlux.collectList())
.flatMap(tuple -> {
List<Executable> existingOnLoadExecutables = tuple.getT1();
List<Executable> creatorContextExecutables = tuple.getT2();
.flatMap(tuple -> featureFlagService
.check(FeatureFlagEnum.release_reactive_actions_enabled)
.flatMap(isReactiveActionsEnabled -> {
List<Executable> existingOnLoadExecutables = tuple.getT1();
List<Executable> creatorContextExecutables = tuple.getT2();

// There are no actions in this page. No need to proceed further since no actions would get
// updated
if (CollectionUtils.isNullOrEmpty(creatorContextExecutables)) {
return Mono.just(FALSE);
}

// There are no actions in this page. No need to proceed further since no actions would get updated
if (creatorContextExecutables.isEmpty()) {
return Mono.just(FALSE);
}
// No actions require an update if no actions have been found as page load actions as well
// as
// existing on load page actions are empty
if (CollectionUtils.isNullOrEmpty(existingOnLoadExecutables)
&& (onLoadExecutables == null
|| CollectionUtils.isNullOrEmpty(onLoadExecutables))) {
return Mono.just(FALSE);
}

// No actions require an update if no actions have been found as page load actions as well as
// existing on load page actions are empty
if (existingOnLoadExecutables.isEmpty()
&& (onLoadExecutables == null || onLoadExecutables.isEmpty())) {
return Mono.just(FALSE);
}
// Extract names of existing page load actions and new page load actions for quick lookup.
Set<String> existingOnLoadExecutableNames = existingOnLoadExecutables.stream()
.map(Executable::getUserExecutableName)
.collect(Collectors.toSet());

// Extract names of existing page load actions and new page load actions for quick lookup.
Set<String> existingOnLoadExecutableNames = existingOnLoadExecutables.stream()
.map(Executable::getUserExecutableName)
.collect(Collectors.toSet());

Set<String> newOnLoadExecutableNames = onLoadExecutables.stream()
.map(Executable::getUserExecutableName)
.collect(Collectors.toSet());

// Calculate the actions which would need to be updated from execute on load TRUE to FALSE.
Set<String> turnedOffExecutableNames = new HashSet<>();
turnedOffExecutableNames.addAll(existingOnLoadExecutableNames);
turnedOffExecutableNames.removeAll(newOnLoadExecutableNames);

// Calculate the actions which would need to be updated from execute on load FALSE to TRUE
Set<String> turnedOnExecutableNames = new HashSet<>();
turnedOnExecutableNames.addAll(newOnLoadExecutableNames);
turnedOnExecutableNames.removeAll(existingOnLoadExecutableNames);

for (Executable executable : creatorContextExecutables) {

String executableName = executable.getUserExecutableName();
// If a user has ever set execute on load, this field can not be changed automatically. It has
// to be explicitly changed by the user again. Add the executable to update only if this
// condition is false.
if (FALSE.equals(executable.getUserSetOnLoad())) {

// If this executable is no longer an onload executable, turn the execute on load to false
if (turnedOffExecutableNames.contains(executableName)) {
executable.setRunBehaviour(RunBehaviourEnum.MANUAL);
toUpdateExecutables.add(executable);
}
Set<String> newOnLoadExecutableNames = onLoadExecutables.stream()
.map(Executable::getUserExecutableName)
.collect(Collectors.toSet());

// If this executable is newly found to be on load, turn execute on load to true
if (turnedOnExecutableNames.contains(executableName)) {
executable.setRunBehaviour(RunBehaviourEnum.ON_PAGE_LOAD);
toUpdateExecutables.add(executable);
}
// Calculate the actions which would need to be updated from execute on load TRUE to FALSE.
Set<String> turnedOffExecutableNames = new HashSet<>();
turnedOffExecutableNames.addAll(existingOnLoadExecutableNames);
turnedOffExecutableNames.removeAll(newOnLoadExecutableNames);

// Calculate the actions which would need to be updated from execute on load FALSE to TRUE
Set<String> turnedOnExecutableNames = new HashSet<>();
turnedOnExecutableNames.addAll(newOnLoadExecutableNames);
turnedOnExecutableNames.removeAll(existingOnLoadExecutableNames);

for (Executable executable : creatorContextExecutables) {

String executableName = executable.getUserExecutableName();
// If a user has ever set execute on load, this field can not be changed automatically.
// It has
// to be explicitly changed by the user again. Add the executable to update only if this
// condition is false.
if (FALSE.equals(executable.getUserSetOnLoad())) {

// If this executable is no longer an onload executable, turn the execute on load to
// false
if (turnedOffExecutableNames.contains(executableName)) {
executable.setRunBehaviour(RunBehaviourEnum.MANUAL);
toUpdateExecutables.add(executable);
}

} else {
// Remove the executable name from either of the lists (if present) because this executable
// should not be updated
turnedOnExecutableNames.remove(executableName);
turnedOffExecutableNames.remove(executableName);
}
}
// If this executable is newly found to be on load, turn execute on load to true
if (turnedOnExecutableNames.contains(executableName)) {
// Use the prefetched feature flag value
if (isReactiveActionsEnabled) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you plan to include the auto-commit change in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We won't need auto commit changes for this right? We are not introducing new prop now, we are introducing new state for that new prop we added in milestone 1

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a case which can cause diff on top of the clean state of the app.

Let’s assume the app is connected to Git and an action is set to run on page load (ON_PAGE_LOAD in the DB) with no diffs. When the feature flag for AUTOMATIC run behavior is enabled, and the user makes changes that turn off the on-page load setting, the DB updates to MANUAL. After discarding changes and pulling the latest commit, the app should have no diffs. However, the feature flag causes the run behavior to switch to AUTOMATIC during reconstruction, resulting in an unexpected diff. This is the specific edge case I’m referring to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a very valid concern and thanks for raising this!! @subrata71

@infinitetrooper @pedro-santos-rodrigues Can you please check this edge case and let me know what should happen in this case?

I have triggered EE DP here in case you need to test this scenario

CC: @vivek-appsmith @ankitakinger

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understood the scenario you're discussing, it seems that this won't be an issue since a change from ON_PAGE_LOAD to AUTOMATIC is never done itself by the system when the feature flag is enabled but rather in the next time the Query/JS is going to be bound in a widget with a .data after enabling the feature flag, as you can check in the Migration Path defined:
image

Am I missing anything here, or with that point it gets clear the expected behavior?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pedro-santos-rodrigues Could you please review the scenario I shared? Do you agree that, in this specific case, it would lead to uncommitted changes? If so, do you think this is something we should be concerned about?

executable.setRunBehaviour(RunBehaviourEnum.AUTOMATIC);
} else {
executable.setRunBehaviour(RunBehaviourEnum.ON_PAGE_LOAD);
}
toUpdateExecutables.add(executable);
}

// Add newly turned on page actions to report back to the caller
executableUpdatesRef.addAll(
addExecutableUpdatesForExecutableNames(creatorContextExecutables, turnedOnExecutableNames));
} else {
// Remove the executable name from either of the lists (if present) because this
// executable
// should not be updated
turnedOnExecutableNames.remove(executableName);
turnedOffExecutableNames.remove(executableName);
}
}

// Add newly turned off page actions to report back to the caller
executableUpdatesRef.addAll(addExecutableUpdatesForExecutableNames(
creatorContextExecutables, turnedOffExecutableNames));
// Add newly turned on page actions to report back to the caller
executableUpdatesRef.addAll(addExecutableUpdatesForExecutableNames(
creatorContextExecutables, turnedOnExecutableNames));

for (Executable executable : creatorContextExecutables) {
String executableName = executable.getUserExecutableName();
if (Boolean.FALSE.equals(executable.isOnLoadMessageAllowed())) {
turnedOffExecutableNames.remove(executableName);
turnedOnExecutableNames.remove(executableName);
}
}
// Add newly turned off page actions to report back to the caller
executableUpdatesRef.addAll(addExecutableUpdatesForExecutableNames(
creatorContextExecutables, turnedOffExecutableNames));

// Now add messagesRef that would eventually be displayed to the developer user informing them
// about the action setting change.
if (!turnedOffExecutableNames.isEmpty()) {
messagesRef.add(
turnedOffExecutableNames.toString() + " will no longer be executed on page load");
}
for (Executable executable : creatorContextExecutables) {
String executableName = executable.getUserExecutableName();
if (Boolean.FALSE.equals(executable.isOnLoadMessageAllowed())) {
turnedOffExecutableNames.remove(executableName);
turnedOnExecutableNames.remove(executableName);
}
}

if (!turnedOnExecutableNames.isEmpty()) {
messagesRef.add(
turnedOnExecutableNames.toString() + " will be executed automatically on page load");
}
// Now add messagesRef that would eventually be displayed to the developer user informing
// them
// about the action setting change.
if (!turnedOffExecutableNames.isEmpty()) {
messagesRef.add(turnedOffExecutableNames.toString()
+ " will no longer be executed on page load");
}

// Finally update the actions which require an update
return Flux.fromIterable(toUpdateExecutables)
.flatMap(executable ->
this.updateUnpublishedExecutable(executable.getId(), executable, creatorType))
.then(Mono.just(TRUE));
});
if (!turnedOnExecutableNames.isEmpty()) {
messagesRef.add(turnedOnExecutableNames.toString()
+ " will be executed automatically on page load");
}

// Finally update the actions which require an update
return Flux.fromIterable(toUpdateExecutables)
.flatMap(executable -> this.updateUnpublishedExecutable(
executable.getId(), executable, creatorType))
.then(Mono.just(TRUE));
}));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.appsmith.server.helpers.ObservationHelperImpl;
import com.appsmith.server.onload.executables.ExecutableOnLoadService;
import com.appsmith.server.services.AstService;
import com.appsmith.server.services.FeatureFlagService;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -18,7 +19,14 @@ public OnLoadExecutablesUtilImpl(
ObjectMapper objectMapper,
ExecutableOnLoadService<NewPage> pageExecutableOnLoadService,
ObservationRegistry observationRegistry,
ObservationHelperImpl observationHelper) {
super(astService, objectMapper, pageExecutableOnLoadService, observationRegistry, observationHelper);
ObservationHelperImpl observationHelper,
FeatureFlagService featureFlagService) {
super(
astService,
objectMapper,
pageExecutableOnLoadService,
observationRegistry,
observationHelper,
featureFlagService);
}
}
Loading