From 16128732ef1dec167cf97ae068ee006c061822d1 Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Thu, 11 Jan 2024 12:36:33 +0100 Subject: [PATCH 01/11] Store server configuration info in a state && render several auth components based on server API --- cvat-core/src/api-implementation.ts | 5 + cvat-core/src/api.ts | 4 + cvat-core/src/index.ts | 1 + cvat-core/src/server-proxy.ts | 12 ++ cvat-ui/src/actions/auth-actions.ts | 28 ----- cvat-ui/src/actions/server-actions.ts | 37 ++++++ cvat-ui/src/components/cvat-app.tsx | 38 ++++-- cvat-ui/src/components/header/header.tsx | 2 +- .../src/components/login-page/login-form.tsx | 118 +++++++++--------- .../src/components/login-page/login-page.tsx | 8 +- .../src/containers/login-page/login-page.tsx | 6 +- cvat-ui/src/index.tsx | 34 +++-- cvat-ui/src/reducers/auth-reducer.ts | 25 ---- cvat-ui/src/reducers/index.ts | 19 ++- cvat-ui/src/reducers/notifications-reducer.ts | 15 ++- cvat-ui/src/reducers/root-reducer.ts | 2 + cvat-ui/src/reducers/server-api-reducer.ts | 64 ++++++++++ 17 files changed, 264 insertions(+), 154 deletions(-) create mode 100644 cvat-ui/src/actions/server-actions.ts create mode 100644 cvat-ui/src/reducers/server-api-reducer.ts diff --git a/cvat-core/src/api-implementation.ts b/cvat-core/src/api-implementation.ts index 1e365965edbb..73dfbb32f1c1 100644 --- a/cvat-core/src/api-implementation.ts +++ b/cvat-core/src/api-implementation.ts @@ -136,6 +136,11 @@ export default function implementAPI(cvat: CVATCore): CVATCore { return result; }); + implementationMixin(cvat.server.apiSchema, async () => { + const result = await serverProxy.server.apiSchema(); + return result; + }); + implementationMixin(cvat.assets.create, async (file: File, guideId: number): Promise => { if (!(file instanceof File)) { throw new ArgumentError('Assets expect a file'); diff --git a/cvat-core/src/api.ts b/cvat-core/src/api.ts index e736e188ed82..f4b482ed48f0 100644 --- a/cvat-core/src/api.ts +++ b/cvat-core/src/api.ts @@ -129,6 +129,10 @@ function build(): CVATCore { const result = await PluginRegistry.apiWrapper(cvat.server.installedApps); return result; }, + async apiSchema() { + const result = await PluginRegistry.apiWrapper(cvat.server.apiSchema); + return result; + }, }, projects: { async get(filter = {}) { diff --git a/cvat-core/src/index.ts b/cvat-core/src/index.ts index 24e9ebb3ec62..7a1d0cfa643a 100644 --- a/cvat-core/src/index.ts +++ b/cvat-core/src/index.ts @@ -65,6 +65,7 @@ export default interface CVATCore { setAuthData: any; removeAuthData: any; installedApps: any; + apiSchema: any; }; assets: { create: any; diff --git a/cvat-core/src/server-proxy.ts b/cvat-core/src/server-proxy.ts index 77fac5b5648e..dd0757f9345f 100644 --- a/cvat-core/src/server-proxy.ts +++ b/cvat-core/src/server-proxy.ts @@ -1841,6 +1841,17 @@ async function installedApps() { } } +async function getApiSchema() { + const { backendAPI } = config; + + try { + const response = await Axios.get(`${backendAPI}/schema/?scheme=json`); + return response.data; + } catch (errorData) { + throw generateError(errorData); + } +} + async function createCloudStorage(storageDetail) { const { backendAPI } = config; @@ -2352,6 +2363,7 @@ export default Object.freeze({ request: serverRequest, userAgreements, installedApps, + apiSchema: getApiSchema, }), projects: Object.freeze({ diff --git a/cvat-ui/src/actions/auth-actions.ts b/cvat-ui/src/actions/auth-actions.ts index 9b8eb53b7bc4..9da362ab91ab 100644 --- a/cvat-ui/src/actions/auth-actions.ts +++ b/cvat-ui/src/actions/auth-actions.ts @@ -6,7 +6,6 @@ import { ActionUnion, createAction, ThunkAction } from 'utils/redux'; import { RegisterData } from 'components/register-page/register-form'; import { getCore } from 'cvat-core-wrapper'; -import isReachable from 'utils/url-checker'; const cvat = getCore(); @@ -33,9 +32,6 @@ export enum AuthActionTypes { RESET_PASSWORD = 'RESET_PASSWORD_CONFIRM', RESET_PASSWORD_SUCCESS = 'RESET_PASSWORD_CONFIRM_SUCCESS', RESET_PASSWORD_FAILED = 'RESET_PASSWORD_CONFIRM_FAILED', - LOAD_AUTH_ACTIONS = 'LOAD_AUTH_ACTIONS', - LOAD_AUTH_ACTIONS_SUCCESS = 'LOAD_AUTH_ACTIONS_SUCCESS', - LOAD_AUTH_ACTIONS_FAILED = 'LOAD_AUTH_ACTIONS_FAILED', } export const authActions = { @@ -65,14 +61,6 @@ export const authActions = { resetPassword: () => createAction(AuthActionTypes.RESET_PASSWORD), resetPasswordSuccess: () => createAction(AuthActionTypes.RESET_PASSWORD_SUCCESS), resetPasswordFailed: (error: any) => createAction(AuthActionTypes.RESET_PASSWORD_FAILED, { error }), - loadServerAuthActions: () => createAction(AuthActionTypes.LOAD_AUTH_ACTIONS), - loadServerAuthActionsSuccess: (allowChangePassword: boolean, allowResetPassword: boolean) => ( - createAction(AuthActionTypes.LOAD_AUTH_ACTIONS_SUCCESS, { - allowChangePassword, - allowResetPassword, - }) - ), - loadServerAuthActionsFailed: (error: any) => createAction(AuthActionTypes.LOAD_AUTH_ACTIONS_FAILED, { error }), }; export type AuthActions = ActionUnion; @@ -188,19 +176,3 @@ export const resetPasswordAsync = ( dispatch(authActions.resetPasswordFailed(error)); } }; - -export const loadAuthActionsAsync = (): ThunkAction => async (dispatch) => { - dispatch(authActions.loadServerAuthActions()); - - try { - const promises: Promise[] = [ - isReachable(`${cvat.config.backendAPI}/auth/password/change`, 'OPTIONS'), - isReachable(`${cvat.config.backendAPI}/auth/password/reset`, 'OPTIONS'), - ]; - const [allowChangePassword, allowResetPassword] = await Promise.all(promises); - - dispatch(authActions.loadServerAuthActionsSuccess(allowChangePassword, allowResetPassword)); - } catch (error) { - dispatch(authActions.loadServerAuthActionsFailed(error)); - } -}; diff --git a/cvat-ui/src/actions/server-actions.ts b/cvat-ui/src/actions/server-actions.ts new file mode 100644 index 000000000000..b5212fb976b4 --- /dev/null +++ b/cvat-ui/src/actions/server-actions.ts @@ -0,0 +1,37 @@ +// Copyright (C) 2024 CVAT.ai Corporation +// +// SPDX-License-Identifier: MIT + +import { ActionUnion, createAction, ThunkAction } from 'utils/redux'; +import { getCore } from 'cvat-core-wrapper'; + +const core = getCore(); + +export enum ServerAPIActionTypes { + GET_SERVER_API_SCHEMA = 'GET_SERVER_API_SCHEMA', + GET_SERVER_API_SCHEMA_SUCCESS = 'GET_SERVER_API_SCHEMA_SUCCESS', + GET_SERVER_API_SCHEMA_FAILED = 'GET_SERVER_API_SCHEMA_FAILED', +} + +const serverAPIActions = { + getServerAPISchema: () => createAction(ServerAPIActionTypes.GET_SERVER_API_SCHEMA), + getServerAPISchemaSuccess: (schema: any) => ( + createAction(ServerAPIActionTypes.GET_SERVER_API_SCHEMA_SUCCESS, { schema }) + ), + getServerAPISchemaFailed: (error: any) => ( + createAction(ServerAPIActionTypes.GET_SERVER_API_SCHEMA_FAILED, { error }) + ), +}; + +export type ServerAPIActions = ActionUnion; + +export const getServerAPISchemaAsync = (): ThunkAction => async (dispatch): Promise => { + dispatch(serverAPIActions.getServerAPISchema()); + + try { + const schema = await core.server.apiSchema(); + dispatch(serverAPIActions.getServerAPISchemaSuccess(schema)); + } catch (error) { + dispatch(serverAPIActions.getServerAPISchemaFailed(error)); + } +}; diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index ff95a5874787..dda3fd8a988e 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -89,9 +89,9 @@ interface CVATAppProps { initModels: () => void; resetErrors: () => void; resetMessages: () => void; - loadAuthActions: () => void; loadOrganization: () => void; initInvitations: () => void; + loadServerAPISchema: () => void; userInitialized: boolean; userFetching: boolean; organizationFetching: boolean; @@ -106,14 +106,16 @@ interface CVATAppProps { aboutFetching: boolean; userAgreementsFetching: boolean; userAgreementsInitialized: boolean; - authActionsFetching: boolean; - authActionsInitialized: boolean; notifications: NotificationsState; user: any; isModelPluginActive: boolean; pluginComponents: PluginsState['components']; invitationsFetching: boolean; invitationsInitialized: boolean; + serverAPISchemaFetching: boolean; + serverAPISchemaInitialized: boolean; + isPasswordResetEnabled: boolean; + isRegistrationEnabled: boolean; } interface CVATAppState { @@ -261,7 +263,7 @@ class CVATApplication extends React.PureComponent <> - + {isRegistrationEnabled && ( + + )} @@ -558,6 +564,16 @@ class CVATApplication extends React.PureComponent + {isPasswordResetEnabled && ( + + )} + {isPasswordResetEnabled && ( + + )} @@ -110,65 +112,69 @@ function LoginFormComponent(props: Props): JSX.Element { onSubmit(loginData); }} > - - Email or username} - className={credential ? 'cvat-input-floating-label-above' : 'cvat-input-floating-label'} - suffix={credential && ( - { - setCredential(''); - form.setFieldsValue({ credential: '', password: '' }); - }} - /> - )} - onChange={(event) => { - const { value } = event.target; - setCredential(value); - if (!value) form.setFieldsValue({ credential: '', password: '' }); - }} - /> - - { - credential && ( + {renderBasicLoginComponent && ( + <> - Email or username} + className={credential ? 'cvat-input-floating-label-above' : 'cvat-input-floating-label'} + suffix={credential && ( + { + setCredential(''); + form.setFieldsValue({ credential: '', password: '' }); + }} + /> + )} + onChange={(event) => { + const { value } = event.target; + setCredential(value); + if (!value) form.setFieldsValue({ credential: '', password: '' }); + }} /> - ) - } - { - !!credential && ( - - - - ) - } + { + credential && ( + + + + ) + } + { + !!credential && ( + + + + ) + } + + )} { pluginsToRender.map(({ component: Component }, index) => ( diff --git a/cvat-ui/src/components/login-page/login-page.tsx b/cvat-ui/src/components/login-page/login-page.tsx index 0a0073a74d19..ba0948d18d78 100644 --- a/cvat-ui/src/components/login-page/login-page.tsx +++ b/cvat-ui/src/components/login-page/login-page.tsx @@ -14,6 +14,8 @@ import LoginForm, { LoginData } from './login-form'; interface LoginPageComponentProps { fetching: boolean; renderResetPassword: boolean; + renderRegistrationComponent: boolean; + renderBasicLoginComponent: boolean; hasEmailVerificationBeenSent: boolean; onLogin: (credential: string, password: string) => void; } @@ -21,13 +23,13 @@ interface LoginPageComponentProps { function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps): JSX.Element { const history = useHistory(); const { - fetching, renderResetPassword, hasEmailVerificationBeenSent, onLogin, + fetching, renderResetPassword, renderRegistrationComponent, renderBasicLoginComponent, + hasEmailVerificationBeenSent, onLogin, } = props; if (hasEmailVerificationBeenSent) { history.push('/auth/email-verification-sent'); } - return ( @@ -36,6 +38,8 @@ function LoginPageComponent(props: LoginPageComponentProps & RouteComponentProps { onLogin(loginData.credential, loginData.password); }} diff --git a/cvat-ui/src/containers/login-page/login-page.tsx b/cvat-ui/src/containers/login-page/login-page.tsx index 71c39939f952..5d1aa2dfbf15 100644 --- a/cvat-ui/src/containers/login-page/login-page.tsx +++ b/cvat-ui/src/containers/login-page/login-page.tsx @@ -11,6 +11,8 @@ interface StateToProps { fetching: boolean; renderResetPassword: boolean; hasEmailVerificationBeenSent: boolean; + renderRegistrationComponent: boolean; + renderBasicLoginComponent: boolean; } interface DispatchToProps { @@ -20,7 +22,9 @@ interface DispatchToProps { function mapStateToProps(state: CombinedState): StateToProps { return { fetching: state.auth.fetching, - renderResetPassword: state.auth.allowResetPassword, + renderResetPassword: state.serverAPI.isPasswordResetEnabled, + renderRegistrationComponent: state.serverAPI.isRegistrationEnabled, + renderBasicLoginComponent: state.serverAPI.isBasicLoginEnabled, hasEmailVerificationBeenSent: state.auth.hasEmailVerificationBeenSent, }; } diff --git a/cvat-ui/src/index.tsx b/cvat-ui/src/index.tsx index a6fbecb0c5d3..1cd1e4ecb46a 100644 --- a/cvat-ui/src/index.tsx +++ b/cvat-ui/src/index.tsx @@ -9,7 +9,7 @@ import { connect, Provider } from 'react-redux'; import { BrowserRouter } from 'react-router-dom'; import { getAboutAsync } from 'actions/about-actions'; -import { authorizedAsync, loadAuthActionsAsync } from 'actions/auth-actions'; +import { authorizedAsync } from 'actions/auth-actions'; import { getFormatsAsync } from 'actions/formats-actions'; import { getModelsAsync } from 'actions/models-actions'; import { getPluginsAsync } from 'actions/plugins-actions'; @@ -23,6 +23,7 @@ import createRootReducer from 'reducers/root-reducer'; import { activateOrganizationAsync } from 'actions/organization-actions'; import { resetErrors, resetMessages } from 'actions/notification-actions'; import { getInvitationsAsync } from 'actions/invitations-actions'; +import { getServerAPISchemaAsync } from 'actions/server-actions'; import { CombinedState, NotificationsState, PluginsState } from './reducers'; createCVATStore(createRootReducer); @@ -44,16 +45,16 @@ interface StateToProps { formatsFetching: boolean; userAgreementsInitialized: boolean; userAgreementsFetching: boolean; - authActionsFetching: boolean; - authActionsInitialized: boolean; - allowChangePassword: boolean; - allowResetPassword: boolean; notifications: NotificationsState; user: any; isModelPluginActive: boolean; pluginComponents: PluginsState['components']; invitationsFetching: boolean; invitationsInitialized: boolean; + serverAPISchemaFetching: boolean; + serverAPISchemaInitialized: boolean; + isPasswordResetEnabled: boolean; + isRegistrationEnabled: boolean; } interface DispatchToProps { @@ -65,20 +66,15 @@ interface DispatchToProps { resetErrors: () => void; resetMessages: () => void; loadUserAgreements: () => void; - loadAuthActions: () => void; loadOrganization: () => void; initInvitations: () => void; + loadServerAPISchema: () => void; } function mapStateToProps(state: CombinedState): StateToProps { - const { plugins } = state; - const { auth } = state; - const { formats } = state; - const { about } = state; - const { userAgreements } = state; - const { models } = state; - const { organizations } = state; - const { invitations } = state; + const { + plugins, auth, formats, about, userAgreements, models, organizations, invitations, serverAPI, + } = state; return { userInitialized: auth.initialized, @@ -95,16 +91,16 @@ function mapStateToProps(state: CombinedState): StateToProps { formatsFetching: formats.fetching, userAgreementsInitialized: userAgreements.initialized, userAgreementsFetching: userAgreements.fetching, - authActionsFetching: auth.authActionsFetching, - authActionsInitialized: auth.authActionsInitialized, - allowChangePassword: auth.allowChangePassword, - allowResetPassword: auth.allowResetPassword, notifications: state.notifications, user: auth.user, pluginComponents: plugins.components, isModelPluginActive: plugins.list.MODELS, invitationsFetching: invitations.fetching, invitationsInitialized: invitations.initialized, + serverAPISchemaFetching: serverAPI.fetching, + serverAPISchemaInitialized: serverAPI.initialized, + isPasswordResetEnabled: serverAPI.isPasswordResetEnabled, + isRegistrationEnabled: serverAPI.isRegistrationEnabled, }; } @@ -118,9 +114,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { loadAbout: (): void => dispatch(getAboutAsync()), resetErrors: (): void => dispatch(resetErrors()), resetMessages: (): void => dispatch(resetMessages()), - loadAuthActions: (): void => dispatch(loadAuthActionsAsync()), loadOrganization: (): void => dispatch(activateOrganizationAsync()), initInvitations: (): void => dispatch(getInvitationsAsync({ page: 1 }, true)), + loadServerAPISchema: (): void => dispatch(getServerAPISchemaAsync()), }; } diff --git a/cvat-ui/src/reducers/auth-reducer.ts b/cvat-ui/src/reducers/auth-reducer.ts index 086ea5b2e1dc..362903fad49c 100644 --- a/cvat-ui/src/reducers/auth-reducer.ts +++ b/cvat-ui/src/reducers/auth-reducer.ts @@ -11,11 +11,7 @@ const defaultState: AuthState = { initialized: false, fetching: false, user: null, - authActionsFetching: false, - authActionsInitialized: false, - allowChangePassword: false, showChangePasswordDialog: false, - allowResetPassword: false, hasEmailVerificationBeenSent: false, }; @@ -140,27 +136,6 @@ export default function (state = defaultState, action: AuthActions | BoundariesA ...state, fetching: false, }; - case AuthActionTypes.LOAD_AUTH_ACTIONS: - return { - ...state, - authActionsFetching: true, - }; - case AuthActionTypes.LOAD_AUTH_ACTIONS_SUCCESS: - return { - ...state, - authActionsFetching: false, - authActionsInitialized: true, - allowChangePassword: action.payload.allowChangePassword, - allowResetPassword: action.payload.allowResetPassword, - }; - case AuthActionTypes.LOAD_AUTH_ACTIONS_FAILED: - return { - ...state, - authActionsFetching: false, - authActionsInitialized: true, - allowChangePassword: false, - allowResetPassword: false, - }; case BoundariesActionTypes.RESET_AFTER_ERROR: { return { ...defaultState }; } diff --git a/cvat-ui/src/reducers/index.ts b/cvat-ui/src/reducers/index.ts index 4c52ae1635eb..24f258a2881c 100644 --- a/cvat-ui/src/reducers/index.ts +++ b/cvat-ui/src/reducers/index.ts @@ -18,11 +18,7 @@ export interface AuthState { initialized: boolean; fetching: boolean; user: any; - authActionsFetching: boolean; - authActionsInitialized: boolean; showChangePasswordDialog: boolean; - allowChangePassword: boolean; - allowResetPassword: boolean; hasEmailVerificationBeenSent: boolean; } @@ -345,6 +341,16 @@ export interface AboutState { initialized: boolean; } +export interface ServerAPIState { + schema: any; + fetching: boolean; + initialized: boolean; + isRegistrationEnabled: boolean; + isBasicLoginEnabled: boolean; + isPasswordResetEnabled: boolean; + isPasswordChangeEnabled: boolean; +} + export interface UserAgreement { name: string; urlDisplayText: string; @@ -434,7 +440,9 @@ export interface NotificationsState { changePassword: null | ErrorState; requestPasswordReset: null | ErrorState; resetPassword: null | ErrorState; - loadAuthActions: null | ErrorState; + }; + serverAPI: { + fetching: null | ErrorState; }; projects: { fetching: null | ErrorState; @@ -966,6 +974,7 @@ export interface CombinedState { invitations: InvitationsState; webhooks: WebhooksState; analytics: AnalyticsState; + serverAPI: ServerAPIState; } export interface Indexable { diff --git a/cvat-ui/src/reducers/notifications-reducer.ts b/cvat-ui/src/reducers/notifications-reducer.ts index 81049aca60a5..16a6353bebea 100644 --- a/cvat-ui/src/reducers/notifications-reducer.ts +++ b/cvat-ui/src/reducers/notifications-reducer.ts @@ -24,6 +24,7 @@ import { OrganizationActionsTypes } from 'actions/organization-actions'; import { JobsActionTypes } from 'actions/jobs-actions'; import { WebhooksActionsTypes } from 'actions/webhooks-actions'; import { InvitationsActionTypes } from 'actions/invitations-actions'; +import { ServerAPIActionTypes } from 'actions/server-actions'; import { AnalyticsActionsTypes } from 'actions/analytics-actions'; import { NotificationsState } from '.'; @@ -38,7 +39,9 @@ const defaultState: NotificationsState = { changePassword: null, requestPasswordReset: null, resetPassword: null, - loadAuthActions: null, + }, + serverAPI: { + fetching: null, }, projects: { fetching: null, @@ -381,15 +384,15 @@ export default function (state = defaultState, action: AnyAction): Notifications }, }; } - case AuthActionTypes.LOAD_AUTH_ACTIONS_FAILED: { + case ServerAPIActionTypes.GET_SERVER_API_SCHEMA_FAILED: { return { ...state, errors: { ...state.errors, - auth: { - ...state.errors.auth, - loadAuthActions: { - message: 'Could not check available auth actions', + serverAPI: { + ...state.errors.serverAPI, + fetching: { + message: 'Could not receive server schema', reason: action.payload.error, shouldLog: !(action.payload.error instanceof ServerError), }, diff --git a/cvat-ui/src/reducers/root-reducer.ts b/cvat-ui/src/reducers/root-reducer.ts index 323eb016153c..2aa6958a8009 100644 --- a/cvat-ui/src/reducers/root-reducer.ts +++ b/cvat-ui/src/reducers/root-reducer.ts @@ -25,6 +25,7 @@ import organizationsReducer from './organizations-reducer'; import webhooksReducer from './webhooks-reducer'; import analyticsReducer from './analytics-reducer'; import invitationsReducer from './invitations-reducer'; +import serverAPIReducer from './server-api-reducer'; export default function createRootReducer(): Reducer { return combineReducers({ @@ -49,5 +50,6 @@ export default function createRootReducer(): Reducer { webhooks: webhooksReducer, analytics: analyticsReducer, invitations: invitationsReducer, + serverAPI: serverAPIReducer, }); } diff --git a/cvat-ui/src/reducers/server-api-reducer.ts b/cvat-ui/src/reducers/server-api-reducer.ts new file mode 100644 index 000000000000..10a42c28cce4 --- /dev/null +++ b/cvat-ui/src/reducers/server-api-reducer.ts @@ -0,0 +1,64 @@ +// Copyright (C) 2024 CVAT.ai Corporation +// +// SPDX-License-Identifier: MIT + +import { BoundariesActions, BoundariesActionTypes } from 'actions/boundaries-actions'; +import { ServerAPIActions, ServerAPIActionTypes } from 'actions/server-actions'; +import { ServerAPIState } from '.'; + +const defaultState: ServerAPIState = { + schema: null, + fetching: false, + initialized: false, + isRegistrationEnabled: true, + isBasicLoginEnabled: true, + isPasswordResetEnabled: true, + isPasswordChangeEnabled: true, +}; + +export default function ( + state: ServerAPIState = defaultState, + action: ServerAPIActions | BoundariesActions, +): ServerAPIState { + switch (action.type) { + case ServerAPIActionTypes.GET_SERVER_API_SCHEMA: { + return { + ...state, + fetching: true, + initialized: false, + }; + } + case ServerAPIActionTypes.GET_SERVER_API_SCHEMA_SUCCESS: { + const { schema } = action.payload; + const isRegistrationEnabled = Object.keys(schema.paths).includes('/api/auth/register'); + const isBasicLoginEnabled = Object.keys(schema.paths).includes('/api/auth/login'); + const isPasswordResetEnabled = Object.keys(schema.paths).includes('/api/auth/password/reset'); + const isPasswordChangeEnabled = Object.keys(schema.paths).includes('/api/auth/password/change'); + + return { + ...state, + fetching: false, + initialized: true, + schema: action.payload.schema, + isRegistrationEnabled, + isBasicLoginEnabled, + isPasswordResetEnabled, + isPasswordChangeEnabled, + }; + } + case ServerAPIActionTypes.GET_SERVER_API_SCHEMA_FAILED: { + return { + ...state, + fetching: false, + initialized: true, + }; + } + case BoundariesActionTypes.RESET_AFTER_ERROR: { + return { + ...defaultState, + }; + } + default: + return state; + } +} From bbb0bef904cfc01e7b92c7283abb8cad36e5ad46 Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Thu, 11 Jan 2024 16:00:02 +0100 Subject: [PATCH 02/11] v14.1.0 --- cvat-core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat-core/package.json b/cvat-core/package.json index 2f53bfef2d6a..ce97097c1408 100644 --- a/cvat-core/package.json +++ b/cvat-core/package.json @@ -1,6 +1,6 @@ { "name": "cvat-core", - "version": "14.0.3", + "version": "14.1.0", "type": "module", "description": "Part of Computer Vision Tool which presents an interface for client-side integration", "main": "src/api.ts", From 8032a69cdf2c5c1d892a254581e8374ffabc6e99 Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Thu, 11 Jan 2024 16:00:09 +0100 Subject: [PATCH 03/11] v1.62.0 --- cvat-ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 614568c7a649..f7f1edb27ce5 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.61.1", + "version": "1.62.0", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { From fabbd1d4af33dbd720e9837f93b33ac8e89f2536 Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Thu, 11 Jan 2024 16:17:04 +0100 Subject: [PATCH 04/11] Run CI From 66516d8718e5628a4500cc404483627ba82a6849 Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Wed, 17 Jan 2024 11:08:15 +0100 Subject: [PATCH 05/11] Apply comments --- .../src/containers/login-page/login-page.tsx | 6 ++--- cvat-ui/src/index.tsx | 4 ++-- cvat-ui/src/reducers/index.ts | 10 +++++---- cvat-ui/src/reducers/server-api-reducer.ts | 22 +++++++++++-------- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/cvat-ui/src/containers/login-page/login-page.tsx b/cvat-ui/src/containers/login-page/login-page.tsx index 5d1aa2dfbf15..8c8b10707c7e 100644 --- a/cvat-ui/src/containers/login-page/login-page.tsx +++ b/cvat-ui/src/containers/login-page/login-page.tsx @@ -22,9 +22,9 @@ interface DispatchToProps { function mapStateToProps(state: CombinedState): StateToProps { return { fetching: state.auth.fetching, - renderResetPassword: state.serverAPI.isPasswordResetEnabled, - renderRegistrationComponent: state.serverAPI.isRegistrationEnabled, - renderBasicLoginComponent: state.serverAPI.isBasicLoginEnabled, + renderResetPassword: state.serverAPI.configuration.isPasswordResetEnabled, + renderRegistrationComponent: state.serverAPI.configuration.isRegistrationEnabled, + renderBasicLoginComponent: state.serverAPI.configuration.isBasicLoginEnabled, hasEmailVerificationBeenSent: state.auth.hasEmailVerificationBeenSent, }; } diff --git a/cvat-ui/src/index.tsx b/cvat-ui/src/index.tsx index 1cd1e4ecb46a..31378940bbd9 100644 --- a/cvat-ui/src/index.tsx +++ b/cvat-ui/src/index.tsx @@ -99,8 +99,8 @@ function mapStateToProps(state: CombinedState): StateToProps { invitationsInitialized: invitations.initialized, serverAPISchemaFetching: serverAPI.fetching, serverAPISchemaInitialized: serverAPI.initialized, - isPasswordResetEnabled: serverAPI.isPasswordResetEnabled, - isRegistrationEnabled: serverAPI.isRegistrationEnabled, + isPasswordResetEnabled: serverAPI.configuration.isPasswordResetEnabled, + isRegistrationEnabled: serverAPI.configuration.isRegistrationEnabled, }; } diff --git a/cvat-ui/src/reducers/index.ts b/cvat-ui/src/reducers/index.ts index 24f258a2881c..5d926ce8f338 100644 --- a/cvat-ui/src/reducers/index.ts +++ b/cvat-ui/src/reducers/index.ts @@ -345,10 +345,12 @@ export interface ServerAPIState { schema: any; fetching: boolean; initialized: boolean; - isRegistrationEnabled: boolean; - isBasicLoginEnabled: boolean; - isPasswordResetEnabled: boolean; - isPasswordChangeEnabled: boolean; + configuration: { + isRegistrationEnabled: boolean; + isBasicLoginEnabled: boolean; + isPasswordResetEnabled: boolean; + isPasswordChangeEnabled: boolean; + }; } export interface UserAgreement { diff --git a/cvat-ui/src/reducers/server-api-reducer.ts b/cvat-ui/src/reducers/server-api-reducer.ts index 10a42c28cce4..789f00342d54 100644 --- a/cvat-ui/src/reducers/server-api-reducer.ts +++ b/cvat-ui/src/reducers/server-api-reducer.ts @@ -10,10 +10,12 @@ const defaultState: ServerAPIState = { schema: null, fetching: false, initialized: false, - isRegistrationEnabled: true, - isBasicLoginEnabled: true, - isPasswordResetEnabled: true, - isPasswordChangeEnabled: true, + configuration: { + isRegistrationEnabled: false, + isBasicLoginEnabled: false, + isPasswordResetEnabled: false, + isPasswordChangeEnabled: false, + }, }; export default function ( @@ -39,11 +41,13 @@ export default function ( ...state, fetching: false, initialized: true, - schema: action.payload.schema, - isRegistrationEnabled, - isBasicLoginEnabled, - isPasswordResetEnabled, - isPasswordChangeEnabled, + schema, + configuration: { + isRegistrationEnabled, + isBasicLoginEnabled, + isPasswordResetEnabled, + isPasswordChangeEnabled, + }, }; } case ServerAPIActionTypes.GET_SERVER_API_SCHEMA_FAILED: { From b175c800cf20031600cdebef3002bcc595a23e13 Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Wed, 17 Jan 2024 13:55:21 +0100 Subject: [PATCH 06/11] Fix defining renderChangePasswordItem --- cvat-ui/src/components/header/header.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cvat-ui/src/components/header/header.tsx b/cvat-ui/src/components/header/header.tsx index 7b8b92411869..c8b64c616c92 100644 --- a/cvat-ui/src/components/header/header.tsx +++ b/cvat-ui/src/components/header/header.tsx @@ -85,7 +85,11 @@ function mapStateToProps(state: CombinedState): StateToProps { shortcuts: { normalizedKeyMap, keyMap, visibleShortcutsHelp: shortcutsModalVisible }, settings: { showDialog: settingsModalVisible }, organizations: { fetching: organizationFetching, current: currentOrganization }, - serverAPI: { isPasswordChangeEnabled: renderChangePasswordItem }, + serverAPI: { + configuration: { + isPasswordChangeEnabled: renderChangePasswordItem, + }, + }, } = state; return { From 20cc7b5a760b4fcc61faf5341dd50d34bbb6e500 Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Mon, 22 Jan 2024 12:27:45 +0100 Subject: [PATCH 07/11] Revert changes --- cvat-ui/src/reducers/server-api-reducer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cvat-ui/src/reducers/server-api-reducer.ts b/cvat-ui/src/reducers/server-api-reducer.ts index 789f00342d54..c57604fdb109 100644 --- a/cvat-ui/src/reducers/server-api-reducer.ts +++ b/cvat-ui/src/reducers/server-api-reducer.ts @@ -11,10 +11,10 @@ const defaultState: ServerAPIState = { fetching: false, initialized: false, configuration: { - isRegistrationEnabled: false, - isBasicLoginEnabled: false, - isPasswordResetEnabled: false, - isPasswordChangeEnabled: false, + isRegistrationEnabled: true, + isBasicLoginEnabled: true, + isPasswordResetEnabled: true, + isPasswordChangeEnabled: true, }, }; From 9d62bc68306992104a1d708325558081c05a93ef Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Fri, 2 Feb 2024 13:58:06 +0100 Subject: [PATCH 08/11] Move path names to constants --- cvat/apps/iam/urls.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cvat/apps/iam/urls.py b/cvat/apps/iam/urls.py index d8d74c3e7794..8b8135fc2d9a 100644 --- a/cvat/apps/iam/urls.py +++ b/cvat/apps/iam/urls.py @@ -16,8 +16,11 @@ ConfirmEmailViewEx, LoginViewEx ) +BASIC_LOGIN_PATH_NAME = 'rest_login' +BASIC_REGISTER_PATH_NAME = 'rest_register' + urlpatterns = [ - path('login', LoginViewEx.as_view(), name='rest_login'), + path('login', LoginViewEx.as_view(), name=BASIC_LOGIN_PATH_NAME), path('logout', LogoutView.as_view(), name='rest_logout'), path('signing', SigningView.as_view(), name='signing'), path('rules', RulesView.as_view(), name='rules'), @@ -25,7 +28,7 @@ if settings.IAM_TYPE == 'BASIC': urlpatterns += [ - path('register', RegisterViewEx.as_view(), name='rest_register'), + path('register', RegisterViewEx.as_view(), name=BASIC_REGISTER_PATH_NAME), # password path('password/reset', PasswordResetView.as_view(), name='rest_password_reset'), From 80a4ada732d761fa2bd959110189c91844ed7408 Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Mon, 12 Feb 2024 10:12:31 +0100 Subject: [PATCH 09/11] Remove outdated code --- cvat-ui/src/components/cvat-app.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index 68f1922e1ca8..9c60ae5adf80 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -574,12 +574,6 @@ class CVATApplication extends React.PureComponent )} - - { routesToRender } From 1685f8a44b5bc87d4194fa1738766d229dd3b6c5 Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Mon, 12 Feb 2024 10:13:36 +0100 Subject: [PATCH 10/11] Add type for api schema --- cvat-core/src/index.ts | 2 +- cvat-core/src/server-proxy.ts | 4 ++-- cvat-core/src/server-response-types.ts | 30 ++++++++++++++++++++++++++ cvat-ui/src/actions/server-actions.ts | 4 ++-- cvat-ui/src/cvat-core-wrapper.ts | 3 ++- cvat-ui/src/reducers/index.ts | 3 ++- 6 files changed, 39 insertions(+), 7 deletions(-) diff --git a/cvat-core/src/index.ts b/cvat-core/src/index.ts index 821c0fecacfe..cbce9cb0ba5e 100644 --- a/cvat-core/src/index.ts +++ b/cvat-core/src/index.ts @@ -65,7 +65,7 @@ export default interface CVATCore { setAuthData: any; removeAuthData: any; installedApps: any; - apiSchema: any; + apiSchema: typeof serverProxy.server.apiSchema; }; assets: { create: any; diff --git a/cvat-core/src/server-proxy.ts b/cvat-core/src/server-proxy.ts index 3589fc368862..b440e81e8e60 100644 --- a/cvat-core/src/server-proxy.ts +++ b/cvat-core/src/server-proxy.ts @@ -16,7 +16,7 @@ import { SerializedAbout, SerializedRemoteFile, SerializedUserAgreement, SerializedRegister, JobsFilter, SerializedJob, SerializedGuide, SerializedAsset, SerializedQualitySettingsData, SerializedInvitationData, SerializedCloudStorage, - SerializedFramesMetaData, SerializedCollection, + SerializedFramesMetaData, SerializedCollection, SerializedAPISchema, } from './server-response-types'; import { SerializedQualityReportData } from './quality-report'; import { SerializedAnalyticsReport } from './analytics-report'; @@ -1899,7 +1899,7 @@ async function installedApps() { } } -async function getApiSchema() { +async function getApiSchema(): Promise { const { backendAPI } = config; try { diff --git a/cvat-core/src/server-response-types.ts b/cvat-core/src/server-response-types.ts index 01b4faf6eb49..d5fff1e7ce9b 100644 --- a/cvat-core/src/server-response-types.ts +++ b/cvat-core/src/server-response-types.ts @@ -344,3 +344,33 @@ export interface SerializedFramesMetaData { start_frame: number; stop_frame: number; } + +export interface SerializedAPISchema { + openapi: string; + info: { + version: string; + description: string; + termsOfService: string; + contact: { + name: string; + url: string; + email: string; + }; + license: { + name: string; + url: string; + } + }; + paths: { + [path: string]: any; + }; + components: { + schemas: { + [component: string]: any; + } + } + externalDocs: { + description: string; + url: string; + }; +} diff --git a/cvat-ui/src/actions/server-actions.ts b/cvat-ui/src/actions/server-actions.ts index b5212fb976b4..b368853d04e2 100644 --- a/cvat-ui/src/actions/server-actions.ts +++ b/cvat-ui/src/actions/server-actions.ts @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT import { ActionUnion, createAction, ThunkAction } from 'utils/redux'; -import { getCore } from 'cvat-core-wrapper'; +import { getCore, SerializedAPISchema } from 'cvat-core-wrapper'; const core = getCore(); @@ -15,7 +15,7 @@ export enum ServerAPIActionTypes { const serverAPIActions = { getServerAPISchema: () => createAction(ServerAPIActionTypes.GET_SERVER_API_SCHEMA), - getServerAPISchemaSuccess: (schema: any) => ( + getServerAPISchemaSuccess: (schema: SerializedAPISchema) => ( createAction(ServerAPIActionTypes.GET_SERVER_API_SCHEMA_SUCCESS, { schema }) ), getServerAPISchemaFailed: (error: any) => ( diff --git a/cvat-ui/src/cvat-core-wrapper.ts b/cvat-ui/src/cvat-core-wrapper.ts index e8608c9baca0..95f341d90a64 100644 --- a/cvat-ui/src/cvat-core-wrapper.ts +++ b/cvat-ui/src/cvat-core-wrapper.ts @@ -14,7 +14,7 @@ import { ModelProvider } from 'cvat-core/src/lambda-manager'; import { Label, Attribute, } from 'cvat-core/src/labels'; -import { SerializedAttribute, SerializedLabel } from 'cvat-core/src/server-response-types'; +import { SerializedAttribute, SerializedLabel, SerializedAPISchema } from 'cvat-core/src/server-response-types'; import { Job, Task } from 'cvat-core/src/session'; import Project from 'cvat-core/src/project'; import QualityReport, { QualitySummary } from 'cvat-core/src/quality-report'; @@ -106,4 +106,5 @@ export type { APIWrapperEnterOptions, QualitySummary, CVATCore, + SerializedAPISchema, }; diff --git a/cvat-ui/src/reducers/index.ts b/cvat-ui/src/reducers/index.ts index 32869732cffb..c77a709a8223 100644 --- a/cvat-ui/src/reducers/index.ts +++ b/cvat-ui/src/reducers/index.ts @@ -8,6 +8,7 @@ import { Canvas, RectDrawingMethod, CuboidDrawingMethod } from 'cvat-canvas-wrap import { Webhook, MLModel, Organization, Job, Label, User, QualityReport, QualityConflict, QualitySettings, FramesMetaData, RQStatus, EventLogger, Invitation, + SerializedAPISchema, } from 'cvat-core-wrapper'; import { IntelligentScissors } from 'utils/opencv-wrapper/intelligent-scissors'; import { KeyMap } from 'utils/mousetrap-react'; @@ -342,7 +343,7 @@ export interface AboutState { } export interface ServerAPIState { - schema: any; + schema: SerializedAPISchema | null; fetching: boolean; initialized: boolean; configuration: { From 659d1a6af623fe836ebd85f74a44c3de9363b046 Mon Sep 17 00:00:00 2001 From: Maria Khrustaleva Date: Mon, 12 Feb 2024 11:01:11 +0100 Subject: [PATCH 11/11] Update cvat-core/src/api-implementation.ts Co-authored-by: Boris Sekachev --- cvat-core/src/api-implementation.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cvat-core/src/api-implementation.ts b/cvat-core/src/api-implementation.ts index 73dfbb32f1c1..8c3388055cf3 100644 --- a/cvat-core/src/api-implementation.ts +++ b/cvat-core/src/api-implementation.ts @@ -136,10 +136,7 @@ export default function implementAPI(cvat: CVATCore): CVATCore { return result; }); - implementationMixin(cvat.server.apiSchema, async () => { - const result = await serverProxy.server.apiSchema(); - return result; - }); + implementationMixin(cvat.server.apiSchema, serverProxy.server.apiSchema); implementationMixin(cvat.assets.create, async (file: File, guideId: number): Promise => { if (!(file instanceof File)) {