From ebecf219c2f584fbcd8f14b9fb8c3de01df36859 Mon Sep 17 00:00:00 2001 From: Charles Wahome Date: Wed, 15 May 2024 15:45:11 +0300 Subject: [PATCH] Chore: Release 10.1.0 (#3148) * Chore: app migration with feature flags (#3144) --- .github/workflows/linter.yml | 1 + azure-pipelines.yml | 2 + package-lock.json | 64 +++++++++---------- package.json | 2 +- src/app/services/variant-constants.ts | 3 +- src/app/services/variant-service.ts | 34 +++++----- .../authentication/AuthenticationWrapper.ts | 29 +++++++-- src/modules/authentication/msal-app.ts | 31 ++++++++- src/telemetry/event-types.ts | 1 + 9 files changed, 108 insertions(+), 59 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 9d916999d..2268744ee 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -185,6 +185,7 @@ jobs: REACT_APP_NOMINATION_PERIOD: ${{secrets.REACT_APP_NOMINATION_PERIOD}} REACT_APP_COOLDOWN_PERIOD: ${{secrets.REACT_APP_COOLDOWN_PERIOD}} REACT_APP_USAGE_TIME: ${{secrets.REACT_APP_USAGE_TIME}} + REACT_APP_MIGRATION_PARAMETER: ${{secrets.REACT_APP_MIGRATION_PARAMETER}} CI: false id: builddeploy uses: Azure/static-web-apps-deploy@v0.0.1-preview diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 76089c13c..c56392968 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -190,6 +190,7 @@ extends: REACT_APP_NOMINATION_PERIOD: $(REACT_APP_NOMINATION_PERIOD) REACT_APP_COOLDOWN_PERIOD: $(REACT_APP_COOLDOWN_PERIOD) REACT_APP_USAGE_TIME: $(REACT_APP_USAGE_TIME) + REACT_APP_MIGRATION_PARAMETER: $(REACT_APP_MIGRATION_PARAMETER) displayName: "Build static assets for staging" - task: PowerShell@2 @@ -215,6 +216,7 @@ extends: REACT_APP_NOMINATION_PERIOD: $(REACT_APP_NOMINATION_PERIOD) REACT_APP_COOLDOWN_PERIOD: $(REACT_APP_COOLDOWN_PERIOD) REACT_APP_USAGE_TIME: $(REACT_APP_USAGE_TIME) + REACT_APP_MIGRATION_PARAMETER: $(REACT_APP_MIGRATION_PARAMETER) displayName: "Build static assets for prod" - task: PowerShell@2 diff --git a/package-lock.json b/package-lock.json index 9b69e05ca..b7f0e5624 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "graph-explorer-v2", - "version": "10.0.0", + "version": "10.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "graph-explorer-v2", - "version": "10.0.0", + "version": "10.1.0", "dependencies": { "@augloop/types-core": "file:packages/types-core-2.16.189.tgz", "@axe-core/webdriverjs": "4.8.5", @@ -6239,12 +6239,12 @@ } }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dev": true, "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -9892,9 +9892,9 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -10309,7 +10309,7 @@ "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4", - "fs-extra": "11.2.0" + "fs-extra": "^11.2.0" }, "engines": { "node": ">= 14" @@ -14568,9 +14568,9 @@ } }, "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "dependencies": { "debug": "^4.3.4" @@ -15795,9 +15795,9 @@ } }, "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "dependencies": { "debug": "^4.3.4" @@ -17255,9 +17255,9 @@ } }, "node_modules/socks": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", - "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, "dependencies": { "ip-address": "^9.0.5", @@ -17269,12 +17269,12 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", - "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", + "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.1", "debug": "^4.3.4", "socks": "^2.7.1" }, @@ -17283,9 +17283,9 @@ } }, "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "dependencies": { "debug": "^4.3.4" @@ -18431,9 +18431,9 @@ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" }, "node_modules/url/node_modules/qs": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz", - "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", "dependencies": { "side-channel": "^1.0.6" }, @@ -18613,9 +18613,9 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", "dependencies": { "colorette": "^2.0.10", "memfs": "^3.4.3", diff --git a/package.json b/package.json index 29bbf1cd4..65bd1a733 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "graph-explorer-v2", - "version": "10.0.0", + "version": "10.1.0", "private": true, "dependencies": { "@augloop/types-core": "file:packages/types-core-2.16.189.tgz", diff --git a/src/app/services/variant-constants.ts b/src/app/services/variant-constants.ts index 1300b0cbc..7da98a710 100644 --- a/src/app/services/variant-constants.ts +++ b/src/app/services/variant-constants.ts @@ -1 +1,2 @@ -export const ALWAYSSHOWBUTTONS = 'alwaysShowButtons'; \ No newline at end of file +export const ALWAYSSHOWBUTTONS = 'alwaysShowButtons'; +export const SAFEROLLOUTACTIVE = 'safeRolloutActive'; \ No newline at end of file diff --git a/src/app/services/variant-service.ts b/src/app/services/variant-service.ts index 4adf0de47..c3f873db4 100644 --- a/src/app/services/variant-service.ts +++ b/src/app/services/variant-service.ts @@ -1,12 +1,12 @@ /* eslint-disable max-len */ -import { VariantAssignmentRequest } from 'expvariantassignmentsdk/src/interfaces/VariantAssignmentRequest'; -import {VariantAssignmentServiceClient} from 'expvariantassignmentsdk/src/contracts/VariantAssignmentServiceClient'; +import { SeverityLevel } from '@microsoft/applicationinsights-web'; import { VariantAssignmentClientSettings } from 'expvariantassignmentsdk/src/contracts/VariantAssignmentClientSettings'; +import { VariantAssignmentServiceClient } from 'expvariantassignmentsdk/src/contracts/VariantAssignmentServiceClient'; +import { VariantAssignmentRequest } from 'expvariantassignmentsdk/src/interfaces/VariantAssignmentRequest'; + 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; @@ -15,6 +15,7 @@ interface TasResponse { interface Parameters { [key: string]: string | boolean | number; } + class VariantService { private endpoint = EXP_URL; @@ -25,21 +26,18 @@ class VariantService { const settings: VariantAssignmentClientSettings = { endpoint: this.endpoint }; this.createUser(); const request: VariantAssignmentRequest = - { - parameters: this.getParameters() - }; + { + 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); - }); + try { + const response = await client.getVariantAssignments(request); + this.expResponse = response.featureVariables as TasResponse[] | null; + this.assignmentContext = response.assignmentContext; + } catch (error) { + telemetry.trackException(new Error(errorTypes.UNHANDLED_ERROR), SeverityLevel.Error, error as object); + } } public createUser() { @@ -51,7 +49,7 @@ class VariantService { return this.assignmentContext; } - public async getFeatureVariables(namespace: string, flagname: string) { + public getFeatureVariables(namespace: string, flagname: string) { const defaultConfig = this.expResponse?.find(c => c.Id === namespace); return defaultConfig?.Parameters[flagname]; } diff --git a/src/modules/authentication/AuthenticationWrapper.ts b/src/modules/authentication/AuthenticationWrapper.ts index 9d44ebba9..944a9e43d 100644 --- a/src/modules/authentication/AuthenticationWrapper.ts +++ b/src/modules/authentication/AuthenticationWrapper.ts @@ -11,13 +11,15 @@ import { DEFAULT_USER_SCOPES, HOME_ACCOUNT_KEY } from '../../app/services/graph-constants'; -import { signInAuthError } from './authentication-error-hints'; +import { SAFEROLLOUTACTIVE } from '../../app/services/variant-constants'; +import variantService from '../../app/services/variant-service'; import { geLocale } from '../../appLocale'; +import { IQuery } from '../../types/query-runner'; +import { ClaimsChallenge } from './ClaimsChallenge'; import { getCurrentUri } from './authUtils'; +import { signInAuthError } from './authentication-error-hints'; import IAuthenticationWrapper from './interfaces/IAuthenticationWrapper'; import { msalApplication } from './msal-app'; -import { IQuery } from '../../types/query-runner'; -import { ClaimsChallenge } from './ClaimsChallenge'; const defaultScopes = DEFAULT_USER_SCOPES.split(' '); @@ -73,7 +75,7 @@ export class AuthenticationWrapper implements IAuthenticationWrapper { authority: this.getAuthority(), prompt: 'select_account', redirectUri: getCurrentUri(), - extraQueryParameters: { mkt: geLocale } + extraQueryParameters: getExtraQueryParameters() }; try { const result = await msalApplication.loginPopup(popUpRequest); @@ -205,7 +207,7 @@ export class AuthenticationWrapper implements IAuthenticationWrapper { authority: this.getAuthority(), prompt: 'select_account', redirectUri: getCurrentUri(), - extraQueryParameters: { mkt: geLocale }, + extraQueryParameters: getExtraQueryParameters(), claims: this.getClaims() }; @@ -297,3 +299,20 @@ export class AuthenticationWrapper implements IAuthenticationWrapper { window.sessionStorage.clear(); } } + +function getExtraQueryParameters(): { [key: string]: string } { + const params: { [key: string]: string } = { + mkt: geLocale + }; + getSafeRolloutParameter(params); + return params; +} + +function getSafeRolloutParameter(params: { [key: string]: string; }) { + const safeRolloutActive = variantService.getFeatureVariables('default', SAFEROLLOUTACTIVE); + const migrationParam = process.env.REACT_APP_MIGRATION_PARAMETER; + if (safeRolloutActive && migrationParam) { + params.safe_rollout = migrationParam; + } +} + diff --git a/src/modules/authentication/msal-app.ts b/src/modules/authentication/msal-app.ts index 70a31ba26..460ea0900 100644 --- a/src/modules/authentication/msal-app.ts +++ b/src/modules/authentication/msal-app.ts @@ -1,4 +1,5 @@ -import { Configuration, PublicClientApplication } from '@azure/msal-browser'; +import { Configuration, LogLevel, PublicClientApplication } from '@azure/msal-browser'; +import { eventTypes, telemetry } from '../../telemetry'; function getClientIdFromWindow() { return (window as any).ClientId; @@ -19,10 +20,36 @@ export const configuration: Configuration = { cacheLocation: 'localStorage', storeAuthStateInCookie: true, claimsBasedCachingEnabled: true + }, + system: { + loggerOptions: { + logLevel: LogLevel.Verbose, + loggerCallback: (level, message, containsPii) => { + if (containsPii) { + return; + } + telemetry.trackEvent(eventTypes.AUTH_REQUEST_EVENT, { message, level }); + switch (level) { + case LogLevel.Error: + console.error('[MSAL]', message); + return; + case LogLevel.Info: + console.info('[MSAL]', message); + return; + case LogLevel.Verbose: + console.debug('[MSAL]', message); + return; + case LogLevel.Warning: + console.warn('[MSAL]', message); + return; + } + }, + piiLoggingEnabled: false + } } }; const msalApplication = new PublicClientApplication(configuration); msalApplication.initialize(); -export{ msalApplication }; +export { msalApplication }; diff --git a/src/telemetry/event-types.ts b/src/telemetry/event-types.ts index 78e363ee7..6ca0862e1 100644 --- a/src/telemetry/event-types.ts +++ b/src/telemetry/event-types.ts @@ -7,3 +7,4 @@ export const LISTITEM_CLICK_EVENT = 'LISTITEM_CLICK_EVENT'; export const DROPDOWN_CHANGE_EVENT = 'DROPDOWN_CHANGE_EVENT'; export const WINDOW_OPEN_EVENT = 'WINDOW_OPEN_EVENT'; export const KEYBOARD_COPY_EVENT = 'KEYBOARD_COPY_EVENT'; +export const AUTH_REQUEST_EVENT = 'AUTH_REQUEST_EVENT';