diff --git a/src/appstudio_strategy.ts b/src/appstudio_strategy.ts deleted file mode 100644 index 5f2878f..0000000 --- a/src/appstudio_strategy.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Strategy as appstudioStrategy } from "unleash-client"; -import { AppStudioMultiContext } from "./appstudio_context"; - -// The name of the str in the server -const APP_STUDIO_MULTI = "AppStudioMulti"; - -export interface AppStudioMultiParameters { - environments: string; // apps - infrastructures: string; //iaass - landscapes: string; // regions - subaccounts: string; - users: string; - wss: string; - tenantids: string; -} - -export class AppStudioMultiStrategy extends appstudioStrategy { - constructor() { - super(APP_STUDIO_MULTI); // for testing the name is: StrategyMulti - } - - // Check only the strategy parameters. the default strategy is calculated at the server side - isEnabled(parameters: AppStudioMultiParameters, context: AppStudioMultiContext): boolean { - // Check if the user has any restrictions defined in the strategy (defined in the FT server) - // !! returns false for: null,undefined,0,"",false - if (!parameters.environments && !parameters.infrastructures && !parameters.landscapes && !parameters.subaccounts && !parameters.users && !parameters.wss && !parameters.tenantids) { - // No restriction from the strategy side - return true; // strategy returns true. if the enabled button in the FT server is "on" the feature returns enabled - } - - // context.currentEnvironment - The Environment that the extension is running on (calculated in this client) - // parameters.environments // The Environments that the extension is allowed to be enabled on (list is defined on the server) - if (!!context.currentEnvironment && parameters.environments && parameters.environments.indexOf(context.currentEnvironment) !== -1) { - //check that the environment this extension is running on is in the environments list defined in the server - return true; - } - // infrastructures - if (!!context.currentInfrastructure && parameters.infrastructures && parameters.infrastructures.indexOf(context.currentInfrastructure) !== -1) { - return true; - } - // landscapes - if (!!context.currentLandscape && parameters.landscapes && parameters.landscapes.indexOf(context.currentLandscape) !== -1) { - return true; - } - // subaccounts - if (!!context.currentCfSubAccount && parameters.subaccounts && parameters.subaccounts.indexOf(context.currentCfSubAccount) !== -1) { - return true; - } - // users - if (!!context.currentUser && parameters.users && parameters.users.indexOf(context.currentUser) !== -1) { - return true; - } - // wss - if (!!context.currentWs && parameters.wss && parameters.wss.indexOf(context.currentWs) !== -1) { - return true; - } - // tenantids - if (!!context.currentTenantId && parameters.tenantids && parameters.tenantids.indexOf(context.currentTenantId) !== -1) { - return true; - } - - // no matches -> send "disable the feature" to the FT server - return false; - } -} diff --git a/src/strategy/appStudioMultiStrategy.ts b/src/strategy/appStudioMultiStrategy.ts new file mode 100644 index 0000000..01afbab --- /dev/null +++ b/src/strategy/appStudioMultiStrategy.ts @@ -0,0 +1,60 @@ +import { Strategy } from "unleash-client"; +import { AppStudioMultiContext } from "../appstudio_context"; +import { log } from "../logger"; +import { AppStudioStrategy } from "./appStudioStrategies"; + +// The name of the str in the server +const APP_STUDIO_MULTI = "AppStudioMulti"; + +export interface AppStudioMultiParameters { + environments: string; // apps + infrastructures: string; //iaass + landscapes: string; // regions + subaccounts: string; + users: string; + wss: string; + tenantids: string; +} + +export class AppStudioMultiStrategy extends Strategy { + private strategies: AppStudioStrategy[] = []; + + constructor() { + super(APP_STUDIO_MULTI); // for testing the name is: StrategyMulti + } + + public register(appStudioStrategies: AppStudioStrategy[]): void { + appStudioStrategies.forEach((appStudioStrategy) => { + const strategyName = appStudioStrategy.getName(); + const isExist = this.strategies.some((strategy) => strategy.getName() === strategyName); + if (isExist) { + throw new Error(`Strategy ${strategyName} already registered`); + } + + log(`Added strategy: ${strategyName}`); + this.strategies.push(appStudioStrategy); + }); + } + + // Check only the strategy parameters. the default strategy is calculated at the server side + /** Template method */ + isEnabled(parameters: AppStudioMultiParameters, context: AppStudioMultiContext): boolean { + // Check if the user has any restrictions defined in the strategy (defined in the FT server) + const isNotDefined = this.strategies.every((strategy) => !strategy.isDefined(parameters)); + if (isNotDefined) { + // No restriction from the strategy side + return true; + } + + // context.currentEnvironment - The Environment that the extension is running on (calculated in this client) + // parameters.environments // The Environments that the extension is allowed to be enabled on (list is defined on the server) + // Check if strategy meet one of the terms + const isStrategyMet = this.strategies.some((strategy) => strategy.isEnabled(parameters, context)); + if (isStrategyMet) { + return true; + } + + // no matches -> send "disable the feature" to the FT server + return false; + } +} diff --git a/src/strategy/appStudioStrategies.ts b/src/strategy/appStudioStrategies.ts new file mode 100644 index 0000000..88b51d4 --- /dev/null +++ b/src/strategy/appStudioStrategies.ts @@ -0,0 +1,18 @@ +import { AppStudioMultiContext } from "../appstudio_context"; +import { AppStudioMultiParameters, AppStudioMultiStrategy } from "./appStudioMultiStrategy"; +import { EnvironmentsStrategy, InfrastructuresStrategy, LandscapesStrategy, SubAccountsStrategy, TenantIdsStrategy, UsersStrategy, WssStrategy } from "./strategies"; + +export interface AppStudioStrategy { + getName(): string; + + // Check if the user has any restrictions defined in the strategy (defined in the FT server) + isDefined(parameters: AppStudioMultiParameters): boolean; + + // context.currentEnvironment - The Environment that the extension is running on (calculated in this client) + // parameters.environments // The Environments that the extension is allowed to be enabled on (list is defined on the server) + isEnabled(parameters: AppStudioMultiParameters, context: AppStudioMultiContext): boolean; +} + +export function registerStrategies(appStudioMultiStrategy: AppStudioMultiStrategy): void { + appStudioMultiStrategy.register([new EnvironmentsStrategy(), new InfrastructuresStrategy(), new LandscapesStrategy(), new SubAccountsStrategy(), new UsersStrategy(), new WssStrategy(), new TenantIdsStrategy()]); +} diff --git a/src/strategy/strategies.ts b/src/strategy/strategies.ts new file mode 100644 index 0000000..03d3fe8 --- /dev/null +++ b/src/strategy/strategies.ts @@ -0,0 +1,122 @@ +import { AppStudioMultiContext } from "../appstudio_context"; +import { AppStudioStrategy } from "./appStudioStrategies"; +import { AppStudioMultiParameters } from "./appStudioMultiStrategy"; + +export class EnvironmentsStrategy implements AppStudioStrategy { + getName(): string { + return `Environments_Strategy`; + } + + isDefined(parameters: AppStudioMultiParameters): boolean { + return !!parameters.environments; + } + + isEnabled(parameters: AppStudioMultiParameters, context: AppStudioMultiContext): boolean { + if (!!context.currentEnvironment && parameters.environments && parameters.environments.indexOf(context.currentEnvironment) !== -1) { + return true; + } + return false; + } +} + +export class InfrastructuresStrategy implements AppStudioStrategy { + getName(): string { + return `Infrastructures_Strategy`; + } + + isDefined(parameters: AppStudioMultiParameters): boolean { + return !!parameters.infrastructures; + } + + isEnabled(parameters: AppStudioMultiParameters, context: AppStudioMultiContext): boolean { + if (!!context.currentInfrastructure && parameters.infrastructures && parameters.infrastructures.indexOf(context.currentInfrastructure) !== -1) { + return true; + } + return false; + } +} + +export class LandscapesStrategy implements AppStudioStrategy { + getName(): string { + return `Landscapes_Strategy`; + } + + isDefined(parameters: AppStudioMultiParameters): boolean { + return !!parameters.landscapes; + } + + isEnabled(parameters: AppStudioMultiParameters, context: AppStudioMultiContext): boolean { + if (!!context.currentLandscape && parameters.landscapes && parameters.landscapes.indexOf(context.currentLandscape) !== -1) { + return true; + } + return false; + } +} + +export class SubAccountsStrategy implements AppStudioStrategy { + getName(): string { + return `SubAccounts_Strategy`; + } + + isDefined(parameters: AppStudioMultiParameters): boolean { + return !!parameters.subaccounts; + } + + isEnabled(parameters: AppStudioMultiParameters, context: AppStudioMultiContext): boolean { + if (!!context.currentCfSubAccount && parameters.subaccounts && parameters.subaccounts.indexOf(context.currentCfSubAccount) !== -1) { + return true; + } + return false; + } +} + +export class UsersStrategy implements AppStudioStrategy { + getName(): string { + return `Users_Strategy`; + } + + isDefined(parameters: AppStudioMultiParameters): boolean { + return !!parameters.users; + } + + isEnabled(parameters: AppStudioMultiParameters, context: AppStudioMultiContext): boolean { + if (!!context.currentUser && parameters.users && parameters.users.indexOf(context.currentUser) !== -1) { + return true; + } + return false; + } +} + +export class WssStrategy implements AppStudioStrategy { + getName(): string { + return `Wss_Strategy`; + } + + isDefined(parameters: AppStudioMultiParameters): boolean { + return !!parameters.wss; + } + + isEnabled(parameters: AppStudioMultiParameters, context: AppStudioMultiContext): boolean { + if (!!context.currentWs && parameters.wss && parameters.wss.indexOf(context.currentWs) !== -1) { + return true; + } + return false; + } +} + +export class TenantIdsStrategy implements AppStudioStrategy { + getName(): string { + return `TenantIds_Strategy`; + } + + isDefined(parameters: AppStudioMultiParameters): boolean { + return !!parameters.tenantids; + } + + isEnabled(parameters: AppStudioMultiParameters, context: AppStudioMultiContext): boolean { + if (!!context.currentTenantId && parameters.tenantids && parameters.tenantids.indexOf(context.currentTenantId) !== -1) { + return true; + } + return false; + } +} diff --git a/src/unleash_client_manager.ts b/src/unleash_client_manager.ts index d7aec65..82fedbc 100644 --- a/src/unleash_client_manager.ts +++ b/src/unleash_client_manager.ts @@ -1,7 +1,8 @@ import { Unleash } from "unleash-client"; import * as ServerArgs from "./server_arguments"; import { initializeUnleashClient } from "./unleash_client_wrapper"; -import { AppStudioMultiStrategy } from "./appstudio_strategy"; +import { AppStudioMultiStrategy } from "./strategy/appStudioMultiStrategy"; +import { registerStrategies } from "./strategy/appStudioStrategies"; // Map of Unleash clients. // map key = extensionName @@ -13,7 +14,9 @@ async function createNewUnleashClient(extensionName: string, unleashClientMap: M const serverArgs = ServerArgs.getServerArgs(); //init client - const client = await initializeUnleashClient(extensionName, serverArgs, [new AppStudioMultiStrategy()]); + const appStudioMultiStrategy = new AppStudioMultiStrategy(); + const client = await initializeUnleashClient(extensionName, serverArgs, [appStudioMultiStrategy]); + registerStrategies(appStudioMultiStrategy); //add the client to the map unleashClientMap.set(extensionName, client); diff --git a/test/appstudio_strategy.spec.ts b/test/strategy.spec.ts similarity index 94% rename from test/appstudio_strategy.spec.ts rename to test/strategy.spec.ts index db23844..e40c1bd 100644 --- a/test/appstudio_strategy.spec.ts +++ b/test/strategy.spec.ts @@ -1,11 +1,14 @@ import { expect } from "chai"; import * as sinon from "sinon"; import { describe, afterEach, it } from "mocha"; -import * as appstudioStrategy from "../src/appstudio_strategy"; +import * as appstudioStrategy from "../src/strategy/appStudioMultiStrategy"; import * as appstudioContext from "../src/appstudio_context"; +import { registerStrategies } from "../src/strategy/appStudioStrategies"; +import { EnvironmentsStrategy } from "../src/strategy/strategies"; describe("Strategy unit tests", () => { const appStudioMultiStrategy = new appstudioStrategy.AppStudioMultiStrategy(); + registerStrategies(appStudioMultiStrategy); afterEach(() => { sinon.restore(); @@ -19,6 +22,7 @@ describe("Strategy unit tests", () => { subaccounts: null, users: "", wss: "", + tenantids: "", }; const isEnabled = appStudioMultiStrategy.isEnabled(serverParams, null); @@ -275,4 +279,10 @@ describe("Strategy unit tests", () => { expect(isEnabled).to.be.false; }); + + it("register throw error when strategy already in the strategy list", () => { + expect(() => { + appStudioMultiStrategy.register([new EnvironmentsStrategy()]); + }).to.throw(`Strategy Environments_Strategy already registered`); + }); }); diff --git a/test/unleash_client_manager.spec.ts b/test/unleash_client_manager.spec.ts index f235c3f..a910947 100644 --- a/test/unleash_client_manager.spec.ts +++ b/test/unleash_client_manager.spec.ts @@ -6,6 +6,7 @@ import { UnleashConfig } from "unleash-client/lib/unleash"; import * as clientManager from "../src/unleash_client_manager"; import * as serverArgs from "../src/server_arguments"; import * as unleashClientWrapper from "../src/unleash_client_wrapper"; +import * as appStudioStrategies from "../src/strategy/appStudioStrategies"; describe("Test unleash client manager", () => { const extensionNameA = "aaa"; @@ -79,4 +80,15 @@ describe("Test unleash client manager", () => { const err = await clientManager.getUnleashClientFromMap(extensionNameA, unleashClientMap).catch((err) => err.message); expect(err).to.equal("Failed to create Unleash client for extension aaa. Error message: TypeError: client.on is not a function"); }); + + it("registerStrategies is called when creating a new client", async () => { + prepGetUnleashClientTests(); + + const registerSpy = sinon.spy(appStudioStrategies, "registerStrategies"); + + // create a new client + await clientManager.getUnleashClientFromMap(extensionNameA, unleashClientMap); + + expect(registerSpy.callCount).to.equal(1); + }); });