From 731a05844f97f1d0e9761dd1e687478da243784b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Oct 2023 10:51:45 -0700 Subject: [PATCH 1/4] Bump com.smartystreets.api:smartystreets-java-sdk from 3.14.0 to 3.14.1 (#426) Bumps [com.smartystreets.api:smartystreets-java-sdk](https://github.com/smartystreets/smartystreets-java-sdk) from 3.14.0 to 3.14.1. - [Release notes](https://github.com/smartystreets/smartystreets-java-sdk/releases) - [Changelog](https://github.com/smartystreets/smartystreets-java-sdk/blob/master/CHANGELOG.md) - [Commits](https://github.com/smartystreets/smartystreets-java-sdk/compare/3.14.0...3.14.1) --- updated-dependencies: - dependency-name: com.smartystreets.api:smartystreets-java-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 92998759b..55ed4388b 100644 --- a/build.gradle +++ b/build.gradle @@ -78,7 +78,7 @@ dependencies { implementation 'org.flywaydb:flyway-core:9.22.3' implementation 'org.webjars.npm:dropzone:5.9.3' implementation 'com.amazonaws:aws-java-sdk-s3:1.12.364' - implementation 'com.smartystreets.api:smartystreets-java-sdk:3.14.0' + implementation 'com.smartystreets.api:smartystreets-java-sdk:3.14.1' implementation 'org.apache.pdfbox:pdfbox:3.0.0' implementation 'org.yaml:snakeyaml:2.2' implementation 'com.mailgun:mailgun-java:1.1.0' From 9910dbb1f63e5e9498cb45c005f05c44df610108 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Oct 2023 09:36:57 -0700 Subject: [PATCH 2/4] Bump io.github.bonigarcia:webdrivermanager from 5.5.3 to 5.6.0 (#428) Bumps [io.github.bonigarcia:webdrivermanager](https://github.com/bonigarcia/webdrivermanager) from 5.5.3 to 5.6.0. - [Release notes](https://github.com/bonigarcia/webdrivermanager/releases) - [Changelog](https://github.com/bonigarcia/webdrivermanager/blob/master/CHANGELOG.md) - [Commits](https://github.com/bonigarcia/webdrivermanager/compare/webdrivermanager-5.5.3...webdrivermanager-5.6.0) --- updated-dependencies: - dependency-name: io.github.bonigarcia:webdrivermanager dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 55ed4388b..b07984c4f 100644 --- a/build.gradle +++ b/build.gradle @@ -104,7 +104,7 @@ dependencies { // we should take a look at this dependency and see if it's really testing things correctly and // monitor the dependencies. testImplementation 'org.apache.httpcomponents.client5:httpclient5:5.2.1' - testImplementation 'io.github.bonigarcia:webdrivermanager:5.5.3' + testImplementation 'io.github.bonigarcia:webdrivermanager:5.6.0' testImplementation 'org.awaitility:awaitility' testImplementation 'org.seleniumhq.selenium:selenium-java' testImplementation 'org.postgresql:postgresql' From 5408db645bc6f1923756954ee41f6dbebbf99e11 Mon Sep 17 00:00:00 2001 From: Sree <72890349+sree-cfa@users.noreply.github.com> Date: Fri, 27 Oct 2023 13:41:54 -0700 Subject: [PATCH 3/4] Adding icons from design library and fixing clip id conflict (#427) * Removing icons that are included with material-icons --- .../resources/templates/fragments/icons.html | 170 ++++++++++++++++-- 1 file changed, 156 insertions(+), 14 deletions(-) diff --git a/src/main/resources/templates/fragments/icons.html b/src/main/resources/templates/fragments/icons.html index 5f368c551..c2bdab4a3 100644 --- a/src/main/resources/templates/fragments/icons.html +++ b/src/main/resources/templates/fragments/icons.html @@ -8,6 +8,109 @@

Icons

+ + moneyTips + + + + + + + + + + + + + + + + + + householdFamilyIncome + + + + + + + + + + + + + + + + + + + + + + + + assetsCashAccounts + + + + + + + + + + + + + + + + + + + + + personMilestone + + + + + + + + + + timeoutClock + + + + + + + + + + + + + + filledLock + + + + + + + + + + + + + + address @@ -27,6 +130,45 @@

Icons

+ + monetization + + + + + + + + syringe + + + + + + translation + + + + + homeCare @@ -770,7 +912,7 @@

Icons

Icons

stroke-linecap="round" stroke-linejoin="round"/> - + @@ -1672,18 +1814,6 @@

Icons

- - - smallPersonIcon - - - - smallDoubleFileIcon @@ -2041,6 +2171,18 @@

Icons

+ + + iconHouseOutlineTiny + + + + + + + From 95b79c3e6066e201236aba145b14496ca75f4da2 Mon Sep 17 00:00:00 2001 From: Martha Pidcock <122047394+mpidcock@users.noreply.github.com> Date: Fri, 27 Oct 2023 16:59:42 -0700 Subject: [PATCH 4/4] Add Ability to Disable a flow (#422) * Add DisabledFlowInterceptor * See starter app index.html for examples of it in action [#185951075] Co-authored-by: Martha Pidcock --- README.md | 30 ++++++++ .../formflow/library/ScreenController.java | 14 ++-- .../DisabledFlowInterceptorConfiguration.java | 36 ++++++++++ .../DisabledFlowPropertyConfiguration.java | 44 ++++++++++++ .../library/config/FlowConfiguration.java | 5 +- .../config/FlowsConfigurationFactory.java | 30 +++++++- .../FlowsConfigurationFactoryConfig.java | 13 +++- ...ionContinuityInterceptorConfiguration.java | 10 ++- .../interceptors/DisabledFlowInterceptor.java | 69 +++++++++++++++++++ .../SessionContinuityInterceptor.java | 5 +- .../DisabledFlowInterceptorTest.java | 65 +++++++++++++++++ .../interceptors/SpyInterceptorConfig.java | 11 +++ src/test/resources/application-test.yaml | 1 + 13 files changed, 318 insertions(+), 15 deletions(-) create mode 100644 src/main/java/formflow/library/config/DisabledFlowInterceptorConfiguration.java create mode 100644 src/main/java/formflow/library/config/DisabledFlowPropertyConfiguration.java create mode 100644 src/main/java/formflow/library/interceptors/DisabledFlowInterceptor.java create mode 100644 src/test/java/formflow/library/controllers/DisabledFlowInterceptorTest.java diff --git a/README.md b/README.md index f3fba4821..b7361418d 100644 --- a/README.md +++ b/README.md @@ -2335,6 +2335,14 @@ to see if a user is on, in the event that they don't have a Session or Submissio It is a page in the flow that a user can enter without having an active Session; it is marking that this page is an entry point into a flow. +### Disabled Flow Interceptor + +The `DisabledFlowInterceptor` is an interceptor that prevents users from accessing a flow that has +been disabled through configuration. This interceptor will redirect users to a configured static screen +when they attempt to access a screen within a disabled flow. + +For more information on disabling a flow, please see the [Disabling a Flow](#disabling-a-flow) section. + # How to use ## Configuration Details @@ -2387,6 +2395,24 @@ form-flow: path: 'name-of-file.yaml' ``` +#### Disabling a Flow + +The form flow library provides a configuration mechanism to disable a flow. This will prevent users +from being able to access a given flow, and will redirect the user to a configured screen if they +attempt to reach a page within a disabled flow. + +Here are two examples of disabled flows, the first goes to the home page and the second goes to a +unique static page. The default is the home page if no `staticRedirectPage` is configured. + +```yaml +form-flow: + disabled-flows: + - flow: ubi + staticRedirectPage: '/' + - flow: docUpload + staticRedirectPage: '/disabledFeature' +``` + #### Design System We are moving towards using a [custom theme](https://codeforamerica.github.io/uswds/dist/) of @@ -2577,6 +2603,10 @@ any one flow, the `Submission` for each flow will be stored in the database. point to go back to the other flow. Otherwise, the flows may not get completed properly.** +The form flow library provides a configuration mechanism to disable a flow. This is useful if you are +still developing a flow, or if a flow has reached its end of life. For more information on +disabling flows see: [Disabling a Flow](#disabling-a-flow) + ### Screens Screens are the actual form that will be displayed to the user. Screens are specified as steps in diff --git a/src/main/java/formflow/library/ScreenController.java b/src/main/java/formflow/library/ScreenController.java index a0f5c450a..eaf93b2a8 100644 --- a/src/main/java/formflow/library/ScreenController.java +++ b/src/main/java/formflow/library/ScreenController.java @@ -49,9 +49,11 @@ @Controller @EnableAutoConfiguration @Slf4j -@RequestMapping("/flow") +@RequestMapping(ScreenController.FLOW) public class ScreenController extends FormFlowController { + public static final String FLOW = "/flow"; + public static final String FLOW_SCREEN_PATH = "{flow}/{screen}"; private final ValidationService validationService; private final AddressValidationService addressValidationService; private final ConditionManager conditionManager; @@ -85,7 +87,7 @@ public ScreenController( * @param httpSession The current httpSession, not null * @return the screen template with model data */ - @GetMapping("{flow}/{screen}") + @GetMapping(FLOW_SCREEN_PATH) ModelAndView getScreen( @PathVariable String flow, @PathVariable String screen, @@ -145,7 +147,7 @@ ModelAndView getScreen( * @param httpSession The HTTP session if it exists, can be null * @return a redirect to endpoint that gets the next screen in the flow */ - @PostMapping({"{flow}/{screen}", "{flow}/{screen}/submit"}) + @PostMapping({FLOW_SCREEN_PATH, FLOW_SCREEN_PATH + "/submit"}) ModelAndView postScreen( @RequestParam(required = false) MultiValueMap formData, @PathVariable String flow, @@ -207,7 +209,7 @@ ModelAndView postScreen( * @param httpSession The current httpSession, not null * @return the screen template with model data */ - @GetMapping({"{flow}/{screen}/{uuid}", "{flow}/{screen}/{uuid}/edit"}) + @GetMapping({FLOW_SCREEN_PATH + "/{uuid}", FLOW_SCREEN_PATH + "/{uuid}/edit"}) ModelAndView getSubflowScreen( @PathVariable String flow, @PathVariable String screen, @@ -255,7 +257,7 @@ ModelAndView getSubflowScreen( * @param httpSession The HTTP session if it exists, not null * @return a redirect to next screen */ - @PostMapping({"{flow}/{screen}/{uuid}", "{flow}/{screen}/{uuid}/edit"}) + @PostMapping({FLOW_SCREEN_PATH + "/{uuid}", FLOW_SCREEN_PATH + "/{uuid}/edit"}) RedirectView postSubflowScreen( @RequestParam(required = false) MultiValueMap formData, @PathVariable String flow, @@ -438,7 +440,7 @@ ModelAndView deleteSubflowIteration( * @param httpSession The current httpSession, not null * @return the screen template with model data, returns error page on error */ - @GetMapping("{flow}/{screen}/navigation") + @GetMapping(FLOW_SCREEN_PATH + "/navigation") ModelAndView navigation( @PathVariable String flow, @PathVariable String screen, diff --git a/src/main/java/formflow/library/config/DisabledFlowInterceptorConfiguration.java b/src/main/java/formflow/library/config/DisabledFlowInterceptorConfiguration.java new file mode 100644 index 000000000..d40c1229e --- /dev/null +++ b/src/main/java/formflow/library/config/DisabledFlowInterceptorConfiguration.java @@ -0,0 +1,36 @@ +package formflow.library.config; + + +import static formflow.library.interceptors.DisabledFlowInterceptor.PATH_FORMAT; + +import formflow.library.interceptors.DisabledFlowInterceptor; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/*** + * Adds DisabledFlowInterceptorConfiguration to the Interceptor registry. + */ +@Configuration +public class DisabledFlowInterceptorConfiguration implements WebMvcConfigurer { + + DisabledFlowPropertyConfiguration disabledFlowPropertyConfiguration; + + public DisabledFlowInterceptorConfiguration(DisabledFlowPropertyConfiguration disabledFlowPropertyConfiguration) { + this.disabledFlowPropertyConfiguration = disabledFlowPropertyConfiguration; + } + + /** + * Adds the DisabledFlowInterceptor to the Interceptor registry. + * @param registry The Interceptor registry. + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new DisabledFlowInterceptor(disabledFlowPropertyConfiguration)) + .addPathPatterns(List.of(PATH_FORMAT)); + } +} diff --git a/src/main/java/formflow/library/config/DisabledFlowPropertyConfiguration.java b/src/main/java/formflow/library/config/DisabledFlowPropertyConfiguration.java new file mode 100644 index 000000000..8a8820f13 --- /dev/null +++ b/src/main/java/formflow/library/config/DisabledFlowPropertyConfiguration.java @@ -0,0 +1,44 @@ +package formflow.library.config; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import lombok.Getter; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +//@ConditionalOnProperty(name = "form-flow.disabled-flows") +@ConfigurationProperties(prefix = "form-flow") +@Getter +@Setter +public class DisabledFlowPropertyConfiguration { + private List> disabledFlows = new ArrayList<>(); + + + /** + * Checks if the flow with flowName is disabled. + * @param flowName the name of the flow to check. + * @return true if the flow is disabled, false otherwise. + */ + public boolean isFlowDisabled(String flowName) { + return this.disabledFlows.stream().anyMatch(flow -> flow.get("flow").equals(flowName)); + } + + /** + * Gets the static redirect page for a disabled flow. + * @param flowName the name of the flow to check. + * @return the static redirect page for the flow if disabled flows are configured and the flow is disabled. + */ + public String getDisabledFlowRedirect(String flowName) { + return disabledFlows.stream() + .filter(flow -> flow.get("flow").equals(flowName)) + .map(flow -> flow.getOrDefault("staticRedirectPage", "")) + .findFirst() + .orElse(null); + } +} diff --git a/src/main/java/formflow/library/config/FlowConfiguration.java b/src/main/java/formflow/library/config/FlowConfiguration.java index e3db7d671..3422be91a 100644 --- a/src/main/java/formflow/library/config/FlowConfiguration.java +++ b/src/main/java/formflow/library/config/FlowConfiguration.java @@ -1,16 +1,15 @@ package formflow.library.config; -import formflow.library.config.submission.Action; -import formflow.library.config.submission.Condition; import java.util.HashMap; -import java.util.List; import lombok.Data; +import lombok.Getter; /** * Represents the configuration for a certain flow. */ @Data +@Getter public class FlowConfiguration { private String name; diff --git a/src/main/java/formflow/library/config/FlowsConfigurationFactory.java b/src/main/java/formflow/library/config/FlowsConfigurationFactory.java index 72b5f8e60..95129fc59 100644 --- a/src/main/java/formflow/library/config/FlowsConfigurationFactory.java +++ b/src/main/java/formflow/library/config/FlowsConfigurationFactory.java @@ -3,8 +3,11 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.concurrent.Flow; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.yaml.snakeyaml.DumperOptions; @@ -21,7 +24,26 @@ public class FlowsConfigurationFactory implements FactoryBean getObject() throws IOException { ClassPathResource classPathResource = new ClassPathResource(configPath); @@ -36,7 +58,13 @@ public List getObject() throws IOException { List appConfigs = new ArrayList<>(); try { Iterable appConfigsIterable = yaml.loadAll(classPathResource.getInputStream()); - appConfigsIterable.forEach(appConfig -> appConfigs.add((FlowConfiguration) appConfig)); + appConfigsIterable.forEach(appConfig -> { + FlowConfiguration flowConfig = (FlowConfiguration) appConfig; + if (disabledFlowPropertyConfiguration == null || !disabledFlowPropertyConfiguration.isFlowDisabled(flowConfig.getName())) { + appConfigs.add(flowConfig); + } + + }); } catch (IOException e) { log.error("Can't find the flow configuration file: " + configPath, e); throw e; diff --git a/src/main/java/formflow/library/config/FlowsConfigurationFactoryConfig.java b/src/main/java/formflow/library/config/FlowsConfigurationFactoryConfig.java index 67af72d71..1799c4056 100644 --- a/src/main/java/formflow/library/config/FlowsConfigurationFactoryConfig.java +++ b/src/main/java/formflow/library/config/FlowsConfigurationFactoryConfig.java @@ -1,10 +1,14 @@ package formflow.library.config; import java.io.IOException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.List; +import java.util.Optional; /** * Factory for FlowsConfiguration configuration. @@ -12,6 +16,10 @@ @Configuration public class FlowsConfigurationFactoryConfig { + @Autowired(required = false) + DisabledFlowPropertyConfiguration disabledFlowPropertyConfiguration; + + /** * Bean to get a FlowsConfigurationFactory object. * @@ -19,7 +27,10 @@ public class FlowsConfigurationFactoryConfig { */ @Bean public FlowsConfigurationFactory flowsConfigurationFactory() { - return new FlowsConfigurationFactory(); + if (this.disabledFlowPropertyConfiguration == null) { + return new FlowsConfigurationFactory(); + } + return new FlowsConfigurationFactory(disabledFlowPropertyConfiguration); } /** diff --git a/src/main/java/formflow/library/config/SessionContinuityInterceptorConfiguration.java b/src/main/java/formflow/library/config/SessionContinuityInterceptorConfiguration.java index 46da93bf9..b315fb01a 100644 --- a/src/main/java/formflow/library/config/SessionContinuityInterceptorConfiguration.java +++ b/src/main/java/formflow/library/config/SessionContinuityInterceptorConfiguration.java @@ -17,10 +17,16 @@ public class SessionContinuityInterceptorConfiguration implements WebMvcConfigur @Autowired List flowConfigurations; - + + + /** + * Adds the SessionContinuityInterceptor to the Interceptor registry. + * @param registry the Interceptor registry. + */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new SessionContinuityInterceptor(flowConfigurations)) - .addPathPatterns(List.of(SessionContinuityInterceptor.FLOW_PATH_FORMAT, SessionContinuityInterceptor.NAVIGATION_FLOW_PATH_FORMAT)); + .addPathPatterns(List.of(SessionContinuityInterceptor.FLOW_PATH_FORMAT, + SessionContinuityInterceptor.NAVIGATION_FLOW_PATH_FORMAT)); } } diff --git a/src/main/java/formflow/library/interceptors/DisabledFlowInterceptor.java b/src/main/java/formflow/library/interceptors/DisabledFlowInterceptor.java new file mode 100644 index 000000000..2a177a778 --- /dev/null +++ b/src/main/java/formflow/library/interceptors/DisabledFlowInterceptor.java @@ -0,0 +1,69 @@ +package formflow.library.interceptors; + +import formflow.library.ScreenController; +import formflow.library.config.DisabledFlowPropertyConfiguration; +import formflow.library.exceptions.LandmarkNotSetException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.servlet.HandlerInterceptor; + +/** + * This interceptor redirects users to the configured screen if a flow is marked as disabled. + */ +@Component +@Slf4j +public class DisabledFlowInterceptor implements HandlerInterceptor, Ordered { + public static final String PATH_FORMAT = ScreenController.FLOW + "/" + ScreenController.FLOW_SCREEN_PATH; + + DisabledFlowPropertyConfiguration disabledFlowPropertyConfiguration; + + public DisabledFlowInterceptor(DisabledFlowPropertyConfiguration disabledFlowPropertyConfiguration) { + this.disabledFlowPropertyConfiguration = disabledFlowPropertyConfiguration; + } + + /** + * Redirects users to the configured screen if a flow is marked as disabled. + * + * @param request current HTTP request + * @param response current HTTP response + * @param handler chosen handler to execute, for type and/or instance evaluation + * @return true if the flow is enabled, which will pass the request to the next registered interceptor. False if the flow is disabled, + * which will redirect the user to the configured staticRedirectPage, and stop the interceptor chain. + */ + + @Override + public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) { + Map parsedUrl = new AntPathMatcher().extractUriTemplateVariables(PATH_FORMAT, request.getRequestURI()); + String requestedFlow = parsedUrl.get("flow"); + if (disabledFlowPropertyConfiguration.isFlowDisabled(requestedFlow)) { + String staticRedirectPage = disabledFlowPropertyConfiguration.getDisabledFlowRedirect(requestedFlow); + if (staticRedirectPage == null || staticRedirectPage.isEmpty()) { + log.warn("Flow %s is disabled but no static redirect page is configured. Going home instead.".formatted(requestedFlow)); + staticRedirectPage = "/"; + } + log.info("Flow %s is disabled. Redirecting to %s.".formatted(requestedFlow, staticRedirectPage)); + + try { + response.sendRedirect(staticRedirectPage); + } catch (IOException e) { + throw new RuntimeException(e); + } + return false; + } + return true; + } + + @Override + public int getOrder() { + return HIGHEST_PRECEDENCE; + } +} diff --git a/src/main/java/formflow/library/interceptors/SessionContinuityInterceptor.java b/src/main/java/formflow/library/interceptors/SessionContinuityInterceptor.java index a06d12d1c..7d4881b93 100644 --- a/src/main/java/formflow/library/interceptors/SessionContinuityInterceptor.java +++ b/src/main/java/formflow/library/interceptors/SessionContinuityInterceptor.java @@ -1,6 +1,7 @@ package formflow.library.interceptors; import formflow.library.FormFlowController; +import formflow.library.ScreenController; import formflow.library.config.FlowConfiguration; import formflow.library.exceptions.LandmarkNotSetException; import jakarta.servlet.http.HttpServletRequest; @@ -25,8 +26,8 @@ @ConditionalOnProperty(name = "form-flow.session-continuity-interceptor.enabled", havingValue = "true") public class SessionContinuityInterceptor implements HandlerInterceptor, Ordered { - public static final String FLOW_PATH_FORMAT = "/flow/{flow}/{screen}"; - public static final String NAVIGATION_FLOW_PATH_FORMAT = "/flow/{flow}/{screen}/navigation"; + public static final String FLOW_PATH_FORMAT = ScreenController.FLOW + "/" + ScreenController.FLOW_SCREEN_PATH; + public static final String NAVIGATION_FLOW_PATH_FORMAT = FLOW_PATH_FORMAT + "/navigation"; private static final String REDIRECT_URL = "/"; diff --git a/src/test/java/formflow/library/controllers/DisabledFlowInterceptorTest.java b/src/test/java/formflow/library/controllers/DisabledFlowInterceptorTest.java new file mode 100644 index 000000000..0bbecc72a --- /dev/null +++ b/src/test/java/formflow/library/controllers/DisabledFlowInterceptorTest.java @@ -0,0 +1,65 @@ +package formflow.library.controllers; + +import static formflow.library.FormFlowController.SUBMISSION_MAP_NAME; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import formflow.library.address_validation.AddressValidationService; +import formflow.library.address_validation.ValidatedAddress; +import formflow.library.data.Submission; +import formflow.library.utilities.AbstractMockMvcTest; +import formflow.library.utilities.FormScreen; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.util.LinkedMultiValueMap; + +@SpringBootTest(properties = {"form-flow.path=flows-config/test-flow.yaml"}) +@TestPropertySource(properties = { + "form-flow.disabled-flows[0].flow=testFlow", + "form-flow.disabled-flows[0].staticRedirectPage=/", + "form-flow.disabled-flows[1].flow=otherTestFlow", + "form-flow.disabled-flows[1].staticRedirectPage=/disabledFeature", +}) +public class DisabledFlowInterceptorTest extends AbstractMockMvcTest { + + @BeforeEach + public void setUp() throws Exception { + UUID submissionUUID = UUID.randomUUID(); + submission = Submission.builder().id(submissionUUID).urlParams(new HashMap<>()).inputData(new HashMap<>()).build(); + // this setups flow info in the session to get passed along later on. + setFlowInfoInSession(session, "testFlow", submission.getId()); + super.setUp(); + } + + @Test + public void shouldRedirectToConfiguredScreenWhenDisabledFlowInterceptionIsEnabled() throws Exception { + mockMvc.perform(get("/flow/testFlow/inputs")) + .andExpect(status().is3xxRedirection()) + .andExpect(result -> assertEquals("/", Objects.requireNonNull(result.getResponse().getRedirectedUrl()))); + mockMvc.perform(get("/flow/otherTestFlow/inputs")) + .andExpect(status().is3xxRedirection()) + .andExpect(result -> assertEquals("/disabledFeature", Objects.requireNonNull(result.getResponse().getRedirectedUrl()))); + } +} diff --git a/src/test/java/formflow/library/interceptors/SpyInterceptorConfig.java b/src/test/java/formflow/library/interceptors/SpyInterceptorConfig.java index 00d276613..17cf67f34 100644 --- a/src/test/java/formflow/library/interceptors/SpyInterceptorConfig.java +++ b/src/test/java/formflow/library/interceptors/SpyInterceptorConfig.java @@ -1,5 +1,6 @@ package formflow.library.interceptors; +import formflow.library.config.DisabledFlowPropertyConfiguration; import formflow.library.config.FlowConfiguration; import java.util.List; import org.mockito.Mockito; @@ -16,6 +17,9 @@ public class SpyInterceptorConfig implements WebMvcConfigurer { @Autowired private List flowConfigurations; + + @Autowired + private DisabledFlowPropertyConfiguration disabledFlowPropertyConfiguration; @Bean @Primary @@ -28,10 +32,17 @@ public LocaleChangeInterceptor localeChangeInterceptor() { public SessionContinuityInterceptor dataRequiredInterceptor() { return Mockito.spy(new SessionContinuityInterceptor(flowConfigurations)); } + + @Bean + @Primary + public DisabledFlowInterceptor disabledFlowInterceptor() { + return Mockito.spy(new DisabledFlowInterceptor(disabledFlowPropertyConfiguration)); + } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()); registry.addInterceptor(dataRequiredInterceptor()); + registry.addInterceptor(disabledFlowInterceptor()); } } diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml index 849ba1de8..2ce9b3c3f 100644 --- a/src/test/resources/application-test.yaml +++ b/src/test/resources/application-test.yaml @@ -1,4 +1,5 @@ form-flow: +# disabled-flows: path: 'test-flow.yaml' inputs: 'formflow.library.inputs.' uploads: