Skip to content

Commit

Permalink
[AC-2806] Add support for conditional routing based on feature flag v…
Browse files Browse the repository at this point in the history
…alue (#9798)

Co-authored-by: Shane Melton <[email protected]>
  • Loading branch information
eliykat and shane-melton authored Jun 26, 2024
1 parent 76a3cb5 commit 794da48
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 8 deletions.
53 changes: 53 additions & 0 deletions libs/angular/src/platform/utils/feature-flagged-route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Type, inject } from "@angular/core";
import { Route, Routes } from "@angular/router";
import { map } from "rxjs";

import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";

import { componentRouteSwap } from "../../utils/component-route-swap";

/**
* @param defaultComponent The component to be used when the feature flag is off.
* @param flaggedComponent The component to be used when the feature flag is on.
* @param featureFlag The feature flag to evaluate
* @param routeOptions The shared route options to apply to both components.
*/
type FeatureFlaggedRouteConfig = {
defaultComponent: Type<any>;
flaggedComponent: Type<any>;
featureFlag: FeatureFlag;
routeOptions: Omit<Route, "component">;
};

/**
* Swap between two routes at runtime based on the value of a feature flag.
* The routes share a common path and configuration but load different components.
* @param config See {@link FeatureFlaggedRouteConfig}
* @returns A tuple containing the conditional configuration for the two routes. This should be unpacked into your existing Routes array.
* @example
* const routes: Routes = [
* ...featureFlaggedRoute({
* defaultComponent: GroupsComponent,
* flaggedComponent: GroupsNewComponent,
* featureFlag: FeatureFlag.GroupsComponentRefactor,
* routeOptions: {
* path: "groups",
* canActivate: [OrganizationPermissionsGuard],
* },
* }),
* ]
*/
export function featureFlaggedRoute(config: FeatureFlaggedRouteConfig): Routes {
const canMatch$ = () =>
inject(ConfigService)
.getFeatureFlag$(config.featureFlag)
.pipe(map((flagValue) => flagValue === true));

return componentRouteSwap(
config.defaultComponent,
config.flaggedComponent,
canMatch$,
config.routeOptions,
);
}
11 changes: 3 additions & 8 deletions libs/angular/src/utils/component-route-swap.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Type } from "@angular/core";
import { Route, Routes } from "@angular/router";
import { CanMatchFn, Route, Routes } from "@angular/router";

/**
* Helper function to swap between two components based on an async condition. The async condition is evaluated
Expand Down Expand Up @@ -32,7 +32,7 @@ import { Route, Routes } from "@angular/router";
export function componentRouteSwap(
defaultComponent: Type<any>,
altComponent: Type<any>,
shouldSwapFn: () => Promise<boolean>,
shouldSwapFn: CanMatchFn,
options: Route,
altOptions?: Route,
): Routes {
Expand All @@ -46,12 +46,7 @@ export function componentRouteSwap(
const altRoute: Route = {
...selectedAltOptions,
component: altComponent,
canMatch: [
async () => {
return await shouldSwapFn();
},
...(selectedAltOptions.canMatch ?? []),
],
canMatch: [shouldSwapFn, ...(selectedAltOptions.canMatch ?? [])],
};

// Return the alternate route first, so it is evaluated first.
Expand Down

0 comments on commit 794da48

Please sign in to comment.