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
8 changes: 3 additions & 5 deletions .env
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
REACT_APP_USE_AUTHENTICATION=true

REACT_APP_API_GATEWAY=api/gateway
REACT_APP_WS_GATEWAY=ws/gateway

EXTEND_ESLINT=true

REACT_APP_API_GATEWAY=/api/gateway
REACT_APP_WS_GATEWAY=/ws/gateway
3 changes: 0 additions & 3 deletions .env.development

This file was deleted.

13 changes: 7 additions & 6 deletions src/components/app-top-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import { LIGHT_THEME, logout, TopBar } from '@gridsuite/commons-ui';
import Parameters, { useParameterState } from './parameters';
import { APP_NAME, PARAM_LANGUAGE, PARAM_THEME } from '../utils/config-params';
import { useDispatch, useSelector } from 'react-redux';
import { fetchAppsAndUrls, fetchVersion } from '../utils/rest-api';
import { getServersInfos } from '../rest/study';
import { AppsMetadataSrv, StudySrv } from '../services';
import { useNavigate } from 'react-router-dom';
import { ReactComponent as PowsyblLogo } from '../images/powsybl_logo.svg';
import AppPackage from '../../package.json';
Expand All @@ -31,7 +30,7 @@ const AppTopBar: FunctionComponent<AppTopBarProps> = (props) => {
const dispatch = useDispatch();

const [appsAndUrls, setAppsAndUrls] = useState<
Awaited<ReturnType<typeof fetchAppsAndUrls>>
Awaited<ReturnType<typeof AppsMetadataSrv.fetchAppsAndUrls>>
>([]);

const theme = useSelector((state: AppState) => state[PARAM_THEME]);
Expand All @@ -45,7 +44,7 @@ const AppTopBar: FunctionComponent<AppTopBarProps> = (props) => {

useEffect(() => {
if (props.user !== null) {
fetchAppsAndUrls().then((res) => {
AppsMetadataSrv.fetchAppsAndUrls().then((res) => {
setAppsAndUrls(res);
});
}
Expand Down Expand Up @@ -73,9 +72,11 @@ const AppTopBar: FunctionComponent<AppTopBarProps> = (props) => {
user={props.user}
appsAndUrls={appsAndUrls}
globalVersionPromise={() =>
fetchVersion().then((res) => res?.deployVersion)
AppsMetadataSrv.fetchVersion().then(
(res) => res?.deployVersion
)
}
additionalModulesPromise={getServersInfos}
additionalModulesPromise={StudySrv.getServersInfos}
onThemeClick={handleChangeTheme}
theme={themeLocal}
onLanguageClick={handleChangeLanguage}
Expand Down
76 changes: 29 additions & 47 deletions src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
AuthenticationRouter,
CardErrorBoundary,
getPreLoginPath,
initializeAuthenticationDev,
initializeAuthenticationProd,
useSnackMessage,
} from '@gridsuite/commons-ui';
Expand All @@ -37,14 +36,13 @@ import {
} from '../redux/actions';
import { AppState } from '../redux/reducer';
import {
ConfigSrv,
ConfigParameter,
ConfigParameters,
connectNotificationsWsUpdateConfig,
fetchAuthorizationCodeFlowFeatureFlag,
fetchConfigParameter,
fetchConfigParameters,
fetchValidateUser,
} from '../utils/rest-api';
UserAdminSrv,
AppsMetadataSrv,
} from '../services';
import { connectNotificationsWsUpdateConfig } from '../utils/rest-api';
import { UserManager } from 'oidc-client';
import {
APP_NAME,
Expand Down Expand Up @@ -109,8 +107,10 @@ const App: FunctionComponent = () => {
const ws = connectNotificationsWsUpdateConfig();
ws.onmessage = function (event) {
let eventData = JSON.parse(event.data);
if (eventData.headers && eventData.headers['parameterName']) {
fetchConfigParameter(eventData.headers['parameterName'])
if (eventData?.headers?.parameterName) {
ConfigSrv.fetchConfigParameter(
eventData.headers.parameterName
)
.then((param) => updateParams([param]))
.catch((error) =>
snackError({
Expand All @@ -132,58 +132,40 @@ const App: FunctionComponent = () => {
path: '/silent-renew-callback',
})
);

const [initialMatchSigninCallbackUrl] = useState(
const [initialMatchSignInCallbackUrl] = useState(
useMatch({
path: '/sign-in-callback',
})
);

const initialize: () => Promise<UserManager> = useCallback(() => {
if (process.env.REACT_APP_USE_AUTHENTICATION === 'true') {
return fetchAuthorizationCodeFlowFeatureFlag().then(
(authorizationCodeFlowEnabled) =>
initializeAuthenticationProd(
dispatch,
initialMatchSilentRenewCallbackUrl != null,
fetch('idpSettings.json'),
fetchValidateUser,
authorizationCodeFlowEnabled,
initialMatchSigninCallbackUrl != null
)
);
} else {
return initializeAuthenticationDev(
dispatch,
initialMatchSilentRenewCallbackUrl != null,
() =>
new Promise((resolve) =>
window.setTimeout(() => resolve(true), 500)
),
initialMatchSigninCallbackUrl != null
);
}
// Note: initialMatchSilentRenewCallbackUrl and dispatch don't change
}, [
initialMatchSilentRenewCallbackUrl,
dispatch,
initialMatchSigninCallbackUrl,
]);

useEffect(() => {
initialize()
AppsMetadataSrv.fetchAuthorizationCodeFlowFeatureFlag()
.then((authorizationCodeFlowEnabled) =>
initializeAuthenticationProd(
dispatch,
initialMatchSilentRenewCallbackUrl != null,
fetch('idpSettings.json'),
UserAdminSrv.fetchValidateUser,
authorizationCodeFlowEnabled,
initialMatchSignInCallbackUrl != null
)
)
.then((userManager: UserManager | undefined) => {
setUserManager({ instance: userManager || null, error: null });
})
.catch((error: any) => {
setUserManager({ instance: null, error: error.message });
});
// Note: initialize and initialMatchSilentRenewCallbackUrl won't change
}, [initialize, initialMatchSilentRenewCallbackUrl, dispatch]);
// Note: initialize and initialMatchSilentRenewCallbackUrl & initialMatchSignInCallbackUrl won't change
}, [
dispatch,
initialMatchSilentRenewCallbackUrl,
initialMatchSignInCallbackUrl,
]);

useEffect(() => {
if (user !== null) {
fetchConfigParameters(COMMON_APP_NAME)
ConfigSrv.fetchConfigParameters(COMMON_APP_NAME)
.then((params) => updateParams(params))
.catch((error) =>
snackError({
Expand All @@ -192,7 +174,7 @@ const App: FunctionComponent = () => {
})
);

fetchConfigParameters(APP_NAME)
ConfigSrv.fetchConfigParameters(APP_NAME)
.then((params) => updateParams(params))
.catch((error) =>
snackError({
Expand Down
4 changes: 2 additions & 2 deletions src/components/parameters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
Typography,
} from '@mui/material';
import { CSSObject, Theme } from '@emotion/react';
import { updateConfigParameter } from '../utils/rest-api';
import { ConfigSrv } from '../services';
import { useSnackMessage } from '@gridsuite/commons-ui';
import { AppState } from '../redux/reducer';
import { TypographyTypeMap } from '@mui/material/Typography/Typography';
Expand Down Expand Up @@ -65,7 +65,7 @@ export function useParameterState<
const handleChangeParamLocalState = useCallback(
(value: any) => {
setParamLocalState(value);
updateConfigParameter(paramName, value).catch((error) => {
ConfigSrv.updateConfigParameter(paramName, value).catch((error) => {
setParamLocalState(paramGlobalState);
snackError({
messageTxt: error.message,
Expand Down
55 changes: 55 additions & 0 deletions src/services/apps-metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ReqResponse } from '../utils/rest-api';

export type EnvJson = typeof import('../../public/env.json');

function fetchEnv(): Promise<EnvJson> {
return fetch('/env.json').then((res: ReqResponse) => res.json());
}

export function fetchAuthorizationCodeFlowFeatureFlag(): Promise<boolean> {
console.info(`Fetching authorization code flow feature flag...`);
return fetchEnv()
.then((env: EnvJson) =>
fetch(`${env.appsMetadataServerUrl}/authentication.json`)
)
.then((res: ReqResponse) => res.json())
.then((res: Record<string, any>) => {
console.log(
`Authorization code flow is ${
res.authorizationCodeFlowFeatureFlag
? 'enabled'
: 'disabled'
}`
);
return res.authorizationCodeFlowFeatureFlag;
})
.catch((error) => {
console.error(error);
console.warn(
`Something wrong happened when retrieving authentication.json: authorization code flow will be disabled`
);
return false;
});
}

export function fetchVersion(): Promise<Record<string, any>> {
console.info(`Fetching global metadata...`);
return fetchEnv()
.then((env: EnvJson) =>
fetch(`${env.appsMetadataServerUrl}/version.json`)
)
.then((response: ReqResponse) => response.json())
.catch((reason) => {
console.error(`Error while fetching the version : ${reason}`);
return reason;
});
}

export function fetchAppsAndUrls(): Promise<Array<Record<string, any>>> {
console.info(`Fetching apps and urls...`);
return fetchEnv()
.then((env: EnvJson) =>
fetch(`${env.appsMetadataServerUrl}/apps-metadata.json`)
)
.then((response: ReqResponse) => response.json());
}
42 changes: 42 additions & 0 deletions src/services/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { getAppName } from '../utils/config-params';
import { backendFetch, backendFetchJson } from '../utils/rest-api';

const PREFIX_CONFIG_QUERIES = `${process.env.REACT_APP_API_GATEWAY}/config`;

export type ConfigParameter = {
//TODO check with config-server swagger
name: string;
value: any;
[propertiesName: string]: unknown; //temporary
};
export type ConfigParameters = Array<ConfigParameter>;
export function fetchConfigParameters(
appName: string
): Promise<ConfigParameters> {
console.info(`Fetching UI configuration params for app : ${appName}`);
const fetchParams = `${PREFIX_CONFIG_QUERIES}/v1/applications/${appName}/parameters`;
return backendFetchJson(fetchParams);
}

export function fetchConfigParameter(
name: string
): ReturnType<typeof backendFetchJson> {
const appName = getAppName(name);
console.info(`Fetching UI config parameter '${name}' for app '${appName}'`);
const fetchParams = `${PREFIX_CONFIG_QUERIES}/v1/applications/${appName}/parameters/${name}`;
return backendFetchJson(fetchParams);
}

export function updateConfigParameter(
name: string,
value: Parameters<typeof encodeURIComponent>[0]
): ReturnType<typeof backendFetch> {
const appName = getAppName(name);
console.info(
`Updating config parameter '${name}=${value}' for app '${appName}'`
);
const updateParams = `${PREFIX_CONFIG_QUERIES}/v1/applications/${appName}/parameters/${name}?value=${encodeURIComponent(
value
)}`;
return backendFetch(updateParams, { method: 'put' });
}
24 changes: 24 additions & 0 deletions src/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as Config from './config';
import * as AppsMetadata from './apps-metadata';
import * as Study from './study';
import * as UserAdmin from './user-admin';

const _ = {
...Config,
...AppsMetadata,
...Study,
...UserAdmin,
};
export default _;

export * as ConfigSrv from './config';
export type * from './config';

export * as AppsMetadataSrv from './apps-metadata';
export type * from './apps-metadata';

export * as StudySrv from './study';
export type * from './study';

export * as UserAdminSrv from './user-admin';
export type * from './user-admin';
8 changes: 2 additions & 6 deletions src/rest/study.ts → src/services/study.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
*/
import { backendFetchJson, Token } from '../utils/rest-api';

const API_URL =
'/api/' +
(process.env.REACT_APP_USE_AUTHENTICATION === 'true'
? `${process.env.REACT_APP_API_GATEWAY}/study/v1`
: `${process.env.REACT_APP_SRV_STUDY_URI}/v1`);
const STUDY_URL = `${process.env.REACT_APP_API_GATEWAY}/study/v1`;

//TODO delete when commons-ui will be in typescript
export type ServerAbout = {
Expand All @@ -22,7 +18,7 @@ export type ServerAbout = {

export function getServersInfos(token: Token): Promise<ServerAbout[]> {
return backendFetchJson(
`${API_URL}/servers/about`,
`${STUDY_URL}/servers/about`,
{
headers: {
Accept: 'application/json',
Expand Down
33 changes: 33 additions & 0 deletions src/services/user-admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { backendFetch, ReqResponse } from '../utils/rest-api';

const USER_ADMIN_URL = `${process.env.REACT_APP_API_GATEWAY}/user-admin`;

export function fetchValidateUser(user: Record<string, any>): Promise<boolean> {
const sub = user?.profile?.sub;
if (!sub) {
return Promise.reject(
new Error(
`Fetching access for missing user.profile.sub : ${JSON.stringify(
user
)}`
)
);
}

console.info(`Fetching access for user...`);
const CheckAccessUrl = `${USER_ADMIN_URL}/v1/users/${sub}`;
console.debug(CheckAccessUrl);

return backendFetch(CheckAccessUrl, { method: 'head' }, user?.id_token)
.then((response: ReqResponse) => {
//if the response is ok, the responseCode will be either 200 or 204 otherwise it's an HTTP error and it will be caught
return response.status === 200;
})
.catch((error) => {
if (error.status === 403) {
return false;
} else {
throw error;
}
});
}
Loading