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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ To run the feature-toggle-node **locally**, you need to provide the following en
Environment variables example:

```
"FT_SERVER_ENDPOINT": "http://unleash.herokuapp.com",
"WS_BASE_URL": "https://cfsubaccount-workspaces-ws-id.region.applicationstudio.cloud.sap",
"FT_SERVER_ENDPOINT": "http://unleash.herokuapp.com",
"USER_NAME": "user@hotmail.com",
"FT_CLIENT_REFRESH_INTERVAL": "6s",
"TENANT_ID" : "b5c05535-9495-4050-9d68-4356d0d34136",
"TENANT_NAME": "cfsubaccount" // subaccount,
"WORKSPACE_ID": "workspaces-ws-x66m6",
"SHOW_LOG": "true",
```

Expand Down
47 changes: 14 additions & 33 deletions src/appstudio_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ import { Context as appstudioContext } from "unleash-client/lib/context";
import { getEnv } from "./utils";

const USER_NAME = "USER_NAME";
const WS_BASE_URL = "WS_BASE_URL";
const LANDSCAPE_ENVIRONMENT = "LANDSCAPE_ENVIRONMENT";
const LANDSCAPE_NAME = "LANDSCAPE_NAME";
const TENANT_ID = "TENANT_ID";

const fullFormatWsBaseUrl = "Expected format: https://<CF sub account>-workspaces-ws-<id>.<cluster region>.<domain>/";
const SUB_ACCOUNT = "TENANT_NAME";
const WORKSPACE_ID = "WORKSPACE_ID";

export interface AppStudioMultiContext extends appstudioContext {
currentEnvironment: string; //app
Expand All @@ -19,52 +18,34 @@ export interface AppStudioMultiContext extends appstudioContext {
currentTenantId: string;
}

function extractCfSubAccountAndWs(wsBaseUrlString: string, context: AppStudioMultiContext): void {
// Example - sub account with hyphen
// https://consumer-trial-workspaces-ws-9gzgq.eu10.trial.applicationstudio.cloud.sap/
// consumer-trial: the cf sub account
// ws-9gzgq: the WS

const regex = new RegExp("^(https:)\\S*(-workspaces-ws-)\\S*", "g");
if (!regex.test(wsBaseUrlString)) {
throw new Error(`Feature toggle env WS_BASE_URL is NOT in the correct format. ${fullFormatWsBaseUrl}`);
}

// make wsBaseUrlString: [consumer-trial, ws-9gzgq.eu10.trial.applicationstudio.cloud.sap/]
const splitByWorkspaces = wsBaseUrlString.substr(8).split("-workspaces-");
context.currentCfSubAccount = splitByWorkspaces[0]; // sub account: "consumer-trial"

// turn ws-9gzgq.eu10.trial.applicationstudio.cloud.sap/ to [ws-9gzgq, eu10,trial, ...]
const splitDotArray = splitByWorkspaces[1].split(".");

context.currentWs = splitDotArray[0]; // workspace: ws-n8vmz
function getEnvWithNotFoundError(envVar: string): string {
return getEnv(envVar, `Feature toggle env ${envVar} was NOT found in the environment variables`);
}

export function createContextObject(): AppStudioMultiContext {
// get the user name from the env
const userName = getEnv(USER_NAME, "Feature toggle env USER_NAME was NOT found in the environment variables");
const userName = getEnvWithNotFoundError(USER_NAME);
// get the environment from the env
const environment = getEnv(LANDSCAPE_ENVIRONMENT, "Feature toggle env LANDSCAPE_ENVIRONMENT was NOT found in the environment variables");
const environment = getEnvWithNotFoundError(LANDSCAPE_ENVIRONMENT);
// get the landscape from the env
const landscape = getEnv(LANDSCAPE_NAME, "Feature toggle env LANDSCAPE_NAME was NOT found in the environment variables");
const landscape = getEnvWithNotFoundError(LANDSCAPE_NAME);
// get the tenant id from the env
const tenantId = getEnvWithNotFoundError(TENANT_ID);
// get the tenant id from the env
const tenantId = getEnv(TENANT_ID, "Feature toggle env TENANT_ID was NOT found in the environment variables");
const subAccount = getEnvWithNotFoundError(SUB_ACCOUNT);
// get WORKSPACE_ID env
const ws = getEnvWithNotFoundError(WORKSPACE_ID);

// Create the context
const context: AppStudioMultiContext = {
currentEnvironment: environment,
currentInfrastructure: "",
currentLandscape: landscape,
currentCfSubAccount: "", // will be added in the next function
currentCfSubAccount: subAccount,
currentUser: userName,
currentWs: "", // will be added in the next function
currentWs: ws,
currentTenantId: tenantId,
};

// get the WS and SubAccount from WS_BASE_URL env
const wsBaseUrlString = getEnv(WS_BASE_URL, "Feature toggle env WS_BASE_URL was NOT found in the environment variables");
// Extract the WS and cluster and save in the context
extractCfSubAccountAndWs(wsBaseUrlString, context);

return context;
}
210 changes: 89 additions & 121 deletions test/context_manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ describe("Test context manager", () => {
const extensionNameA = "aaa";
const extensionNameB = "bbb";

const USER = "koko@sap.com";
const ENVIRONMENT = "prod";
const LANDSCAPE = "eu10";
const TENANTID = "111-a11-111";
const user = "koko@sap.com";
Copy link
Member

Choose a reason for hiding this comment

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

out of curiosity, why did you change it to small letters?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

because it is used in the test (parameter) and not as const

const env = "prod";
const landscape = "eu10";
const tenantId = "111-a11-111";
const subAccount = "azureconseu";
const ws = "workspaces-ws-n8vmz";
let contextMap;

afterEach(() => {
Expand All @@ -20,151 +22,117 @@ describe("Test context manager", () => {

beforeEach(() => {
sinon.stub(process, "env").value({
WS_BASE_URL: "https://azureconseu-workspaces-ws-n8vmz.eu20.applicationstudio.cloud.sap/",
USER_NAME: USER,
LANDSCAPE_ENVIRONMENT: ENVIRONMENT,
LANDSCAPE_NAME: LANDSCAPE,
TENANT_ID: TENANTID,
USER_NAME: user,
LANDSCAPE_ENVIRONMENT: env,
LANDSCAPE_NAME: landscape,
TENANT_ID: tenantId,
TENANT_NAME: subAccount,
WORKSPACE_ID: ws,
});

contextMap = new Map<string, AppStudioMultiContext>();
});

function testNoEnvError(extensionName: string, contextMap: Map<string, AppStudioMultiContext>, errorMessage: string): void {
expect(() => {
contextManager.getContextFromMap(extensionName, contextMap);
}).to.throw(errorMessage);
}
describe("getContext cache", () => {
it("return the same instance from cache", () => {
// create context - NOT in cache -> create a new one
const contextACreated = contextManager.getContext(extensionNameA);
// create context - in cache -> only get from cache
const contextAFromCache = contextManager.getContext(extensionNameA);

it("Test getContext - positive flow", () => {
// create context - NOT in cache -> create a new one
const contextACreated = contextManager.getContext(extensionNameA);
// create context - in cache -> only get from cache
const contextAFromCache = contextManager.getContext(extensionNameA);

// same instance, ref compare
expect(contextACreated === contextAFromCache).to.be.true;
});

it("Test getContext - not fetching from cache when when using different extensions names", () => {
// create context - NOT in cache -> create a new one
const contextACreated = contextManager.getContextFromMap(extensionNameA, contextMap);
// create context - NOT in cache -> create a new one
const contextBCreated = contextManager.getContextFromMap(extensionNameB, contextMap);

// NOT same instance, ref compare
expect(contextACreated === contextBCreated).to.be.false;
});

it("Test context - getContext - getting the expected context values - positive flow", () => {
//WS_BASE_URL: "https://azureconseu-workspaces-ws-n8vmz.eu20.applicationstudio.cloud.sap/",

const expectedContext: AppStudioMultiContext = {
currentEnvironment: ENVIRONMENT,
currentInfrastructure: "",
currentLandscape: LANDSCAPE,
currentCfSubAccount: "azureconseu",
currentUser: USER,
currentWs: "ws-n8vmz",
currentTenantId: TENANTID,
};

// create context - NOT in cache -> create a new one
const contextA: AppStudioMultiContext = contextManager.getContextFromMap(extensionNameA, contextMap);

expect(contextA).to.deep.equal(expectedContext);
});

it("Test context - getContext - getting the expected context values - positive flow", () => {
sinon.stub(process, "env").value({
WS_BASE_URL: "https://consumer-trial-workspaces-ws-9gzgq.eu10.trial.applicationstudio.cloud.sap/",
USER_NAME: USER,
LANDSCAPE_ENVIRONMENT: ENVIRONMENT,
LANDSCAPE_NAME: LANDSCAPE,
TENANT_ID: TENANTID,
// same instance, ref compare
expect(contextACreated === contextAFromCache).to.be.true;
});

const expectedContext: AppStudioMultiContext = {
currentEnvironment: ENVIRONMENT,
currentInfrastructure: "",
currentLandscape: LANDSCAPE,
currentCfSubAccount: "consumer-trial",
currentUser: USER,
currentWs: "ws-9gzgq",
currentTenantId: TENANTID,
};

// create context - NOT in cache -> create a new one
const contextA = contextManager.getContextFromMap(extensionNameA, contextMap);
it("return different instances from cache when when using different extensions names", () => {
// create context - NOT in cache -> create a new one
const contextACreated = contextManager.getContextFromMap(extensionNameA, contextMap);
// create context - NOT in cache -> create a new one
const contextBCreated = contextManager.getContextFromMap(extensionNameB, contextMap);

expect(contextA).to.deep.equal(expectedContext);
// NOT same instance, ref compare
expect(contextACreated === contextBCreated).to.be.false;
});
});

it("Test context - getContext - getting error for lack of '-workspaces-ws-' in WS_BASE_URL - negative flow", () => {
sinon.stub(process, "env").value({
WS_BASE_URL: "https://azureconseun8vmz.eu20",
USER_NAME: USER,
LANDSCAPE_ENVIRONMENT: ENVIRONMENT,
LANDSCAPE_NAME: LANDSCAPE,
TENANT_ID: TENANTID,
describe("getContextFromMap", () => {
function testNoEnvError(extensionName: string, contextMap: Map<string, AppStudioMultiContext>, errorMessage: string): void {
expect(() => {
contextManager.getContextFromMap(extensionName, contextMap);
}).to.throw(errorMessage);
}

it("getting the expected context values", () => {
const expectedContext: AppStudioMultiContext = {
currentEnvironment: env,
currentInfrastructure: "",
currentLandscape: landscape,
currentCfSubAccount: subAccount,
currentUser: user,
currentWs: ws,
currentTenantId: tenantId,
};

// create context - NOT in cache -> create a new one
const contextA: AppStudioMultiContext = contextManager.getContextFromMap(extensionNameA, contextMap);

expect(contextA).to.deep.equal(expectedContext);
});

testNoEnvError(extensionNameA, contextMap, `Feature toggle env WS_BASE_URL is NOT in the correct format. Expected format: https://<CF sub account>-workspaces-ws-<id>.<cluster region>.<domain>/`);
});
it("getting error for no WORKSPACE_ID environment variable", () => {
sinon.stub(process, "env").value({
USER_NAME: user,
LANDSCAPE_ENVIRONMENT: env,
LANDSCAPE_NAME: landscape,
TENANT_ID: tenantId,
TENANT_NAME: subAccount,
});

it("Test context - getContext - getting error for when using http instead of https in WS_BASE_URL - negative flow", () => {
sinon.stub(process, "env").value({
WS_BASE_URL: "http://azureconseu-workspaces-ws-n8vmz.eu20.applicationstudio.cloud.sap/",
USER_NAME: USER,
LANDSCAPE_ENVIRONMENT: ENVIRONMENT,
LANDSCAPE_NAME: LANDSCAPE,
TENANT_ID: TENANTID,
testNoEnvError(extensionNameA, contextMap, "Feature toggle env WORKSPACE_ID was NOT found in the environment variables");
});

testNoEnvError(extensionNameA, contextMap, `Feature toggle env WS_BASE_URL is NOT in the correct format. Expected format: https://<CF sub account>-workspaces-ws-<id>.<cluster region>.<domain>/`);
});
it("getting error for no TENANT_NAME environment variable", () => {
sinon.stub(process, "env").value({
USER_NAME: user,
LANDSCAPE_ENVIRONMENT: env,
LANDSCAPE_NAME: landscape,
TENANT_ID: tenantId,
});

it("Test context - getContext - getting error for no WS_BASE_URL env parameter - negative flow", () => {
sinon.stub(process, "env").value({
USER_NAME: USER,
LANDSCAPE_ENVIRONMENT: ENVIRONMENT,
LANDSCAPE_NAME: LANDSCAPE,
TENANT_ID: TENANTID,
testNoEnvError(extensionNameA, contextMap, "Feature toggle env TENANT_NAME was NOT found in the environment variables");
});

testNoEnvError(extensionNameA, contextMap, "Feature toggle env WS_BASE_URL was NOT found in the environment variables");
});
it("getting error for no USER_NAME environment variable", () => {
sinon.stub(process, "env").value({});

it("Test context - getContext - getting error for no USER_NAME env parameter - negative flow", () => {
sinon.stub(process, "env").value({});
testNoEnvError(extensionNameA, contextMap, "Feature toggle env USER_NAME was NOT found in the environment variables");
});

testNoEnvError(extensionNameA, contextMap, "Feature toggle env USER_NAME was NOT found in the environment variables");
});
it("getting error for no LANDSCAPE_ENVIRONMENT environment variable", () => {
sinon.stub(process, "env").value({
USER_NAME: user,
});

it("Test context - getContext - getting error for no LANDSCAPE_ENVIRONMENT env parameter - negative flow", () => {
sinon.stub(process, "env").value({
USER_NAME: USER,
testNoEnvError(extensionNameA, contextMap, "Feature toggle env LANDSCAPE_ENVIRONMENT was NOT found in the environment variables");
});

testNoEnvError(extensionNameA, contextMap, "Feature toggle env LANDSCAPE_ENVIRONMENT was NOT found in the environment variables");
});
it("getting error for no TENANT_ID environment variable", () => {
sinon.stub(process, "env").value({
USER_NAME: user,
LANDSCAPE_ENVIRONMENT: env,
LANDSCAPE_NAME: landscape,
});

it("Test context - getContext - getting error for no LANDSCAPE_NAME env parameter - negative flow", () => {
sinon.stub(process, "env").value({
USER_NAME: USER,
LANDSCAPE_ENVIRONMENT: ENVIRONMENT,
testNoEnvError(extensionNameA, contextMap, "Feature toggle env TENANT_ID was NOT found in the environment variables");
});

testNoEnvError(extensionNameA, contextMap, "Feature toggle env LANDSCAPE_NAME was NOT found in the environment variables");
});
it("getting error for no LANDSCAPE_NAME environment variable", () => {
sinon.stub(process, "env").value({
USER_NAME: user,
LANDSCAPE_ENVIRONMENT: env,
});

it("Test context - getContext - getting error for no TENANT_ID env parameter - negative flow", () => {
sinon.stub(process, "env").value({
USER_NAME: USER,
LANDSCAPE_ENVIRONMENT: ENVIRONMENT,
LANDSCAPE_NAME: LANDSCAPE,
testNoEnvError(extensionNameA, contextMap, "Feature toggle env LANDSCAPE_NAME was NOT found in the environment variables");
});

testNoEnvError(extensionNameA, contextMap, "Feature toggle env TENANT_ID was NOT found in the environment variables");
});
});