Skip to content

Commit 7541a1d

Browse files
Remove mock auth & split REST APIs (#2)
1 parent 371068e commit 7541a1d

File tree

12 files changed

+206
-214
lines changed

12 files changed

+206
-214
lines changed

.env

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
REACT_APP_USE_AUTHENTICATION=true
2-
3-
REACT_APP_API_GATEWAY=api/gateway
4-
REACT_APP_WS_GATEWAY=ws/gateway
5-
61
EXTEND_ESLINT=true
2+
3+
REACT_APP_API_GATEWAY=/api/gateway
4+
REACT_APP_WS_GATEWAY=/ws/gateway

.env.development

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/components/app-top-bar.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import { LIGHT_THEME, logout, TopBar } from '@gridsuite/commons-ui';
1010
import Parameters, { useParameterState } from './parameters';
1111
import { APP_NAME, PARAM_LANGUAGE, PARAM_THEME } from '../utils/config-params';
1212
import { useDispatch, useSelector } from 'react-redux';
13-
import { fetchAppsAndUrls, fetchVersion } from '../utils/rest-api';
14-
import { getServersInfos } from '../rest/study';
13+
import { AppsMetadataSrv, StudySrv } from '../services';
1514
import { useNavigate } from 'react-router-dom';
1615
import { ReactComponent as PowsyblLogo } from '../images/powsybl_logo.svg';
1716
import AppPackage from '../../package.json';
@@ -31,7 +30,7 @@ const AppTopBar: FunctionComponent<AppTopBarProps> = (props) => {
3130
const dispatch = useDispatch();
3231

3332
const [appsAndUrls, setAppsAndUrls] = useState<
34-
Awaited<ReturnType<typeof fetchAppsAndUrls>>
33+
Awaited<ReturnType<typeof AppsMetadataSrv.fetchAppsAndUrls>>
3534
>([]);
3635

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

4645
useEffect(() => {
4746
if (props.user !== null) {
48-
fetchAppsAndUrls().then((res) => {
47+
AppsMetadataSrv.fetchAppsAndUrls().then((res) => {
4948
setAppsAndUrls(res);
5049
});
5150
}
@@ -73,9 +72,11 @@ const AppTopBar: FunctionComponent<AppTopBarProps> = (props) => {
7372
user={props.user}
7473
appsAndUrls={appsAndUrls}
7574
globalVersionPromise={() =>
76-
fetchVersion().then((res) => res?.deployVersion)
75+
AppsMetadataSrv.fetchVersion().then(
76+
(res) => res?.deployVersion
77+
)
7778
}
78-
additionalModulesPromise={getServersInfos}
79+
additionalModulesPromise={StudySrv.getServersInfos}
7980
onThemeClick={handleChangeTheme}
8081
theme={themeLocal}
8182
onLanguageClick={handleChangeLanguage}

src/components/app.tsx

Lines changed: 29 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import {
2626
AuthenticationRouter,
2727
CardErrorBoundary,
2828
getPreLoginPath,
29-
initializeAuthenticationDev,
3029
initializeAuthenticationProd,
3130
useSnackMessage,
3231
} from '@gridsuite/commons-ui';
@@ -37,14 +36,13 @@ import {
3736
} from '../redux/actions';
3837
import { AppState } from '../redux/reducer';
3938
import {
39+
ConfigSrv,
4040
ConfigParameter,
4141
ConfigParameters,
42-
connectNotificationsWsUpdateConfig,
43-
fetchAuthorizationCodeFlowFeatureFlag,
44-
fetchConfigParameter,
45-
fetchConfigParameters,
46-
fetchValidateUser,
47-
} from '../utils/rest-api';
42+
UserAdminSrv,
43+
AppsMetadataSrv,
44+
} from '../services';
45+
import { connectNotificationsWsUpdateConfig } from '../utils/rest-api';
4846
import { UserManager } from 'oidc-client';
4947
import {
5048
APP_NAME,
@@ -109,8 +107,10 @@ const App: FunctionComponent = () => {
109107
const ws = connectNotificationsWsUpdateConfig();
110108
ws.onmessage = function (event) {
111109
let eventData = JSON.parse(event.data);
112-
if (eventData.headers && eventData.headers['parameterName']) {
113-
fetchConfigParameter(eventData.headers['parameterName'])
110+
if (eventData?.headers?.parameterName) {
111+
ConfigSrv.fetchConfigParameter(
112+
eventData.headers.parameterName
113+
)
114114
.then((param) => updateParams([param]))
115115
.catch((error) =>
116116
snackError({
@@ -132,58 +132,40 @@ const App: FunctionComponent = () => {
132132
path: '/silent-renew-callback',
133133
})
134134
);
135-
136-
const [initialMatchSigninCallbackUrl] = useState(
135+
const [initialMatchSignInCallbackUrl] = useState(
137136
useMatch({
138137
path: '/sign-in-callback',
139138
})
140139
);
141140

142-
const initialize: () => Promise<UserManager> = useCallback(() => {
143-
if (process.env.REACT_APP_USE_AUTHENTICATION === 'true') {
144-
return fetchAuthorizationCodeFlowFeatureFlag().then(
145-
(authorizationCodeFlowEnabled) =>
146-
initializeAuthenticationProd(
147-
dispatch,
148-
initialMatchSilentRenewCallbackUrl != null,
149-
fetch('idpSettings.json'),
150-
fetchValidateUser,
151-
authorizationCodeFlowEnabled,
152-
initialMatchSigninCallbackUrl != null
153-
)
154-
);
155-
} else {
156-
return initializeAuthenticationDev(
157-
dispatch,
158-
initialMatchSilentRenewCallbackUrl != null,
159-
() =>
160-
new Promise((resolve) =>
161-
window.setTimeout(() => resolve(true), 500)
162-
),
163-
initialMatchSigninCallbackUrl != null
164-
);
165-
}
166-
// Note: initialMatchSilentRenewCallbackUrl and dispatch don't change
167-
}, [
168-
initialMatchSilentRenewCallbackUrl,
169-
dispatch,
170-
initialMatchSigninCallbackUrl,
171-
]);
172-
173141
useEffect(() => {
174-
initialize()
142+
AppsMetadataSrv.fetchAuthorizationCodeFlowFeatureFlag()
143+
.then((authorizationCodeFlowEnabled) =>
144+
initializeAuthenticationProd(
145+
dispatch,
146+
initialMatchSilentRenewCallbackUrl != null,
147+
fetch('idpSettings.json'),
148+
UserAdminSrv.fetchValidateUser,
149+
authorizationCodeFlowEnabled,
150+
initialMatchSignInCallbackUrl != null
151+
)
152+
)
175153
.then((userManager: UserManager | undefined) => {
176154
setUserManager({ instance: userManager || null, error: null });
177155
})
178156
.catch((error: any) => {
179157
setUserManager({ instance: null, error: error.message });
180158
});
181-
// Note: initialize and initialMatchSilentRenewCallbackUrl won't change
182-
}, [initialize, initialMatchSilentRenewCallbackUrl, dispatch]);
159+
// Note: initialize and initialMatchSilentRenewCallbackUrl & initialMatchSignInCallbackUrl won't change
160+
}, [
161+
dispatch,
162+
initialMatchSilentRenewCallbackUrl,
163+
initialMatchSignInCallbackUrl,
164+
]);
183165

184166
useEffect(() => {
185167
if (user !== null) {
186-
fetchConfigParameters(COMMON_APP_NAME)
168+
ConfigSrv.fetchConfigParameters(COMMON_APP_NAME)
187169
.then((params) => updateParams(params))
188170
.catch((error) =>
189171
snackError({
@@ -192,7 +174,7 @@ const App: FunctionComponent = () => {
192174
})
193175
);
194176

195-
fetchConfigParameters(APP_NAME)
177+
ConfigSrv.fetchConfigParameters(APP_NAME)
196178
.then((params) => updateParams(params))
197179
.catch((error) =>
198180
snackError({

src/components/parameters.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
Typography,
2929
} from '@mui/material';
3030
import { CSSObject, Theme } from '@emotion/react';
31-
import { updateConfigParameter } from '../utils/rest-api';
31+
import { ConfigSrv } from '../services';
3232
import { useSnackMessage } from '@gridsuite/commons-ui';
3333
import { AppState } from '../redux/reducer';
3434
import { TypographyTypeMap } from '@mui/material/Typography/Typography';
@@ -65,7 +65,7 @@ export function useParameterState<
6565
const handleChangeParamLocalState = useCallback(
6666
(value: any) => {
6767
setParamLocalState(value);
68-
updateConfigParameter(paramName, value).catch((error) => {
68+
ConfigSrv.updateConfigParameter(paramName, value).catch((error) => {
6969
setParamLocalState(paramGlobalState);
7070
snackError({
7171
messageTxt: error.message,

src/services/apps-metadata.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { ReqResponse } from '../utils/rest-api';
2+
3+
export type EnvJson = typeof import('../../public/env.json');
4+
5+
function fetchEnv(): Promise<EnvJson> {
6+
return fetch('/env.json').then((res: ReqResponse) => res.json());
7+
}
8+
9+
export function fetchAuthorizationCodeFlowFeatureFlag(): Promise<boolean> {
10+
console.info(`Fetching authorization code flow feature flag...`);
11+
return fetchEnv()
12+
.then((env: EnvJson) =>
13+
fetch(`${env.appsMetadataServerUrl}/authentication.json`)
14+
)
15+
.then((res: ReqResponse) => res.json())
16+
.then((res: Record<string, any>) => {
17+
console.log(
18+
`Authorization code flow is ${
19+
res.authorizationCodeFlowFeatureFlag
20+
? 'enabled'
21+
: 'disabled'
22+
}`
23+
);
24+
return res.authorizationCodeFlowFeatureFlag;
25+
})
26+
.catch((error) => {
27+
console.error(error);
28+
console.warn(
29+
`Something wrong happened when retrieving authentication.json: authorization code flow will be disabled`
30+
);
31+
return false;
32+
});
33+
}
34+
35+
export function fetchVersion(): Promise<Record<string, any>> {
36+
console.info(`Fetching global metadata...`);
37+
return fetchEnv()
38+
.then((env: EnvJson) =>
39+
fetch(`${env.appsMetadataServerUrl}/version.json`)
40+
)
41+
.then((response: ReqResponse) => response.json())
42+
.catch((reason) => {
43+
console.error(`Error while fetching the version : ${reason}`);
44+
return reason;
45+
});
46+
}
47+
48+
export function fetchAppsAndUrls(): Promise<Array<Record<string, any>>> {
49+
console.info(`Fetching apps and urls...`);
50+
return fetchEnv()
51+
.then((env: EnvJson) =>
52+
fetch(`${env.appsMetadataServerUrl}/apps-metadata.json`)
53+
)
54+
.then((response: ReqResponse) => response.json());
55+
}

src/services/config.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { getAppName } from '../utils/config-params';
2+
import { backendFetch, backendFetchJson } from '../utils/rest-api';
3+
4+
const PREFIX_CONFIG_QUERIES = `${process.env.REACT_APP_API_GATEWAY}/config`;
5+
6+
export type ConfigParameter = {
7+
//TODO check with config-server swagger
8+
name: string;
9+
value: any;
10+
[propertiesName: string]: unknown; //temporary
11+
};
12+
export type ConfigParameters = Array<ConfigParameter>;
13+
export function fetchConfigParameters(
14+
appName: string
15+
): Promise<ConfigParameters> {
16+
console.info(`Fetching UI configuration params for app : ${appName}`);
17+
const fetchParams = `${PREFIX_CONFIG_QUERIES}/v1/applications/${appName}/parameters`;
18+
return backendFetchJson(fetchParams);
19+
}
20+
21+
export function fetchConfigParameter(
22+
name: string
23+
): ReturnType<typeof backendFetchJson> {
24+
const appName = getAppName(name);
25+
console.info(`Fetching UI config parameter '${name}' for app '${appName}'`);
26+
const fetchParams = `${PREFIX_CONFIG_QUERIES}/v1/applications/${appName}/parameters/${name}`;
27+
return backendFetchJson(fetchParams);
28+
}
29+
30+
export function updateConfigParameter(
31+
name: string,
32+
value: Parameters<typeof encodeURIComponent>[0]
33+
): ReturnType<typeof backendFetch> {
34+
const appName = getAppName(name);
35+
console.info(
36+
`Updating config parameter '${name}=${value}' for app '${appName}'`
37+
);
38+
const updateParams = `${PREFIX_CONFIG_QUERIES}/v1/applications/${appName}/parameters/${name}?value=${encodeURIComponent(
39+
value
40+
)}`;
41+
return backendFetch(updateParams, { method: 'put' });
42+
}

src/services/index.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as Config from './config';
2+
import * as AppsMetadata from './apps-metadata';
3+
import * as Study from './study';
4+
import * as UserAdmin from './user-admin';
5+
6+
const _ = {
7+
...Config,
8+
...AppsMetadata,
9+
...Study,
10+
...UserAdmin,
11+
};
12+
export default _;
13+
14+
export * as ConfigSrv from './config';
15+
export type * from './config';
16+
17+
export * as AppsMetadataSrv from './apps-metadata';
18+
export type * from './apps-metadata';
19+
20+
export * as StudySrv from './study';
21+
export type * from './study';
22+
23+
export * as UserAdminSrv from './user-admin';
24+
export type * from './user-admin';

src/rest/study.ts renamed to src/services/study.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@
66
*/
77
import { backendFetchJson, Token } from '../utils/rest-api';
88

9-
const API_URL =
10-
'/api/' +
11-
(process.env.REACT_APP_USE_AUTHENTICATION === 'true'
12-
? `${process.env.REACT_APP_API_GATEWAY}/study/v1`
13-
: `${process.env.REACT_APP_SRV_STUDY_URI}/v1`);
9+
const STUDY_URL = `${process.env.REACT_APP_API_GATEWAY}/study/v1`;
1410

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

2319
export function getServersInfos(token: Token): Promise<ServerAbout[]> {
2420
return backendFetchJson(
25-
`${API_URL}/servers/about`,
21+
`${STUDY_URL}/servers/about`,
2622
{
2723
headers: {
2824
Accept: 'application/json',

src/services/user-admin.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { backendFetch, ReqResponse } from '../utils/rest-api';
2+
3+
const USER_ADMIN_URL = `${process.env.REACT_APP_API_GATEWAY}/user-admin`;
4+
5+
export function fetchValidateUser(user: Record<string, any>): Promise<boolean> {
6+
const sub = user?.profile?.sub;
7+
if (!sub) {
8+
return Promise.reject(
9+
new Error(
10+
`Fetching access for missing user.profile.sub : ${JSON.stringify(
11+
user
12+
)}`
13+
)
14+
);
15+
}
16+
17+
console.info(`Fetching access for user...`);
18+
const CheckAccessUrl = `${USER_ADMIN_URL}/v1/users/${sub}`;
19+
console.debug(CheckAccessUrl);
20+
21+
return backendFetch(CheckAccessUrl, { method: 'head' }, user?.id_token)
22+
.then((response: ReqResponse) => {
23+
//if the response is ok, the responseCode will be either 200 or 204 otherwise it's an HTTP error and it will be caught
24+
return response.status === 200;
25+
})
26+
.catch((error) => {
27+
if (error.status === 403) {
28+
return false;
29+
} else {
30+
throw error;
31+
}
32+
});
33+
}

0 commit comments

Comments
 (0)