Skip to content

Commit

Permalink
Feature: Add flighting support (#2621)
Browse files Browse the repository at this point in the history
  • Loading branch information
ElinorW authored Jun 29, 2023
1 parent 9f7bcdf commit 0aed99f
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 20 deletions.
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"eslint-plugin-react": "7.32.2",
"eslint-webpack-plugin": "4.0.1",
"express": "4.18.2",
"expvariantassignmentsdk": "file:packages/expvariantassignmentsdk-1.0.0.tgz",
"file-loader": "6.2.0",
"fork-ts-checker-webpack-plugin": "8.0.0",
"fs-extra": "11.1.1",
Expand Down
Binary file added packages/expvariantassignmentsdk-1.0.0.tgz
Binary file not shown.
5 changes: 3 additions & 2 deletions src/app/middleware/localStorageMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { collectionsCache } from '../../modules/cache/collections.cache';
import { resourcesCache } from '../../modules/cache/resources.cache';
import { samplesCache } from '../../modules/cache/samples.cache';
import { saveTheme } from '../../themes/theme-utils';
import { AppAction } from '../../types/action';
import { ResourcePath } from '../../types/resources';
import { addResourcePaths } from '../services/actions/collections-action-creators';
import { CURRENT_THEME } from '../services/graph-constants';
import { getUniquePaths } from '../services/reducers/collections-reducer.util';
import {
CHANGE_THEME_SUCCESS, COLLECTION_CREATE_SUCCESS, FETCH_RESOURCES_ERROR, FETCH_RESOURCES_SUCCESS,
RESOURCEPATHS_ADD_SUCCESS, RESOURCEPATHS_DELETE_SUCCESS, SAMPLES_FETCH_SUCCESS
} from '../services/redux-constants';
import { saveToLocalStorage } from '../utils/local-storage';

const localStorageMiddleware = (store: any) => (next: any) => async (action: AppAction) => {
switch (action.type) {
case CHANGE_THEME_SUCCESS:
saveTheme(action.response);
saveToLocalStorage(CURRENT_THEME,action.response);
break;

case SAMPLES_FETCH_SUCCESS:
Expand Down
2 changes: 2 additions & 0 deletions src/app/services/graph-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ export const REVOKING_PERMISSIONS_REQUIRED_SCOPES = 'DelegatedPermissionGrant.Re
export const ADMIN_CONSENT_DOC_LINK = 'https://learn.microsoft.com/en-us/graph/security-authorization#:~:text=If%20you%27re%20calling%20the%20Microsoft%20Graph%20Security%20API%20from%20Graph%20Explorer'
// eslint-disable-next-line max-len
export const CONSENT_TYPE_DOC_LINK = 'https://learn.microsoft.com/en-us/graph/api/resources/oauth2permissiongrant?view=graph-rest-1.0#:~:text=(eq%20only).-,consentType,-String'
export const CURRENT_THEME='CURRENT_THEME';
export const EXP_URL='https://default.exp-tas.com/exptas76/9b835cbf-9742-40db-84a7-7a323a77f3eb-gedev/api/v1/tas'
1 change: 1 addition & 0 deletions src/app/services/variant-constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const ALWAYSSHOWBUTTONS = 'alwaysShowButtons';
68 changes: 68 additions & 0 deletions src/app/services/variant-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* eslint-disable max-len */
import { VariantAssignmentRequest } from 'expvariantassignmentsdk/src/interfaces/VariantAssignmentRequest';
import {VariantAssignmentServiceClient} from 'expvariantassignmentsdk/src/contracts/VariantAssignmentServiceClient';
import { VariantAssignmentClientSettings } from 'expvariantassignmentsdk/src/contracts/VariantAssignmentClientSettings';
import { errorTypes, telemetry } from '../../telemetry';
import { readFromLocalStorage, saveToLocalStorage } from '../utils/local-storage';
import { EXP_URL } from './graph-constants';
import { SeverityLevel } from '@microsoft/applicationinsights-web';


interface TasResponse {
Id: string;
Parameters: Parameters;
}
interface Parameters {
[key: string]: string | boolean | number;
}
class VariantService {

private endpoint = EXP_URL;
private expResponse: TasResponse[] | null = [];
private assignmentContext: string = '';

public async initialize() {
const settings: VariantAssignmentClientSettings = { endpoint: this.endpoint };
this.createUser();
const request: VariantAssignmentRequest =
{
parameters: this.getParameters()
};

const client = new VariantAssignmentServiceClient(settings);
const response = await client.getVariantAssignments(request);
Promise.resolve(response).then((r) => {
if (r){
this.expResponse = r.featureVariables as TasResponse[] | null;
this.assignmentContext = r.assignmentContext;
}
})
.catch((error) => {
telemetry.trackException(new Error(errorTypes.UNHANDLED_ERROR), SeverityLevel.Error, error);
});
}

public createUser() {
const userid = telemetry.getUserId();
saveToLocalStorage('userid', userid.toString());
}

public getAssignmentContext() {
return this.assignmentContext;
}

public async getFeatureVariables(namespace: string, flagname: string) {
const defaultConfig = this.expResponse?.find(c => c.Id === namespace);
return defaultConfig?.Parameters[flagname];
}

// Parameters will include randomization units (you can have more than one in a single call!)
// and audience filters like market/region, browser, ismsft etc.,
private getParameters(): Map<string, string[]> {
const map: Map<string, string[]> = new Map<string, string[]>();
map.set('userid', [readFromLocalStorage('userid')]);
return map;
}
}

export default new VariantService();
17 changes: 17 additions & 0 deletions src/app/utils/local-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const saveToLocalStorage = (key: string, value: Object|string) => {
if (typeof value === 'string') {
localStorage.setItem(key, value);
} else {
localStorage.setItem(key, JSON.stringify(value));
}
};

export const readFromLocalStorage = (key: string) => {
const value = localStorage.getItem(key);

if (value && typeof value === 'object') {
return JSON.parse(value);
} else{
return value;
}
};
9 changes: 6 additions & 3 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ import './styles/index.scss';
import { telemetry } from './telemetry';
import ITelemetry from './telemetry/ITelemetry';
import { loadGETheme } from './themes';
import { readTheme } from './themes/theme-utils';
import { readFromLocalStorage } from './app/utils/local-storage';
import { IDevxAPI } from './types/devx-api';
import { Mode } from './types/enums';
import { Collection } from './types/resources';
import variantService from './app/services/variant-service';
import { CURRENT_THEME } from './app/services/graph-constants';


const appRoot: HTMLElement = document.getElementById('root')!;
initializeIcons();

let currentTheme = readTheme() || 'light';
let currentTheme = readFromLocalStorage(CURRENT_THEME) || 'light';
export function removeSpinners() {
// removes the loading spinner from GE html after the app is loaded
const spinner = document.getElementById('spinner');
Expand All @@ -54,7 +56,7 @@ export function removeSpinners() {
}

function setCurrentSystemTheme(): void {
const themeFromLocalStorage = readTheme();
const themeFromLocalStorage = readFromLocalStorage(CURRENT_THEME);

if (themeFromLocalStorage) {
currentTheme = themeFromLocalStorage;
Expand Down Expand Up @@ -161,6 +163,7 @@ function loadResources() {
}
loadResources();

variantService.initialize();
const telemetryProvider: ITelemetry = telemetry;
telemetryProvider.initialize();

Expand Down
1 change: 1 addition & 0 deletions src/telemetry/ITelemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export default interface ITelemetry {
severityLevel: SeverityLevel,
properties: {}
): void;
getUserId():string
}
16 changes: 14 additions & 2 deletions src/telemetry/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
getBrowserScreenSize,
getDeviceScreenScale
} from '../app/utils/device-characteristics-telemetry';
import variantService from '../app/services/variant-service';

class Telemetry implements ITelemetry {
private appInsights: ApplicationInsights;
Expand Down Expand Up @@ -66,8 +67,10 @@ class Telemetry implements ITelemetry {
this.appInsights.context.application.ver = getVersion().toString();
}

public trackEvent(name: string, properties: {}) {
this.appInsights.trackEvent({ name, properties });
public trackEvent(name: string, properties:{ AssignmentContext?: string, [key: string]: any } = {}) {
const defaultProperties = { AssignmentContext: variantService.getAssignmentContext() };
const mergedProperties = { ...defaultProperties, ...properties };
this.appInsights.trackEvent({ name, properties: mergedProperties });
}

public trackException(
Expand Down Expand Up @@ -140,6 +143,15 @@ class Telemetry implements ITelemetry {
''
);
}

public getUserId(){
try {
const userId = document.cookie.split(';').filter((item) => item.trim().startsWith('ai_user')).map((item) => item.split('=')[1])[0];
return userId.split('|')[0];
} catch (error) {
return '';
}
}
}

export const telemetry = new Telemetry();
7 changes: 4 additions & 3 deletions src/themes/theme-utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { saveTheme, readTheme } from './theme-utils';
import { CURRENT_THEME } from '../app/services/graph-constants';
import { readFromLocalStorage, saveToLocalStorage } from '../app/utils/local-storage';

describe('Tests theme utils should', () => {
it('save theme to local storage then retrieve the saved theme', () => {
const theme = 'dark';
saveTheme(theme);
expect(readTheme()).toEqual(theme);
saveToLocalStorage(CURRENT_THEME,theme);
expect(readFromLocalStorage(CURRENT_THEME)).toEqual(theme);
})
})
10 changes: 0 additions & 10 deletions src/themes/theme-utils.ts

This file was deleted.

0 comments on commit 0aed99f

Please sign in to comment.