Skip to content

Commit 39df321

Browse files
authored
refactor!: Fluent V9 Migration upgrades (#3758)
1 parent c2898b9 commit 39df321

File tree

169 files changed

+6987
-7509
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

169 files changed

+6987
-7509
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"package.json": "package-lock.json, .npmrc"
2424
},
2525
"cSpell.words": [
26-
"fluentui"
26+
"fluentui",
27+
"noreferrer"
2728
],
2829
}

package-lock.json

Lines changed: 21 additions & 805 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
"@azure/msal-browser": "4.5.1",
99
"@babel/core": "7.26.10",
1010
"@babel/runtime": "7.26.10",
11-
"@fluentui/react": "8.122.9",
11+
"@fluentui-contrib/react-resize-handle": "0.6.1",
1212
"@fluentui/react-components": "9.60.0",
1313
"@fluentui/react-icons": "2.0.274",
14-
"@fluentui/react-icons-mdl2": "1.3.83",
1514
"@microsoft/applicationinsights-react-js": "17.3.4",
1615
"@microsoft/applicationinsights-web": "3.3.6",
1716
"@microsoft/microsoft-graph-client": "3.0.7",
17+
"@microsoft/microsoft-graph-types": "2.40.0",
1818
"@monaco-editor/react": "4.7.0",
1919
"@ms-ofb/officebrowserfeedbacknpm": "file:packages/officebrowserfeedbacknpm-1.6.6.tgz",
2020
"@reduxjs/toolkit": "2.6.0",
@@ -48,7 +48,6 @@
4848
"postcss-flexbugs-fixes": "5.0.2",
4949
"postcss-loader": "8.1.1",
5050
"postcss-preset-env": "10.1.1",
51-
"re-resizable": "6.11.2",
5251
"react": "18.3.0",
5352
"react-app-polyfill": "3.0.0",
5453
"react-dom": "18.3.0",
@@ -69,7 +68,8 @@
6968
"start": "node scripts/start.js",
7069
"build": "node scripts/build.js && node versioned-build.js",
7170
"test": "node scripts/test.js --no-watch --testPathIgnorePatterns=src/tests/ui src/tests/accessibility /scripts/ --max-old-space-size=8192",
72-
"lint": "eslint . \"**/*.{js,ts,tsx}\"",
71+
"lint": "eslint . \"**/*.{js,ts,tsx}\" --quiet",
72+
"lint-fix": "eslint . \"**/*.{js,ts,tsx}\" --quiet --fix",
7373
"prebuild:prod": "standard-version",
7474
"build:prod": "npm run build",
7575
"bump": "standard-version --skip.tag --skip.changelog",

src/app/services/actions/autocomplete-action-creators.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ApplicationState, store } from '../../../../src/store/index';
66
import { fetchAutoCompleteOptions } from '../../../app/services/slices/autocomplete.slice';
77
import { suggestions } from '../../../modules/suggestions/suggestions';
88
import { Mode } from '../../../types/enums';
9+
import { SnippetError } from '../../../types/snippets';
910
import { AUTOCOMPLETE_FETCH_ERROR, AUTOCOMPLETE_FETCH_PENDING, AUTOCOMPLETE_FETCH_SUCCESS } from '../redux-constants';
1011
import { mockThunkMiddleware } from './mockThunkMiddleware';
1112

@@ -61,13 +62,13 @@ const mockState: ApplicationState = {
6162
isLoadingData: false,
6263
response: {
6364
body: undefined,
64-
headers: undefined
65+
headers: {}
6566
}
6667
},
6768
snippets: {
6869
pending: false,
69-
data: [],
70-
error: null
70+
data: {},
71+
error: {} as SnippetError
7172
},
7273
responseAreaExpanded: false,
7374
dimensions: {

src/app/services/actions/permissions-action-creator.spec.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import { authenticationWrapper } from '../../../modules/authentication';
1212
import { ApplicationState, store } from '../../../store/index';
1313
import { Mode } from '../../../types/enums';
14+
import { SnippetError } from '../../../types/snippets';
1415
import { getPermissionsScopeType } from '../../utils/getPermissionsScopeType';
1516
import { translateMessage } from '../../utils/translate-messages';
1617
import { ACCOUNT_TYPE } from '../graph-constants';
@@ -82,13 +83,13 @@ const mockState: ApplicationState = {
8283
isLoadingData: false,
8384
response: {
8485
body: undefined,
85-
headers: undefined
86+
headers: {}
8687
}
8788
},
8889
snippets: {
8990
pending: false,
90-
data: [],
91-
error: null
91+
data: {},
92+
error: {} as SnippetError
9293
},
9394
responseAreaExpanded: false,
9495
dimensions: {
@@ -301,7 +302,7 @@ describe('Permissions action creators', () => {
301302
statusText: translateMessage('Revoking'),
302303
status: translateMessage('Please wait while we revoke this permission'),
303304
ok: false,
304-
messageType: 0
305+
messageBarType: 'info'
305306
}
306307
}
307308
]
@@ -357,7 +358,7 @@ describe('Permissions action creators', () => {
357358
statusText: translateMessage('Revoking '),
358359
status: translateMessage('Please wait while we revoke this permission'),
359360
ok: false,
360-
messageType: 0
361+
messageBarType: 'info'
361362
}
362363
}
363364
]
@@ -416,7 +417,7 @@ describe('Permissions action creators', () => {
416417
statusText: translateMessage('Revoking'),
417418
status: translateMessage('Please wait while we revoke this permission'),
418419
ok: false,
419-
messageType: 0
420+
messageBarType: 'info'
420421
}
421422
}
422423
]

src/app/services/actions/permissions-action-creator.util.ts

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
import { User } from '@microsoft/microsoft-graph-types';
12
import { componentNames, eventTypes, telemetry } from '../../../telemetry';
23
import { IOAuthGrantPayload, IPermissionGrant } from '../../../types/permissions';
34
import { IUser } from '../../../types/profile';
5+
import { CustomBody, ResponseValue } from '../../../types/query-response';
46
import { IQuery } from '../../../types/query-runner';
57
import { RevokeScopesError } from '../../utils/error-utils/RevokeScopesError';
68
import { exponentialFetchRetry } from '../../utils/fetch-retry-handler';
79
import { GRAPH_URL } from '../graph-constants';
8-
import { makeGraphRequest, parseResponse } from './query-action-creator-util';
10+
import { parseResponse, makeGraphRequest as queryMakeGraphRequest } from './query-action-creator-util';
911

1012
interface IPreliminaryChecksObject {
1113
defaultUserScopes: string[];
@@ -107,7 +109,9 @@ export class RevokePermissionsUtil {
107109
const tenantAdminQuery = { ...genericQuery };
108110
tenantAdminQuery.sampleUrl = `${GRAPH_URL}/v1.0/me/memberOf`;
109111
const response = await RevokePermissionsUtil.makeExponentialFetch([], tenantAdminQuery);
110-
return response ? response.value.some((value: any) => value.displayName === 'Global Administrator') : false
112+
const value = (response as CustomBody).value
113+
const isAdmin = value ? value.some((v: Partial<User>)=>v?.displayName === 'Global Administrator') : false
114+
return isAdmin
111115
}
112116

113117
public async getUserPermissionChecks(preliminaryObject: PartialCheckObject): Promise<{
@@ -191,7 +195,7 @@ export class RevokePermissionsUtil {
191195
genericQuery.sampleUrl = `${GRAPH_URL}/v1.0/oauth2PermissionGrants?$filter=clientId eq '${servicePrincipalAppId}'`;
192196
genericQuery.sampleHeaders = [{ name: 'ConsistencyLevel', value: 'eventual' }];
193197
const oAuthGrant = await RevokePermissionsUtil.makeExponentialFetch(scopes, genericQuery);
194-
return oAuthGrant;
198+
return oAuthGrant as IOAuthGrantPayload;
195199
}
196200

197201
public permissionToRevokeInGrant(permissionsGrant: IPermissionGrant, allPrincipalGrant: IPermissionGrant,
@@ -207,7 +211,8 @@ export class RevokePermissionsUtil {
207211
const currentAppId = process.env.REACT_APP_CLIENT_ID;
208212
genericQuery.sampleUrl = `${GRAPH_URL}/v1.0/servicePrincipals?$filter=appId eq '${currentAppId}'`;
209213
const response = await this.makeGraphRequest(scopes, genericQuery);
210-
return response ? response.value[0].id : '';
214+
const value = (response as CustomBody)?.value
215+
return value ? value[0]?.id ?? '' : ''
211216
}
212217

213218
private async revokePermission(permissionGrantId: string, newScopes: string): Promise<boolean> {
@@ -219,32 +224,24 @@ export class RevokePermissionsUtil {
219224
patchQuery.sampleUrl = `${GRAPH_URL}/v1.0/oauth2PermissionGrants/${permissionGrantId}`;
220225
genericQuery.sampleHeaders = [{ name: 'ConsistencyLevel', value: 'eventual' }];
221226
patchQuery.selectedVerb = 'PATCH';
222-
// eslint-disable-next-line no-useless-catch
223-
try {
224-
const response = await RevokePermissionsUtil.makeGraphRequest([], patchQuery);
225-
const { error } = response;
226-
if (error) {
227-
return false;
228-
}
229-
return true;
230-
}
231-
catch (error: any) {
232-
throw error;
227+
228+
const response = await RevokePermissionsUtil.makeGraphRequest([], patchQuery);
229+
const error = (response as CustomBody).error;
230+
if (error) {
231+
return false;
233232
}
233+
return true;
234234
}
235235

236-
private static async makeExponentialFetch(scopes: string[], query: IQuery, condition?:
237-
(args?: any) => Promise<boolean>) {
238-
const respHeaders: any = {};
239-
const response = await exponentialFetchRetry(() => makeGraphRequest(scopes)(query),
240-
8, 100, condition);
241-
return parseResponse(response, respHeaders);
236+
private static async makeExponentialFetch(
237+
scopes: string[], query: IQuery, condition?: (args?: unknown) => Promise<boolean>) {
238+
const response = await exponentialFetchRetry(() => queryMakeGraphRequest(scopes)(query), 8, 100, condition);
239+
return parseResponse(response as Response);
242240
}
243241

244242
private static async makeGraphRequest(scopes: string[], query: IQuery) {
245-
const respHeaders: any = {};
246-
const response = await makeGraphRequest(scopes)(query);
247-
return parseResponse(response, respHeaders);
243+
const response = await queryMakeGraphRequest(scopes)(query);
244+
return parseResponse(response as Response);
248245
}
249246

250247
private trackRevokeConsentEvent = (status: string, permissionObject: any) => {

src/app/services/actions/profile-actions.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,9 @@ export async function getProfileImage(): Promise<string> {
103103

104104
export async function getProfileResponse(): Promise<IProfileResponse> {
105105
const scopes = DEFAULT_USER_SCOPES.split(' ');
106-
const respHeaders: Record<string, string> = {};
107106

108107
const response = await makeGraphRequest(scopes)(query);
109-
const userInfo = await parseResponse(response, respHeaders);
108+
const userInfo = await parseResponse(response as Response);
110109
return {
111110
userInfo,
112111
response

src/app/services/actions/query-action-creator-util.ts

Lines changed: 40 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ import {
1010

1111
import { authenticationWrapper } from '../../../modules/authentication';
1212
import { ApplicationState } from '../../../store';
13-
import { ContentType } from '../../../types/enums';
13+
import { ResponseBody } from '../../../types/query-response';
1414
import { IQuery } from '../../../types/query-runner';
1515
import { IRequestOptions } from '../../../types/request';
1616
import { IStatus } from '../../../types/status';
1717
import { ClientError } from '../../utils/error-utils/ClientError';
18+
import { getHeaders } from '../../utils/http-methods.utils';
1819
import { encodeHashCharacters } from '../../utils/query-url-sanitization';
1920
import { translateMessage } from '../../utils/translate-messages';
2021
import { authProvider, GraphClient } from '../graph-client';
@@ -36,7 +37,7 @@ export async function anonymousRequest(
3637
export function createAnonymousRequest(query: IQuery, proxyUrl: string, queryRunnerStatus: IStatus) {
3738
const escapedUrl = encodeURIComponent(query.sampleUrl);
3839
const graphUrl = `${proxyUrl}?url=${escapedUrl}`;
39-
const sampleHeaders: any = {};
40+
const sampleHeaders: Record<string, string> = {};
4041

4142
if (query.sampleHeaders && query.sampleHeaders.length > 0) {
4243
query.sampleHeaders.forEach((header) => {
@@ -106,30 +107,30 @@ function createAuthenticatedRequest(
106107

107108
export function makeGraphRequest(scopes: string[]) {
108109
return async (query: IQuery) => {
109-
let response;
110+
let response: ResponseBody;
110111

111112
const graphRequest: GraphRequest = createAuthenticatedRequest(scopes, query);
112113

113114
switch (query.selectedVerb) {
114115
case 'GET':
115-
response = await graphRequest.get();
116+
response = await graphRequest.get() as ResponseBody;
116117
break;
117118
case 'POST':
118-
response = await graphRequest.post(query.sampleBody);
119+
response = await graphRequest.post(query.sampleBody) as ResponseBody;
119120
break;
120121
case 'PUT':
121-
response = await graphRequest.put(query.sampleBody);
122+
response = await graphRequest.put(query.sampleBody) as ResponseBody;
122123
break;
123124
case 'PATCH':
124-
response = await graphRequest.patch(query.sampleBody);
125+
response = await graphRequest.patch(query.sampleBody) as ResponseBody;
125126
break;
126127
case 'DELETE':
127-
response = await graphRequest.delete();
128+
response = await graphRequest.delete() as ResponseBody;
128129
break;
129130
default:
130131
return;
131132
}
132-
return Promise.resolve(response);
133+
return Promise.resolve(response) as ResponseBody;
133134
};
134135
}
135136

@@ -146,32 +147,26 @@ export function isBetaURLResponse(json: any) {
146147
return !!json?.account?.[0]?.source?.type?.[0];
147148
}
148149

149-
export function getContentType(headers: any) {
150-
let contentType = null;
151-
152-
if (headers) {
153-
let contentTypes = headers['content-type'];
154-
if (headers instanceof Headers) {
155-
contentTypes = headers.get('content-type');
156-
}
157-
if (contentTypes) {
158-
/* Example: application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
159-
* Take the first option after splitting since it is the only value useful in the description of the content
160-
*/
161-
const splitContentTypes = contentTypes.split(';');
162-
if (splitContentTypes.length > 0) {
163-
contentType = splitContentTypes[0].toLowerCase();
164-
}
165-
}
150+
export function getContentType(headers: Record<string, string>): string {
151+
const contentTypeHeader = Object.keys(headers).find(header => header.toLowerCase() === 'content-type');
152+
let contentType = contentTypeHeader ? headers[contentTypeHeader] : '';
153+
/* Example: application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
154+
* Take the first option after splitting since it is the only value useful in the description of the content
155+
*/
156+
if (contentType) {
157+
const splitContentTypes = contentType.split(';');
158+
contentType = (splitContentTypes.length > 0) ? splitContentTypes[0].toLowerCase() : contentType;
166159
}
167-
return contentType;
160+
return contentType.toLowerCase();
168161
}
169162

170-
export function isFileResponse(headers: any) {
171-
const contentDisposition = headers['content-disposition'];
163+
export function isFileResponse(headers: Record<string, string>) {
164+
const contentDisposition: string | null = (headers instanceof Headers) ?
165+
headers.get('content-disposition') : headers['content-disposition'];
166+
172167
if (contentDisposition) {
173168
const directives = contentDisposition.split(';');
174-
if (directives.contains('attachment')) {
169+
if (directives.includes('attachment')) {
175170
return true;
176171
}
177172
}
@@ -191,48 +186,40 @@ export function isFileResponse(headers: any) {
191186
return false;
192187
}
193188

194-
export async function generateResponseDownloadUrl(
195-
response: Response,
196-
respHeaders: any
197-
) {
189+
export async function generateResponseDownloadUrl(response: Response) {
190+
const headers = getHeaders(response)
198191
try {
199-
const fileContents = await parseResponse(response, respHeaders);
200-
const contentType = getContentType(respHeaders);
192+
const fileContents = await parseResponse(response);
193+
const contentType = getContentType(headers);
201194
if (fileContents) {
202195
const buffer = await response.arrayBuffer();
203196
const blob = new Blob([buffer], { type: contentType });
204197
return URL.createObjectURL(blob);
205198
}
206-
} catch (error) {
199+
} catch {
207200
return null;
208201
}
209202
}
210203

211204
async function tryParseJson(textValue: string) {
212205
try {
213206
return JSON.parse(textValue);
214-
} catch (error) {
207+
} catch {
215208
return textValue;
216209
}
217210
}
218211

219-
export function parseResponse(
220-
response: Response,
221-
respHeaders: { [key: string]: string } = {}
222-
): Promise<any> {
223-
if (response && response.headers) {
224-
response.headers.forEach((val: string, key: string) => {
225-
respHeaders[key] = val;
226-
});
227-
228-
const contentType = getContentType(response.headers);
212+
export const parseResponse = (response: ResponseBody): Promise<ResponseBody> => {
213+
if (response instanceof Response && response.headers) {
214+
const headers = getHeaders(response)
215+
const contentType = getContentType(headers);
229216
switch (contentType) {
230-
case ContentType.Json:
217+
case 'application/json':
231218
return response.text().then(tryParseJson);
232-
case ContentType.XML:
233-
case ContentType.HTML:
234-
case ContentType.TextCsv:
235-
case ContentType.TextPlain:
219+
case 'application/xml':
220+
case 'text/html':
221+
case 'text/csv':
222+
case 'text/plain':
236223
return response.text();
237224

238225
default:

0 commit comments

Comments
 (0)