Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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 a variable it depends on changes
}
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 @@ -18,6 +19,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.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.micrometer.observation.ObservationRegistry;
Expand Down Expand Up @@ -90,6 +92,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 @@ -399,23 +402,45 @@ public Mono<Boolean> updateExecutablesRunBehaviour(
}
}

// 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");
}
return featureFlagService
.check(FeatureFlagEnum.release_reactive_actions_enabled)
.flatMap(isReactiveActionsEnabled -> {
if (isReactiveActionsEnabled) {
// 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 run automatically. You can run it manually when needed.");
}

if (!turnedOnExecutableNames.isEmpty()) {
messagesRef.add(
turnedOnExecutableNames.toString() + " will be executed automatically on page load");
}
if (!turnedOnExecutableNames.isEmpty()) {
messagesRef.add(
turnedOnExecutableNames.toString()
+ " will run automatically on page load or when a variable it depends on changes");
}
} else {
// 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));
});
});
}

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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
package com.appsmith.server.onload.internal;

import com.appsmith.external.dtos.LayoutExecutableUpdateDTO;
import com.appsmith.external.models.Executable;
import com.appsmith.external.models.RunBehaviourEnum;
import com.appsmith.server.onload.executables.ExecutableOnLoadService;
import com.appsmith.server.services.AstService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.util.ArrayList;
import java.util.List;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

class OnLoadExecutablesUtilCEImplTest {

@Mock
private AstService astService;

@Mock
private ObjectMapper objectMapper;

@Mock
private ExecutableOnLoadService executableOnLoadService;

@Mock
private FeatureFlagService featureFlagService;

@InjectMocks
private OnLoadExecutablesUtilCEImpl onLoadExecutablesUtilCEImpl;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
onLoadExecutablesUtilCEImpl = new OnLoadExecutablesUtilCEImpl(
astService,
objectMapper,
executableOnLoadService,
null, // ObservationRegistry
null, // ObservationHelperImpl
featureFlagService);
}

@Test
void updateExecutablesRunBehaviour_shouldReturnFalseWhenNoExecutables() {
List<Executable> onLoadExecutables = new ArrayList<>();
List<LayoutExecutableUpdateDTO> executableUpdatesRef = new ArrayList<>();
List<String> messagesRef = new ArrayList<>();

when(executableOnLoadService.getAllExecutablesByCreatorIdFlux(any())).thenReturn(Flux.empty());

StepVerifier.create(onLoadExecutablesUtilCEImpl.updateExecutablesRunBehaviour(
onLoadExecutables, "creatorId", executableUpdatesRef, messagesRef, null))
.expectNext(false)
.verifyComplete();
}

@Test
void updateExecutablesRunBehaviour_shouldAddMessageWhenExecutableTurnedOn_FeatureFlagOff() {
ActionDTO existing = new ActionDTO();
existing.setName("Api1");
existing.setUserSetOnLoad(false);
existing.setRunBehaviour(RunBehaviourEnum.MANUAL);
existing.setId("1");

ActionDTO toTurnOn = new ActionDTO();
toTurnOn.setName("Api1");
toTurnOn.setUserSetOnLoad(false);
toTurnOn.setRunBehaviour(RunBehaviourEnum.ON_PAGE_LOAD);
toTurnOn.setId("1");

List<Executable> onLoadExecutables = List.of(toTurnOn);
List<LayoutExecutableUpdateDTO> executableUpdatesRef = new ArrayList<>();
List<String> messagesRef = new ArrayList<>();

when(executableOnLoadService.getAllExecutablesByCreatorIdFlux(any())).thenReturn(Flux.just(existing));
when(featureFlagService.check(any())).thenReturn(Mono.just(false));

StepVerifier.create(onLoadExecutablesUtilCEImpl.updateExecutablesRunBehaviour(
onLoadExecutables, "creatorId", executableUpdatesRef, messagesRef, null))
.expectNext(true)
.verifyComplete();

// Should contain the old message
assert messagesRef.stream()
.anyMatch(msg -> msg.contains("Api1") && msg.contains("executed automatically on page load"));
}

@Test
void updateExecutablesRunBehaviour_shouldAddMessageWhenExecutableTurnedOn_FeatureFlagOn() {
ActionDTO existing = new ActionDTO();
existing.setName("Api1");
existing.setUserSetOnLoad(false);
existing.setRunBehaviour(RunBehaviourEnum.MANUAL);
existing.setId("1");

ActionDTO toTurnOn = new ActionDTO();
toTurnOn.setName("Api1");
toTurnOn.setUserSetOnLoad(false);
toTurnOn.setRunBehaviour(RunBehaviourEnum.AUTOMATIC);
toTurnOn.setId("1");

List<Executable> onLoadExecutables = List.of(toTurnOn);
List<LayoutExecutableUpdateDTO> executableUpdatesRef = new ArrayList<>();
List<String> messagesRef = new ArrayList<>();

when(executableOnLoadService.getAllExecutablesByCreatorIdFlux(any())).thenReturn(Flux.just(existing));
when(featureFlagService.check(any())).thenReturn(Mono.just(true));

StepVerifier.create(onLoadExecutablesUtilCEImpl.updateExecutablesRunBehaviour(
onLoadExecutables, "creatorId", executableUpdatesRef, messagesRef, null))
.expectNext(true)
.verifyComplete();

// Should contain the new message
assert messagesRef.stream()
.anyMatch(msg -> msg.contains("Api1")
&& msg.contains(
"will run automatically on page load or when a variable it depends on changes"));
}

@Test
void updateExecutablesRunBehaviour_shouldAddMessageWhenExecutableTurnedOff_FeatureFlagOff() {
ActionDTO existing = new ActionDTO();
existing.setName("Api2");
existing.setUserSetOnLoad(false);
existing.setRunBehaviour(RunBehaviourEnum.ON_PAGE_LOAD);
existing.setId("2");

List<Executable> onLoadExecutables = List.of(); // Now none should be on page load
List<LayoutExecutableUpdateDTO> executableUpdatesRef = new ArrayList<>();
List<String> messagesRef = new ArrayList<>();

when(executableOnLoadService.getAllExecutablesByCreatorIdFlux(any())).thenReturn(Flux.just(existing));
when(featureFlagService.check(any())).thenReturn(Mono.just(false));

StepVerifier.create(onLoadExecutablesUtilCEImpl.updateExecutablesRunBehaviour(
onLoadExecutables, "creatorId", executableUpdatesRef, messagesRef, null))
.expectNext(true)
.verifyComplete();

// Should contain the old message
assert messagesRef.stream()
.anyMatch(msg -> msg.contains("Api2") && msg.contains("no longer be executed on page load"));
}

@Test
void updateExecutablesRunBehaviour_shouldAddMessageWhenExecutableTurnedOff_FeatureFlagOn() {
ActionDTO existing = new ActionDTO();
existing.setName("Api2");
existing.setUserSetOnLoad(false);
existing.setRunBehaviour(RunBehaviourEnum.AUTOMATIC);
existing.setId("2");

List<Executable> onLoadExecutables = List.of(); // Now none should be on page load
List<LayoutExecutableUpdateDTO> executableUpdatesRef = new ArrayList<>();
List<String> messagesRef = new ArrayList<>();

when(executableOnLoadService.getAllExecutablesByCreatorIdFlux(any())).thenReturn(Flux.just(existing));
when(featureFlagService.check(any())).thenReturn(Mono.just(true));

StepVerifier.create(onLoadExecutablesUtilCEImpl.updateExecutablesRunBehaviour(
onLoadExecutables, "creatorId", executableUpdatesRef, messagesRef, null))
.expectNext(true)
.verifyComplete();

// Should contain the new message
assert messagesRef.stream()
.anyMatch(msg -> msg.contains("Api2")
&& msg.contains("will no longer run automatically. You can run it manually when needed."));
}

@Test
void updateExecutablesRunBehaviour_shouldAddMessagesForBothOnAndOff_FeatureFlagOn() {
ActionDTO existing1 = new ActionDTO();
existing1.setName("Api1");
existing1.setUserSetOnLoad(false);
existing1.setRunBehaviour(RunBehaviourEnum.AUTOMATIC);
existing1.setId("1");

ActionDTO existing2 = new ActionDTO();
existing2.setName("Api2");
existing2.setUserSetOnLoad(false);
existing2.setRunBehaviour(RunBehaviourEnum.MANUAL);
existing2.setId("2");

ActionDTO toTurnOn = new ActionDTO();
toTurnOn.setName("Api2");
toTurnOn.setUserSetOnLoad(false);
toTurnOn.setRunBehaviour(RunBehaviourEnum.AUTOMATIC);
toTurnOn.setId("2");

List<Executable> onLoadExecutables = List.of(toTurnOn); // Api2 turned on, Api1 turned off
List<LayoutExecutableUpdateDTO> executableUpdatesRef = new ArrayList<>();
List<String> messagesRef = new ArrayList<>();

when(executableOnLoadService.getAllExecutablesByCreatorIdFlux(any()))
.thenReturn(Flux.just(existing1, existing2));
when(featureFlagService.check(any())).thenReturn(Mono.just(true));

StepVerifier.create(onLoadExecutablesUtilCEImpl.updateExecutablesRunBehaviour(
onLoadExecutables, "creatorId", executableUpdatesRef, messagesRef, null))
.expectNext(true)
.verifyComplete();

assert messagesRef.stream()
.anyMatch(msg -> msg.contains("Api1")
&& msg.contains("will no longer run automatically. You can run it manually when needed."));
assert messagesRef.stream()
.anyMatch(msg -> msg.contains("Api2")
&& msg.contains(
"will run automatically on page load or when a variable it depends on changes"));
}

@Test
void updateExecutablesRunBehaviour_shouldNotAddMessageWhenNoChange_FeatureFlagOn() {
ActionDTO existing = new ActionDTO();
existing.setName("Api3");
existing.setUserSetOnLoad(false);
existing.setRunBehaviour(RunBehaviourEnum.AUTOMATIC);
existing.setId("3");

ActionDTO toStayOn = new ActionDTO();
toStayOn.setName("Api3");
toStayOn.setUserSetOnLoad(false);
toStayOn.setRunBehaviour(RunBehaviourEnum.AUTOMATIC);
toStayOn.setId("3");

List<Executable> onLoadExecutables = List.of(toStayOn);
List<LayoutExecutableUpdateDTO> executableUpdatesRef = new ArrayList<>();
List<String> messagesRef = new ArrayList<>();

when(executableOnLoadService.getAllExecutablesByCreatorIdFlux(any())).thenReturn(Flux.just(existing));
when(featureFlagService.check(any())).thenReturn(Mono.just(true));

StepVerifier.create(onLoadExecutablesUtilCEImpl.updateExecutablesRunBehaviour(
onLoadExecutables, "creatorId", executableUpdatesRef, messagesRef, null))
.expectNext(false)
.verifyComplete();

assert messagesRef.isEmpty();
}

// Add more tests for other scenarios as needed
}