Skip to content

Commit

Permalink
Merge branch 'dev' into feat/fluent-v9-upgrade
Browse files Browse the repository at this point in the history
  • Loading branch information
musale committed Dec 9, 2024
2 parents 1f9d50a + ffd05dd commit 6b56ffa
Show file tree
Hide file tree
Showing 52 changed files with 1,498 additions and 494 deletions.
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "10.4.1"
".": "10.5.0"
}
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [10.5.0](https://github.com/microsoftgraph/microsoft-graph-explorer-v4/compare/v10.4.1...v10.5.0) (2024-12-02)


### Features

* add a notification banner to display for first time users ([#3396](https://github.com/microsoftgraph/microsoft-graph-explorer-v4/issues/3396)) ([79e3b85](https://github.com/microsoftgraph/microsoft-graph-explorer-v4/commit/79e3b85083d305cbe2b8f851c48b3069aa180bdb))
* API Permissions and Collections redesign ([#3391](https://github.com/microsoftgraph/microsoft-graph-explorer-v4/issues/3391)) ([d239f8f](https://github.com/microsoftgraph/microsoft-graph-explorer-v4/commit/d239f8fd53c2a937ef5d4c15a8e0ffd505c664f1))


### Bug Fixes

* select only the required values from state and useEffect to get description error message ([#3395](https://github.com/microsoftgraph/microsoft-graph-explorer-v4/issues/3395)) ([9141255](https://github.com/microsoftgraph/microsoft-graph-explorer-v4/commit/914125564325f74f5e739835cfdf4afb353188fd))

## [10.4.1](https://github.com/microsoftgraph/microsoft-graph-explorer-v4/compare/v10.4.0...v10.4.1) (2024-10-07)


Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
{
"name": "graph-explorer-v2",
"version": "10.4.1",
"version": "10.5.0",
"private": true,
"dependencies": {
"@augloop/types-core": "file:packages/types-core-2.16.189.tgz",
"@axe-core/webdriverjs": "4.10.0",
"@fluentui/react-components": "9.55.1",
"@fluentui/react-icons": "2.0.264",
"@azure/msal-browser": "3.26.1",
"@babel/core": "7.26.0",
"@babel/runtime": "7.26.0",
Expand Down
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ sonar.projectKey=microsoftgraph_microsoft-graph-explorer-v4
sonar.organization=microsoftgraph2
sonar.projectName=microsoft-graph-explorer-v4
// x-release-please-start-version
sonar.projectVersion=10.4.1
sonar.projectVersion=10.5.0
// x-release-please-end
sonar.host.url=https://sonarcloud.io

Expand Down
3 changes: 2 additions & 1 deletion src/app/middleware/localStorageMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { CURRENT_THEME } from '../services/graph-constants';
import { getUniquePaths } from '../services/reducers/collections-reducer.util';
import {
CHANGE_THEME_SUCCESS, COLLECTION_CREATE_SUCCESS,
RESOURCEPATHS_ADD_SUCCESS, RESOURCEPATHS_DELETE_SUCCESS, SAMPLES_FETCH_SUCCESS
RESOURCEPATHS_ADD_SUCCESS, RESOURCEPATHS_DELETE_SUCCESS,
SAMPLES_FETCH_SUCCESS
} from '../services/redux-constants';
import { saveToLocalStorage } from '../utils/local-storage';

Expand Down
10 changes: 8 additions & 2 deletions src/app/services/actions/autocomplete-action-creators.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,20 @@ const mockState: ApplicationState = {
permissions: [],
error: null
},
collections: [],
collections: {
collections: [],
saved: false
},
proxyUrl: ''
}

store.getState = () => ({
...mockState,
proxyUrl: '',
collections: [],
collections: {
collections: [],
saved: false
},
graphExplorerMode: Mode.Complete,
queryRunnerStatus: null,
samples: {
Expand Down
5 changes: 4 additions & 1 deletion src/app/services/actions/permissions-action-creator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ const mockState: ApplicationState = {
permissions: [],
error: null
},
collections: [],
collections: {
collections: [],
saved: false
},
proxyUrl: ''
}
const currentState = store.getState();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ const mockState: ApplicationState = {
data: {},
error: null
},
collections: [],
collections: {
collections: [],
saved: false
},
proxyUrl: ''
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createContext } from 'react';

import { CollectionPermission, ResourcePath } from '../../../../types/resources';

interface CollectionPermissionsContext {
getPermissions: (paths: ResourcePath[]) => Promise<void>;
permissions?: { [key: string]: CollectionPermission[] };
isFetching?: boolean;
}

// eslint-disable-next-line @typescript-eslint/no-redeclare
export const CollectionPermissionsContext = createContext<CollectionPermissionsContext>(
{} as CollectionPermissionsContext
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { ReactNode, useMemo, useState } from 'react';

import { CollectionPermission, Method, ResourcePath } from '../../../../types/resources';
import {
getScopesFromPaths, getVersionsFromPaths, scopeOptions
} from '../../../views/sidebar/resource-explorer/collection/collection.util';
import { CollectionPermissionsContext } from './CollectionPermissionsContext';
import { useAppSelector } from '../../../../store';

interface CollectionRequest {
method: Method;
requestUrl: string;
}

function getRequestsFromPaths(paths: ResourcePath[], version: string, scope: string) {
const requests: CollectionRequest[] = [];
paths.forEach(path => {
const { method, url } = path;
const pathScope = path.scope ?? scopeOptions[0].key;
if (version === path.version && scope === pathScope) {
requests.push({
method: method as Method,
requestUrl: url
});
}
});
return requests;
}

async function getCollectionPermissions(permissionsUrl: string, paths: ResourcePath[]):
Promise<{ [key: string]: CollectionPermission[] }> {
const versions = getVersionsFromPaths(paths);
const scopes = getScopesFromPaths(paths);
const collectionPermissions: { [key: string]: CollectionPermission[] } = {};

for (const version of versions) {
for (const scope of scopes) {
const requestPaths = getRequestsFromPaths(paths, version, scope);
if (requestPaths.length === 0) {
continue;
}
const url = `${permissionsUrl}?version=${version}&scopeType=${scope}`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestPaths)
});
const perms = await response.json();
collectionPermissions[`${version}-${scope}`] = (perms.results) ? perms.results : [];
}
}
return collectionPermissions;
}

const CollectionPermissionsProvider = ({ children }: { children: ReactNode }) => {
const { baseUrl } = useAppSelector((state) => state.devxApi);
const [permissions, setPermissions] = useState<{ [key: string]: CollectionPermission[] } | undefined>(undefined);
const [isFetching, setIsFetching] = useState(false);
const [code, setCode] = useState('');

const getPermissions = async (items: ResourcePath[]): Promise<void> => {
const hashCode = window.btoa(JSON.stringify([...items]));
if (hashCode !== code) {
try {
setIsFetching(true);
const perms = await getCollectionPermissions(`${baseUrl}/permissions`, items);
setPermissions(perms);
setCode(hashCode);
} catch (error) {
setPermissions(undefined);
} finally {
setIsFetching(false);
}
}
};

const contextValue = useMemo(
() => ({ getPermissions, permissions, isFetching }),
[getPermissions, permissions, isFetching]
);

return (
<CollectionPermissionsContext.Provider value={contextValue}>
{children}
</CollectionPermissionsContext.Provider>
);
};

export default CollectionPermissionsProvider;
3 changes: 2 additions & 1 deletion src/app/services/graph-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ export const ADMIN_CONSENT_DOC_LINK = 'https://learn.microsoft.com/en-us/graph/s
// 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'
export const EXP_URL='https://default.exp-tas.com/exptas76/9b835cbf-9742-40db-84a7-7a323a77f3eb-gedev/api/v1/tas'
export const BANNER_IS_VISIBLE = 'bannerIsVisible';
7 changes: 7 additions & 0 deletions src/app/services/hooks/useCollectionPermissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useContext } from 'react';

import { CollectionPermissionsContext } from '../context/collection-permissions/CollectionPermissionsContext';

export const useCollectionPermissions = () => {
return useContext(CollectionPermissionsContext);
};
1 change: 1 addition & 0 deletions src/app/services/redux-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,4 @@ export const REVOKE_SCOPES_PENDING = 'auth/revokeScopes/pending';
export const REVOKE_SCOPES_SUCCESS = 'auth/revokeScopes/fulfilled';
export const REVOKE_SCOPES_ERROR = 'auth/revokeScopes/rejected';
export const COLLECTION_CREATE_SUCCESS = 'collections/createCollection';
export const SET_BANNER_STATE = 'banner/setBannerState';
64 changes: 46 additions & 18 deletions src/app/services/slices/collections.slice.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,66 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { Collection, ResourcePath } from '../../../types/resources';

const initialState: Collection[] = [];
interface CollectionsState {
collections: Collection[];
saved: boolean;
}

const initialState: CollectionsState = {
collections: [],
saved: false
};

const collections = createSlice({
name: 'collections',
initialState,
reducers: {
createCollection: (state, action: PayloadAction<Collection>) => {
state.push(action.payload);
return state
state.collections.push(action.payload);
state.saved = false;
},
addResourcePaths:(state, action: PayloadAction<ResourcePath[]>) => {
const index = state.findIndex(collection => collection.isDefault);
addResourcePaths: (state, action: PayloadAction<ResourcePath[]>) => {
const index = state.collections.findIndex(collection => collection.isDefault);
if (index > -1) {
state[index].paths.push(...action.payload)
state.collections[index].paths.push(...action.payload);
state.saved = false;
}
},
updateResourcePaths: (state, action: PayloadAction<ResourcePath[]>) => {
const collectionIndex = state.collections.findIndex(k => k.isDefault);
if (collectionIndex > -1) {
state.collections[collectionIndex] = {
...state.collections[collectionIndex],
paths: action.payload
};
state.saved = true;
}
},
removeResourcePaths: (state, action: PayloadAction<ResourcePath[]>)=>{
const index = state.findIndex(collection => collection.isDefault);
if(index > -1) {
const defaultResourcePaths = [...state[index].paths];
action.payload.forEach((resourcePath: ResourcePath)=>{
const delIndex = defaultResourcePaths.findIndex(p=>p.key === resourcePath.key)
removeResourcePaths: (state, action: PayloadAction<ResourcePath[]>) => {
const index = state.collections.findIndex(collection => collection.isDefault);
if (index > -1) {
const defaultResourcePaths = [...state.collections[index].paths];
action.payload.forEach((resourcePath: ResourcePath) => {
const delIndex = defaultResourcePaths.findIndex(p => p.key === resourcePath.key);
if (delIndex > -1) {
defaultResourcePaths.splice(delIndex, 1)
defaultResourcePaths.splice(delIndex, 1);
}
})
state[index].paths = defaultResourcePaths;
});
state.collections[index].paths = defaultResourcePaths;
state.saved = false;
}
},
resetSaveState: (state) => {
state.saved = false;
}
}
})
});

export const {createCollection, addResourcePaths, removeResourcePaths} = collections.actions
export const
{ createCollection,
addResourcePaths,
updateResourcePaths,
removeResourcePaths,
resetSaveState } = collections.actions;

export default collections.reducer
export default collections.reducer;
2 changes: 1 addition & 1 deletion src/app/utils/searchbox.styles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const searchBoxStyles: any = () => ({
root: {
width: '97%'
width: '100%'
},
field: [
{
Expand Down
Loading

0 comments on commit 6b56ffa

Please sign in to comment.