diff --git a/frontend/src/AppLoader/AppLoader.jsx b/frontend/src/AppLoader/AppLoader.jsx index c54c0e3c68..a05404261c 100644 --- a/frontend/src/AppLoader/AppLoader.jsx +++ b/frontend/src/AppLoader/AppLoader.jsx @@ -1,41 +1,11 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import { withTranslation } from 'react-i18next'; import { Editor } from '../Editor/Editor'; import { RealtimeEditor } from '@/Editor/RealtimeEditor'; import config from 'config'; -import { appService } from '@/_services'; -import { useAppDataActions } from '@/_stores/appDataStore'; const AppLoaderComponent = React.memo((props) => { - const [shouldLoadApp, setShouldLoadApp] = React.useState(false); - const { updateState } = useAppDataActions(); - - useEffect(() => { - props?.id && props?.slug && loadAppDetails(props?.id); - - return () => { - setShouldLoadApp(false); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const loadAppDetails = (appId) => { - appService.fetchApp(appId, 'edit').then((data) => { - setShouldLoadApp(true); - updateState({ - app: data, - appId: data.id, - }); - }); - }; - - if (!shouldLoadApp) return <>; - - return config.ENABLE_MULTIPLAYER_EDITING ? ( - - ) : ( - - ); + return config.ENABLE_MULTIPLAYER_EDITING ? : ; }); export const AppLoader = withTranslation()(AppLoaderComponent); diff --git a/frontend/src/Editor/AppVersionsManager/AppVersionsManager.jsx b/frontend/src/Editor/AppVersionsManager/AppVersionsManager.jsx index 5db49eed9d..5e65e714de 100644 --- a/frontend/src/Editor/AppVersionsManager/AppVersionsManager.jsx +++ b/frontend/src/Editor/AppVersionsManager/AppVersionsManager.jsx @@ -1,6 +1,5 @@ -import React, { useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import cx from 'classnames'; -import { appVersionService } from '@/_services'; import { CustomSelect } from './CustomSelect'; import { toast } from 'react-hot-toast'; import { shallow } from 'zustand/shallow'; @@ -15,53 +14,8 @@ const appVersionLoadingStatus = Object.freeze({ error: 'error', }); -export const AppVersionsManager = function ({ - appId, - setAppDefinitionFromVersion, - onVersionDelete, - isEditable = true, - isViewer, -}) { - const { initializedEnvironmentDropdown, versionsPromotedToEnvironment, lazyLoadAppVersions, appVersionsLazyLoaded } = - useEnvironmentsAndVersionsStore( - (state) => ({ - appVersionsLazyLoaded: state.appVersionsLazyLoaded, - initializedEnvironmentDropdown: state.initializedEnvironmentDropdown, - versionsPromotedToEnvironment: state.versionsPromotedToEnvironment, - lazyLoadAppVersions: state.actions.lazyLoadAppVersions, - }), - shallow - ); - - if (initializedEnvironmentDropdown) { - return ( - - ); - } else { - return <>; - } -}; - -const RenderComponent = ({ - appId, - isEditable, - isViewer, - setAppDefinitionFromVersion, - onVersionDelete, - versionsPromotedToEnvironment, - lazyLoadAppVersions, - appVersionsLazyLoaded, -}) => { - const [appVersionStatus, setGetAppVersionStatus] = useState(appVersionLoadingStatus.loaded); +export const AppVersionsManager = function ({ appId, setAppDefinitionFromVersion, isEditable = true, isViewer }) { + const [appVersionStatus, setGetAppVersionStatus] = useState(appVersionLoadingStatus.loading); const [deleteVersion, setDeleteVersion] = useState({ versionId: '', versionName: '', @@ -85,6 +39,40 @@ const RenderComponent = ({ const darkMode = localStorage.getItem('darkMode') === 'true'; + const { + initializedEnvironmentDropdown, + versionsPromotedToEnvironment, + lazyLoadAppVersions, + appVersionsLazyLoaded, + setEnvironmentAndVersionsInitStatus, + changeEditorVersionAction, + selectedVersion, + deleteVersionAction, + } = useEnvironmentsAndVersionsStore( + (state) => ({ + appVersionsLazyLoaded: state.appVersionsLazyLoaded, + initializedEnvironmentDropdown: state.initializedEnvironmentDropdown, + versionsPromotedToEnvironment: state.versionsPromotedToEnvironment, + selectedVersion: state.selectedVersion, + lazyLoadAppVersions: state.actions.lazyLoadAppVersions, + setEnvironmentAndVersionsInitStatus: state.actions.setEnvironmentAndVersionsInitStatus, + deleteVersionAction: state.actions.deleteVersionAction, + changeEditorVersionAction: state.actions.changeEditorVersionAction, + }), + shallow + ); + + useEffect(() => { + setEnvironmentAndVersionsInitStatus(true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (initializedEnvironmentDropdown) { + setGetAppVersionStatus(appVersionLoadingStatus.loaded); + } + }, [initializedEnvironmentDropdown]); + const selectVersion = (id) => { const currentVersionId = useAppDataStore.getState().currentVersionId; @@ -96,15 +84,18 @@ const RenderComponent = ({ }); } - return appVersionService - .getAppVersionData(appId, id) - .then((data) => { - const isCurrentVersionReleased = data.currentVersionId ? true : false; - setAppDefinitionFromVersion(data, isCurrentVersionReleased); - }) - .catch((error) => { + changeEditorVersionAction( + appId, + id, + (newDeff) => { + setAppDefinitionFromVersion(newDeff); + }, + (error) => { toast.error(error); - }); + } + ); + + return; }; const resetDeleteModal = () => { @@ -117,25 +108,26 @@ const RenderComponent = ({ const deleteAppVersion = (versionId, versionName) => { const deleteingToastId = toast.loading('Deleting version...'); - appVersionService - .del(appId, versionId) - .then(() => { + deleteVersionAction( + appId, + versionId, + (newVersionDef) => { + if (newVersionDef) { + /* User deleted new version */ + setAppDefinitionFromVersion(newVersionDef); + } toast.dismiss(deleteingToastId); toast.success(`Version - ${versionName} Deleted`); resetDeleteModal(); - setGetAppVersionStatus(appVersionLoadingStatus.loading); - }) - .catch((error) => { + setGetAppVersionStatus(appVersionLoadingStatus.loaded); + }, + (error) => { toast.dismiss(deleteingToastId); toast.error(error?.error ?? 'Oops, something went wrong'); setGetAppVersionStatus(appVersionLoadingStatus.error); resetDeleteModal(); - }) - .finally(() => { - appVersionService.getAll(appId, true).then((data) => { - onVersionDelete(); - }); - }); + } + ); }; const options = versionsPromotedToEnvironment.map((appVersion) => ({ @@ -197,10 +189,28 @@ const RenderComponent = ({ resetDeleteModal, }; + /* Force close is not working with usual blur function of react-select */ + const clickedOutsideRef = useRef(null); + useEffect(() => { + function handleClickOutside(event) { + if (clickedOutsideRef.current && !clickedOutsideRef.current.contains(event.target)) { + if (!forceMenuOpen) { + setForceMenuOpen(false); + } + } + } + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [clickedOutsideRef]); + return (
selectVersion(id)} {...customSelectProps} className={` ${darkMode && 'dark-theme'}`} diff --git a/frontend/src/Editor/AppVersionsManager/CreateVersionModal.jsx b/frontend/src/Editor/AppVersionsManager/CreateVersionModal.jsx index 2801f278b0..07c67506d5 100644 --- a/frontend/src/Editor/AppVersionsManager/CreateVersionModal.jsx +++ b/frontend/src/Editor/AppVersionsManager/CreateVersionModal.jsx @@ -6,17 +6,25 @@ import { useTranslation } from 'react-i18next'; import Select from '@/_ui/Select'; import { useAppVersionStore } from '@/_stores/appVersionStore'; import { shallow } from 'zustand/shallow'; +import { useEnvironmentsAndVersionsStore } from '@/_stores/environmentsAndVersionsStore'; export const CreateVersion = ({ appId, - appVersions, - setAppVersions, setAppDefinitionFromVersion, showCreateAppVersion, setShowCreateAppVersion, }) => { const [isCreatingVersion, setIsCreatingVersion] = useState(false); const [versionName, setVersionName] = useState(''); + const { versionsPromotedToEnvironment: appVersions, createNewVersionAction } = useEnvironmentsAndVersionsStore( + (state) => ({ + appVersionsLazyLoaded: state.appVersionsLazyLoaded, + versionsPromotedToEnvironment: state.versionsPromotedToEnvironment, + lazyLoadAppVersions: state.actions.lazyLoadAppVersions, + createNewVersionAction: state.actions.createNewVersionAction, + }), + shallow + ); const { t } = useTranslation(); const { editingVersion } = useAppVersionStore( @@ -46,30 +54,29 @@ export const CreateVersion = ({ setIsCreatingVersion(true); - appVersionService - .create(appId, versionName, selectedVersion.id) - .then((data) => { + createNewVersionAction( + appId, + versionName, + selectedVersion.id, + (newVersion) => { toast.success('Version Created'); - appVersionService.getAll(appId).then((data) => { - setVersionName(''); - setIsCreatingVersion(false); - setAppVersions(data.versions); - setShowCreateAppVersion(false); - }); - + setVersionName(''); + setIsCreatingVersion(false); + setShowCreateAppVersion(false); appVersionService - .getAppVersionData(appId, data.id) + .getAppVersionData(appId, newVersion.id) .then((data) => { setAppDefinitionFromVersion(data); }) .catch((error) => { toast.error(error); }); - }) - .catch((error) => { + }, + (error) => { toast.error(error?.error); setIsCreatingVersion(false); - }); + } + ); }; return ( diff --git a/frontend/src/Editor/AppVersionsManager/EditVersionModal.jsx b/frontend/src/Editor/AppVersionsManager/EditVersionModal.jsx index e7e91a5dac..1ba1a30994 100644 --- a/frontend/src/Editor/AppVersionsManager/EditVersionModal.jsx +++ b/frontend/src/Editor/AppVersionsManager/EditVersionModal.jsx @@ -1,19 +1,19 @@ import React, { useState } from 'react'; -import { appVersionService } from '@/_services'; import AlertDialog from '@/_ui/AlertDialog'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; +import { shallow } from 'zustand/shallow'; +import { useEnvironmentsAndVersionsStore } from '@/_stores/environmentsAndVersionsStore'; -export const EditVersion = ({ - appId, - value: editingVersionId, - setAppVersions, - setShowEditAppVersion, - showEditAppVersion, - appVersions, -}) => { +export const EditVersion = ({ appId, setShowEditAppVersion, showEditAppVersion }) => { const [isEditingVersion, setIsEditingVersion] = useState(false); - const editingVersion = appVersions?.find((version) => version.id === editingVersionId); + const { updateVersionNameAction, selectedVersion: editingVersion } = useEnvironmentsAndVersionsStore( + (state) => ({ + updateVersionNameAction: state.actions.updateVersionNameAction, + selectedVersion: state.selectedVersion, + }), + shallow + ); const [versionName, setVersionName] = useState(editingVersion?.name || ''); const { t } = useTranslation(); @@ -28,21 +28,20 @@ export const EditVersion = ({ } setIsEditingVersion(true); - appVersionService - .save(appId, editingVersionId, { name: versionName }) - .then(() => { + updateVersionNameAction( + appId, + editingVersion?.id, + versionName, + () => { toast.success('Version name updated'); - appVersionService.getAll(appId).then((data) => { - const versions = data.versions; - setAppVersions(versions); - }); setIsEditingVersion(false); setShowEditAppVersion(false); - }) - .catch((error) => { + }, + (error) => { setIsEditingVersion(false); toast.error(error?.error); - }); + } + ); }; return ( diff --git a/frontend/src/Editor/CommentNotifications/index.jsx b/frontend/src/Editor/CommentNotifications/index.jsx index f2eda8323e..63f7969b1e 100644 --- a/frontend/src/Editor/CommentNotifications/index.jsx +++ b/frontend/src/Editor/CommentNotifications/index.jsx @@ -34,7 +34,6 @@ const CommentNotifications = ({ socket, pageId }) => { async function fetchData(selectedKey) { if (appId) { - console.log('inside-CommentNotifications', appId); const isResolved = selectedKey === 'resolved'; setLoading(true); const { data } = await commentsService.getNotifications(appId, isResolved, appVersionsId, pageId); diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx index 2dbbd42d3f..b4666b2eab 100644 --- a/frontend/src/Editor/Editor.jsx +++ b/frontend/src/Editor/Editor.jsx @@ -88,6 +88,7 @@ import { useResolveStore } from '@/_stores/resolverStore'; import { dfs } from '@/_stores/handleReferenceTransactions'; import { decimalToHex } from './editorConstants'; import { findComponentsWithReferences, handleLowPriorityWork } from '@/_helpers/editorHelpers'; +import { TJLoader } from '@/_ui/TJLoader/TJLoader'; setAutoFreeze(false); enablePatches(); @@ -491,7 +492,9 @@ const EditorComponent = (props) => { const initEventListeners = () => { socket?.addEventListener('message', (event) => { const data = event.data.replace(/^"(.+(?="$))"$/, '$1'); - if (data === 'versionReleased') fetchApp(); + if (data === 'versionReleased') { + //TODO update the released version id + } }); }; @@ -500,7 +503,7 @@ const EditorComponent = (props) => { useResolveStore.getState().actions.updateJSHints(); - await fetchApp(props.params.pageHandle, true); + await runForInitialLoad(); await fetchOrgEnvironmentVariables(); await fetchOrgEnvironmentConstants(); initComponentVersioning(); @@ -534,10 +537,6 @@ const EditorComponent = (props) => { useDataSourcesStore.getState().actions.fetchGlobalDataSources(organizationId); }; - const onVersionDelete = () => { - fetchApp(props.params.pageHandle); - }; - const toggleAppMaintenance = () => { const newState = !isMaintenanceOn; @@ -698,28 +697,56 @@ const EditorComponent = (props) => { }); }; - const callBack = async (data, startingPageHandle, versionSwitched = false) => { - setWindowTitle({ page: pageTitles.EDITOR, appName: data.name }); - useAppVersionStore.getState().actions.updateEditingVersion(data.editing_version); - if (!releasedVersionId || !versionSwitched) { - useAppVersionStore.getState().actions.updateReleasedVersionId(data.current_version_id); - } - - const currentOrgId = data?.organization_id || data?.organizationId; + /* Only for the first load of an app. Should not use for any other cases */ + const runForInitialLoad = async () => { + const appId = props.id; + const appData = await appService.fetchApp(appId); + const { + name: appName, + current_version_id, + editing_version, + organization_id: organizationId, + slug, + is_maintenance_on: isMaintenanceOn, + is_public: isPublic, + user_id: userId, + events, + } = appData; + const startingPageHandle = props.params.pageHandle; + setWindowTitle({ page: pageTitles.EDITOR, appName }); + useAppVersionStore.getState().actions.updateEditingVersion(editing_version); + current_version_id && useAppVersionStore.getState().actions.updateReleasedVersionId(current_version_id); updateState({ - slug: data.slug, - isMaintenanceOn: data?.is_maintenance_on, - organizationId: currentOrgId, - isPublic: data?.is_public || data?.isPublic, - appName: data?.name, - userId: data?.user_id, - appId: data?.id, - events: data.events, - currentVersionId: data?.editing_version?.id, - app: data, + slug, + isMaintenanceOn, + organizationId, + isPublic, + appName, + userId, + appId, + events, + currentVersionId: editing_version?.id, + app: appData, }); + await processNewAppDefinition(appData, startingPageHandle, false, ({ homePageId }) => { + handleLowPriorityWork(async () => { + await useDataSourcesStore.getState().actions.fetchGlobalDataSources(organizationId); + await fetchDataSources(editing_version?.id); + commonLowPriorityActions(events, { homePageId }); + }); + }); + }; + + const commonLowPriorityActions = async (events, { homePageId }) => { + const currentPageEvents = events.filter((event) => event.target === 'page' && event.sourceId === homePageId); + const editorRef = getEditorRef(); + await runQueries(useDataQueriesStore.getState().dataQueries, editorRef, true); + await handleEvent('onPageLoad', currentPageEvents, {}, true); + }; + + const processNewAppDefinition = async (data, startingPageHandle, versionSwitched = false, onComplete) => { const appDefData = buildAppDefinition(data); const appJson = appDefData; @@ -756,39 +783,17 @@ const EditorComponent = (props) => { }); } - Promise.all([ - await useDataSourcesStore.getState().actions.fetchGlobalDataSources(data?.organization_id), - await fetchDataSources(data.editing_version?.id), - await fetchDataQueries(data.editing_version?.id, true, true), - ]) + Promise.all([await fetchDataQueries(data.editing_version?.id, true, true)]) .then(async () => { await onEditorLoad(appJson, homePageId, versionSwitched); updateEntityReferences(appJson, homePageId); }) .finally(async () => { - const currentPageEvents = data.events.filter( - (event) => event.target === 'page' && event.sourceId === homePageId - ); - - const editorRef = getEditorRef(); - - handleLowPriorityWork(async () => { - await runQueries(useDataQueriesStore.getState().dataQueries, editorRef, true); - await handleEvent('onPageLoad', currentPageEvents, {}, true); - }); + const funcParams = { homePageId }; + typeof onComplete === 'function' && (await onComplete(funcParams)); }); }; - const fetchApp = async (startingPageHandle, onMount = false) => { - const _appId = props?.params?.id || props?.params?.slug; - - if (!onMount) { - await appService.fetchApp(_appId).then((data) => callBack(data, startingPageHandle)); - } else { - callBack(app, startingPageHandle); - } - }; - const setAppDefinitionFromVersion = (appData) => { const version = appData?.editing_version?.id; if (version?.id !== editingVersionId) { @@ -802,11 +807,24 @@ const EditorComponent = (props) => { updateEditorState({ isLoading: true, }); + useCurrentStateStore.getState().actions.setCurrentState({}); useCurrentStateStore.getState().actions.setEditorReady(false); useResolveStore.getState().actions.resetStore(); - callBack(appData, null, true); + const { editing_version } = appData; + useAppVersionStore.getState().actions.updateEditingVersion(editing_version); + updateState({ + events, + currentVersionId: editing_version?.id, + app: appData, + }); + processNewAppDefinition(appData, null, true, ({ homePageId }) => { + handleLowPriorityWork(async () => { + await fetchDataSources(editing_version?.id); + commonLowPriorityActions(events, homePageId); + }); + }); initComponentVersioning(); } }; @@ -1912,29 +1930,7 @@ const EditorComponent = (props) => { const isEditorReady = useCurrentStateStore((state) => state.isEditorReady); if (isLoading && !isEditorReady) { - return ( -
-
-
-
-
-
- -
- {Array.from(Array(4)).map((_item, index) => ( - - ))} -
- -
- - -
-
-
-
-
- ); + return ; } return ( @@ -1970,7 +1966,6 @@ const EditorComponent = (props) => { setAppDefinitionFromVersion={setAppDefinitionFromVersion} onVersionRelease={onVersionRelease} saveEditingVersion={saveEditingVersion} - onVersionDelete={onVersionDelete} isMaintenanceOn={isMaintenanceOn} appName={appName} appId={appId} diff --git a/frontend/src/Editor/Header/RightTopHeaderButtons/EnvironmentManager/EnvironmentsManager.jsx b/frontend/src/Editor/Header/EnvironmentManager/EnvironmentsManager.jsx similarity index 53% rename from frontend/src/Editor/Header/RightTopHeaderButtons/EnvironmentManager/EnvironmentsManager.jsx rename to frontend/src/Editor/Header/EnvironmentManager/EnvironmentsManager.jsx index e8a116e077..73571d6320 100644 --- a/frontend/src/Editor/Header/RightTopHeaderButtons/EnvironmentManager/EnvironmentsManager.jsx +++ b/frontend/src/Editor/Header/EnvironmentManager/EnvironmentsManager.jsx @@ -1,4 +1,4 @@ -import { useEnvironmentsAndVersionsActions } from '@/_stores/environmentsAndVersionsStore'; +import { useEnvironmentsAndVersionsStore } from '@/_stores/environmentsAndVersionsStore'; import React, { useEffect } from 'react'; import { shallow } from 'zustand/shallow'; import { useAppVersionStore } from '@/_stores/appVersionStore'; @@ -10,9 +10,17 @@ const EnvironmentManager = () => { }), shallow ); - const { init, setEnvironmentDropdownStatus } = useEnvironmentsAndVersionsActions(); + const { init, setEnvironmentDropdownStatus, initializedEnvironmentDropdown } = useEnvironmentsAndVersionsStore( + (state) => ({ + initializedEnvironmentDropdown: state.initializedEnvironmentDropdown, + init: state.actions.init, + setEnvironmentDropdownStatus: state.actions.setEnvironmentDropdownStatus, + }), + shallow + ); + useEffect(() => { - initComponent(); + !initializedEnvironmentDropdown && initComponent(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -21,7 +29,7 @@ const EnvironmentManager = () => { setEnvironmentDropdownStatus(true); }; - return
; + return <>; }; export default EnvironmentManager; diff --git a/frontend/src/Editor/Header/RightTopHeaderButtons/EnvironmentManager/index.js b/frontend/src/Editor/Header/EnvironmentManager/index.js similarity index 100% rename from frontend/src/Editor/Header/RightTopHeaderButtons/EnvironmentManager/index.js rename to frontend/src/Editor/Header/EnvironmentManager/index.js diff --git a/frontend/src/Editor/Header/RightTopHeaderButtons/ReleaseVersionButton.jsx b/frontend/src/Editor/Header/RightTopHeaderButtons/ReleaseVersionButton.jsx index 3c72c32a89..198bf3926f 100644 --- a/frontend/src/Editor/Header/RightTopHeaderButtons/ReleaseVersionButton.jsx +++ b/frontend/src/Editor/Header/RightTopHeaderButtons/ReleaseVersionButton.jsx @@ -24,10 +24,10 @@ export const ReleaseVersionButton = function DeployVersionButton({ onVersionRele setShowPageDeletionConfirmation(false); setIsReleasing(true); - const { id: versionToBeReleased, name, app_id } = editingVersion; + const { id: versionToBeReleased, name, app_id, appId } = editingVersion; appsService - .releaseVersion(app_id, versionToBeReleased) + .releaseVersion(app_id || appId, versionToBeReleased) .then(() => { toast(`Version ${name} released`, { icon: '🚀', diff --git a/frontend/src/Editor/Header/index.js b/frontend/src/Editor/Header/index.js index 51696634b4..9b089b682a 100644 --- a/frontend/src/Editor/Header/index.js +++ b/frontend/src/Editor/Header/index.js @@ -16,7 +16,7 @@ import queryString from 'query-string'; import { isEmpty } from 'lodash'; import LogoNavDropdown from '@/_components/LogoNavDropdown'; import RightTopHeaderButtons from './RightTopHeaderButtons'; -import EnvironmentManager from './RightTopHeaderButtons/EnvironmentManager'; +import EnvironmentManager from './EnvironmentManager'; export default function EditorHeader({ M, @@ -28,7 +28,6 @@ export default function EditorHeader({ onNameChanged, setAppDefinitionFromVersion, onVersionRelease, - onVersionDelete, slug, darkMode, }) { @@ -149,7 +148,6 @@ export default function EditorHeader({ )} diff --git a/frontend/src/Editor/LeftSidebar/SidebarComment.jsx b/frontend/src/Editor/LeftSidebar/SidebarComment.jsx index 3c64014eb2..7be1378c8d 100644 --- a/frontend/src/Editor/LeftSidebar/SidebarComment.jsx +++ b/frontend/src/Editor/LeftSidebar/SidebarComment.jsx @@ -30,13 +30,14 @@ export const LeftSidebarComment = forwardRef(({ selectedSidebarItem, currentPage const [notifications, setNotifications] = React.useState([]); React.useEffect(() => { - if (appVersionsId && appId) { + if (isActive) { commentsService.getNotifications(appId, false, appVersionsId, currentPageId).then(({ data }) => { setNotifications(data); }); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [appVersionsId, currentPageId, appId]); + }, [isActive]); + return ( 0} diff --git a/frontend/src/Editor/Viewer/PreviewSettings.jsx b/frontend/src/Editor/Viewer/PreviewSettings.jsx index 3ec6acbf29..6218813833 100644 --- a/frontend/src/Editor/Viewer/PreviewSettings.jsx +++ b/frontend/src/Editor/Viewer/PreviewSettings.jsx @@ -10,6 +10,7 @@ import Navbar from 'react-bootstrap/Navbar'; import Offcanvas from 'react-bootstrap/Offcanvas'; import 'bootstrap/dist/css/bootstrap.min.css'; import classNames from 'classnames'; +import EnvironmentManager from '@/Editor/Header/EnvironmentManager'; const PreviewSettings = ({ isMobileLayout, setAppDefinitionFromVersion, showHeader, darkMode }) => { const { editingVersion } = useAppVersionStore((state) => ({ @@ -23,6 +24,7 @@ const PreviewSettings = ({ isMobileLayout, setAppDefinitionFromVersion, showHead
Preview settings + {editingVersion && ( + {editingVersion && ( ({ + organizationList: state.organizations, + isGettingOrganizations: state.isGettingOrganizations, + fetchOrganizations: state.actions.fetchOrganizations, + }), + shallow + ); const darkMode = localStorage.getItem('darkMode') === 'true'; useEffect(() => { - setGetOrgStatus('loading'); - const sessionObservable = authenticationService.currentSession.subscribe((newSession) => { - setOrganizationList(newSession.organizations ?? []); - if (newSession.organizations?.length > 0) setGetOrgStatus('success'); - }); - - () => sessionObservable.unsubscribe(); + fetchOrganizations(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const switchOrganization = (id) => { @@ -54,7 +61,7 @@ export const OrganizationList = function () { return (
switchOrganization(id)} diff --git a/frontend/src/_helpers/authorizeWorkspace.js b/frontend/src/_helpers/authorizeWorkspace.js index 1f8e0a0298..5dc9ad7711 100644 --- a/frontend/src/_helpers/authorizeWorkspace.js +++ b/frontend/src/_helpers/authorizeWorkspace.js @@ -85,16 +85,6 @@ const isThisExistedRoute = () => { return pathnames?.length > 0 ? (checkPath() ? true : false) : false; }; -const fetchOrganizations = (current_organization_id, callback) => { - organizationService.getOrganizations().then((response) => { - const current_organization = response.organizations?.find((org) => org.id === current_organization_id); - callback({ - organizations: response.organizations, - current_organization, - }); - }); -}; - const isThisWorkspaceLoginPage = (justLoginPage = false) => { const subpath = getSubpath(); const pathname = location.pathname.replace(subpath, ''); @@ -102,7 +92,7 @@ const isThisWorkspaceLoginPage = (justLoginPage = false) => { return (justLoginPage && pathnames[0] === 'login') || (pathnames.length === 2 && pathnames[0] === 'login'); }; -const updateCurrentSession = (newSession) => { +export const updateCurrentSession = (newSession) => { const currentSession = authenticationService.currentSessionValue; authenticationService.updateCurrentSession({ ...currentSession, ...newSession }); }; @@ -125,16 +115,12 @@ export const authorizeUserAndHandleErrors = (workspace_id, workspace_slug) => { .authorize() .then((data) => { /* CASE-1 */ - const { current_organization_id } = data; - fetchOrganizations(current_organization_id, ({ organizations, current_organization }) => { - const { name: current_organization_name } = current_organization; - /* add the user details like permission and user previlliage details to the subject */ - updateCurrentSession({ - ...data, - current_organization_name, - organizations, - load_app: true, - }); + const { current_organization_name } = data; + /* add the user details like permission and user previlliage details to the subject */ + updateCurrentSession({ + ...data, + current_organization_name, + load_app: true, }); }) .catch((error) => { @@ -148,7 +134,7 @@ export const authorizeUserAndHandleErrors = (workspace_id, workspace_slug) => { /* get current session's workspace id */ authenticationService .validateSession() - .then(({ current_organization_id }) => { + .then(({ current_organization_id, ...restSessionData }) => { /* change current organization id to valid one [current logged in organization] */ updateCurrentSession({ current_organization_id, @@ -160,20 +146,17 @@ export const authorizeUserAndHandleErrors = (workspace_id, workspace_slug) => { authorizeUserAndHandleErrors(unauthorized_organization_id); }) .catch(() => { - /* CASE-3 */ - fetchOrganizations(current_organization_id, ({ current_organization }) => { - const { name: current_organization_name, slug: current_organization_slug } = current_organization; - updateCurrentSession({ - current_organization_name, - current_organization_slug, - load_app: true, - }); - - if (!isThisWorkspaceLoginPage()) - return (window.location = `${ - subpath ?? '' - }/login/${unauthorized_organization_slug}?redirectTo=${getRedirectToWithParams()}`); + const { current_organization_name, current_organization_slug } = restSessionData; + updateCurrentSession({ + current_organization_name, + current_organization_slug, + load_app: true, }); + + if (!isThisWorkspaceLoginPage()) + return (window.location = `${ + subpath ?? '' + }/login/${unauthorized_organization_slug}?redirectTo=${getRedirectToWithParams()}`); }); }) /* CASE-3 */ diff --git a/frontend/src/_helpers/websocketConnection.js b/frontend/src/_helpers/websocketConnection.js index a8ca670323..837ecf0d28 100644 --- a/frontend/src/_helpers/websocketConnection.js +++ b/frontend/src/_helpers/websocketConnection.js @@ -1,10 +1,18 @@ import config from 'config'; class WebSocketConnection { - constructor(appId) { - this.socket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${this.getWebsocketUrl()}`); + static instance; + static appId; + constructor(appId) { + if (WebSocketConnection.instance && WebSocketConnection.appId === appId) { + return WebSocketConnection.instance; + } + + this.socket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${this.getWebsocketUrl()}`); this.addListeners(appId); + + WebSocketConnection.instance = this; } getWebsocketUrl() { diff --git a/frontend/src/_services/app_environment.service.js b/frontend/src/_services/app_environment.service.js index 411ef368ca..9f6b5286c8 100644 --- a/frontend/src/_services/app_environment.service.js +++ b/frontend/src/_services/app_environment.service.js @@ -6,6 +6,7 @@ export const appEnvironmentService = { getAllEnvironments, getVersionsByEnvironment, init, + postVersionDeleteAction, }; function getAllEnvironments() { @@ -36,3 +37,13 @@ function init(editing_version_id = null) { const query = queryString.stringify({ editing_version_id }); return fetch(`${config.apiUrl}/app-environments/init?${query}`, requestOptions).then(handleResponse); } + +function postVersionDeleteAction(actionParams = {}) { + const requestOptions = { + method: 'POST', + body: JSON.stringify(actionParams), + headers: authHeader(), + credentials: 'include', + }; + return fetch(`${config.apiUrl}/app-environments/post-action/version_deleted`, requestOptions).then(handleResponse); +} diff --git a/frontend/src/_stores/currentSessionStore.js b/frontend/src/_stores/currentSessionStore.js new file mode 100644 index 0000000000..f62d974346 --- /dev/null +++ b/frontend/src/_stores/currentSessionStore.js @@ -0,0 +1,32 @@ +import create from 'zustand'; +import { zustandDevTools } from './utils'; +import { organizationService } from '@/_services'; + +const initialState = { + organizations: [], + isGettingOrganizations: false, +}; + +//TODO: migrate all rxjs functions to zustand in future +export const useCurrentSessionStore = create( + zustandDevTools( + (set, get) => ({ + ...initialState, + actions: { + fetchOrganizations: async () => { + if (get().isGettingOrganizations || get().organizations.length) return; + try { + set({ isGettingOrganizations: true }); + const response = await organizationService.getOrganizations(); + set({ organizations: response.organizations, isGettingOrganizations: false }); + } catch (error) { + console.error('Error while fetching organizations', error); + } + }, + }, + }), + { name: 'Current Session Store' } + ) +); + +export const useCurrentSessionStoreActions = () => useCurrentSessionStore((state) => state.actions); diff --git a/frontend/src/_stores/environmentsAndVersionsStore.js b/frontend/src/_stores/environmentsAndVersionsStore.js index 80c9cba59c..56af559edc 100644 --- a/frontend/src/_stores/environmentsAndVersionsStore.js +++ b/frontend/src/_stores/environmentsAndVersionsStore.js @@ -1,6 +1,6 @@ import create from 'zustand'; import { zustandDevTools } from './utils'; -import { appEnvironmentService } from '../_services/app_environment.service'; +import { appEnvironmentService, appVersionService } from '@/_services'; const initialState = { selectedVersion: null, @@ -11,7 +11,6 @@ const initialState = { shouldRenderPromoteButton: false, shouldRenderReleaseButton: false, initializedEnvironmentDropdown: false, - initializedVersionsDropdown: false, environmentsLazyLoaded: false, appVersionsLazyLoaded: false, }; @@ -31,7 +30,7 @@ export const useEnvironmentsAndVersionsStore = create( appVersionEnvironment: response.appVersionEnvironment, shouldRenderPromoteButton: response.shouldRenderPromoteButton, shouldRenderReleaseButton: response.shouldRenderReleaseButton, - environments: [response.editorEnvironment], + environments: response.environments, versionsPromotedToEnvironment: [response.editorVersion], })); } catch (error) { @@ -51,6 +50,95 @@ export const useEnvironmentsAndVersionsStore = create( console.error('Error while getting the versions', error); } }, + setSelectedVersion: (selectedVersion) => set({ selectedVersion }), + setEnvironmentAndVersionsInitStatus: (state) => set({ completedEnvironmentAndVersionsInit: state }), + createNewVersionAction: async (appId, versionName, selectedVersionId, onSuccess, onFailure) => { + try { + const newVersion = await appVersionService.create(appId, versionName, selectedVersionId); + const editorVersion = { + id: newVersion.id, + name: newVersion.name, + current_environment_id: newVersion.current_environment_id, + }; + set((state) => ({ + ...state, + selectedVersion: editorVersion, + versionsPromotedToEnvironment: [editorVersion], + appVersionsLazyLoaded: false, + })); + onSuccess(newVersion); + } catch (error) { + onFailure(error); + } + }, + updateVersionNameAction: async (appId, versionId, versionName, onSuccess, onFailure) => { + try { + await appVersionService.save(appId, versionId, { name: versionName }); + const selectedVersion = get().selectedVersion; + selectedVersion.name = versionName; + set((state) => ({ + ...state, + selectedVersion, + versionsPromotedToEnvironment: [selectedVersion], + appVersionsLazyLoaded: false, + })); + onSuccess(); + } catch (error) { + onFailure(error); + } + }, + deleteVersionAction: async (appId, versionId, onSuccess, onFailure) => { + try { + await appVersionService.del(appId, versionId); + const isCurrentVersion = get().selectedVersion.id === versionId; + let optionsToUpdate = { + appVersionsLazyLoaded: false, + }; + if (isCurrentVersion) { + /* User is deleted the editor version (currently selected). */ + const response = await appEnvironmentService.postVersionDeleteAction({ + appId, + editorVersionId: versionId, + deletedVersionId: versionId, + }); + const selectedVersion = response.editorVersion; + optionsToUpdate = { + ...optionsToUpdate, + selectedVersion, + selectedEnvironment: response.editorEnvironment, + appVersionEnvironment: response.appVersionEnvironment, + shouldRenderPromoteButton: response.shouldRenderPromoteButton, + shouldRenderReleaseButton: response.shouldRenderReleaseButton, + }; + } + set((state) => ({ + ...state, + ...optionsToUpdate, + })); + let newVersionDef; + const newEditorVersion = optionsToUpdate.selectedVersion; + if (newEditorVersion) { + newVersionDef = await appVersionService.getAppVersionData(appId, newEditorVersion.id); + } + onSuccess(newVersionDef); + } catch (error) { + onFailure(error); + } + }, + changeEditorVersionAction: async (appId, versionId, onSuccess, onFailure) => { + try { + const data = await appVersionService.getAppVersionData(appId, versionId); + const selectedVersion = { + id: data.editing_version.id, + name: data.editing_version.name, + current_environment_id: data.editing_version.currentEnvironmentId, + }; + set((state) => ({ ...state, selectedVersion })); + onSuccess(data); + } catch (error) { + onFailure(error); + } + }, }, }), { name: 'App Version Manager Store' } diff --git a/server/src/controllers/app.controller.ts b/server/src/controllers/app.controller.ts index 721a4fa277..2c1993f6c5 100644 --- a/server/src/controllers/app.controller.ts +++ b/server/src/controllers/app.controller.ts @@ -78,7 +78,7 @@ export class AppController { currentOrganization = organization; } - return this.authService.generateSessionPayload(user, currentOrganization); + return await this.authService.generateSessionPayload(user, currentOrganization); } @UseGuards(JwtAuthGuard) diff --git a/server/src/controllers/app_environments.controller.ts b/server/src/controllers/app_environments.controller.ts index 50f8dbc674..b15a9fc199 100644 --- a/server/src/controllers/app_environments.controller.ts +++ b/server/src/controllers/app_environments.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common'; +import { Body, Controller, Get, Param, Post, Query, UseGuards } from '@nestjs/common'; import { decamelizeKeys } from 'humps'; import { JwtAuthGuard } from '../modules/auth/jwt-auth.guard'; import { ForbiddenException } from '@nestjs/common'; @@ -8,6 +8,7 @@ import { GlobalDataSourceAbilityFactory } from 'src/modules/casl/abilities/globa import { DataSource } from 'src/entities/data_source.entity'; import { OrgEnvironmentVariablesAbilityFactory } from 'src/modules/casl/abilities/org-environment-variables-ability.factory'; import { OrgEnvironmentVariable } from 'src/entities/org_envirnoment_variable.entity'; +import { AppEnvironmentActionParametersDto } from '@dto/environment_action_parameters.dto'; @Controller('app-environments') export class AppEnvironmentsController { @@ -24,7 +25,20 @@ export class AppEnvironmentsController { init is a method in the AppEnvironmentService class that is used to initialize the app environment mananger. Should not use for any other purpose. */ - return await this.appEnvironmentServices.init(editingVersionId); + return await this.appEnvironmentServices.init(editingVersionId, user.organizationId); + } + + @UseGuards(JwtAuthGuard) + @Post('/post-action/:action') + async environmentActions( + @Param('action') action: string, + @Body() appEnvironmentActionParametersDto: AppEnvironmentActionParametersDto + ) { + /* + init is a method in the AppEnvironmentService class that is used to initialize the app environment mananger. + Should not use for any other purpose. + */ + return await this.appEnvironmentServices.processActions(action, appEnvironmentActionParametersDto); } @UseGuards(JwtAuthGuard) diff --git a/server/src/dto/environment_action_parameters.dto.ts b/server/src/dto/environment_action_parameters.dto.ts new file mode 100644 index 0000000000..a3994c309e --- /dev/null +++ b/server/src/dto/environment_action_parameters.dto.ts @@ -0,0 +1,19 @@ +import { IsOptional, IsUUID } from 'class-validator'; + +export class AppEnvironmentActionParametersDto { + @IsOptional() + @IsUUID() + editorEnvironmentId: string; + + @IsOptional() + @IsUUID() + editorVersionId: string; + + @IsOptional() + @IsUUID() + deletedVersionId: string; + + @IsOptional() + @IsUUID() + appId: string; +} diff --git a/server/src/services/app_environments.service.ts b/server/src/services/app_environments.service.ts index 635dddc7d2..1a01afa28e 100644 --- a/server/src/services/app_environments.service.ts +++ b/server/src/services/app_environments.service.ts @@ -6,6 +6,12 @@ import { OrgEnvironmentConstantValue } from 'src/entities/org_environment_consta import { OrganizationConstant } from 'src/entities/organization_constants.entity'; import { EntityManager, FindOneOptions, In, DeleteResult } from 'typeorm'; import { AppVersion } from 'src/entities/app_version.entity'; +import { AppEnvironmentActionParametersDto } from '@dto/environment_action_parameters.dto'; +import { App } from 'src/entities/app.entity'; + +interface ExtendedEnvironment extends AppEnvironment { + appVersionsCount?: number; +} export interface AppEnvironmentResponse { editorVersion: Partial; @@ -13,37 +19,184 @@ export interface AppEnvironmentResponse { appVersionEnvironment: AppEnvironment; shouldRenderPromoteButton: boolean; shouldRenderReleaseButton: boolean; + environments: ExtendedEnvironment[]; +} + +export enum AppEnvironmentActions { + VERSION_DELETED = 'version_deleted', + ENVIROMENT_CHANGED = 'environment_changed', } @Injectable() export class AppEnvironmentService { - async init(editingVersionId: string, manager?: EntityManager): Promise { + async init( + editingVersionId: string, + organizationId: string, + manager?: EntityManager + ): Promise { return await dbTransactionWrap(async (manager: EntityManager) => { const editorVersion = await manager.findOne(AppVersion, { - select: ['id', 'name', 'currentEnvironmentId'], + select: ['id', 'name', 'currentEnvironmentId', 'appId'], where: { id: editingVersionId }, }); - const editorEnvironment = await manager.findOne(AppEnvironment, { id: editorVersion.currentEnvironmentId }); - const { shouldRenderPromoteButton, shouldRenderReleaseButton } = - this.calculateButtonVisibility(editorEnvironment); + const environments: ExtendedEnvironment[] = await this.getAll(organizationId, manager, editorVersion.appId); + const editorEnvironment = environments.find((env) => env.id === editorVersion.currentEnvironmentId); + const { shouldRenderPromoteButton, shouldRenderReleaseButton } = await this.calculateButtonVisibility( + false, + editorEnvironment, + editorVersion.appId, + editorVersion.id, + manager + ); const response: AppEnvironmentResponse = { editorVersion, editorEnvironment, appVersionEnvironment: editorEnvironment, shouldRenderPromoteButton, shouldRenderReleaseButton, + environments, }; return response; }, manager); } - calculateButtonVisibility(appVersionEnvironment: AppEnvironment) { + async calculateButtonVisibility( + isMultiEnvironmentEnabled: boolean, + appVersionEnvironment?: AppEnvironment, + appId?: string, + versionId?: string, + manager?: EntityManager + ) { /* Further conditions can handle from here */ - const shouldRenderPromoteButton = false; - const shouldRenderReleaseButton = true; + if (!isMultiEnvironmentEnabled) { + return { + shouldRenderPromoteButton: false, + shouldRenderReleaseButton: true, + }; + } + const appDetails = await manager.findOneOrFail(App, { + select: ['id', 'currentVersionId'], + where: { id: appId }, + }); + const isVersionReleased = appDetails.currentVersionId === versionId; + const isCurrentVersionInProduction = appVersionEnvironment?.isDefault; + const shouldRenderPromoteButton = !isCurrentVersionInProduction && !isVersionReleased; + const shouldRenderReleaseButton = isCurrentVersionInProduction || isVersionReleased; return { shouldRenderPromoteButton, shouldRenderReleaseButton }; } + async getSelectedVersion(selectedEnvironmentId: string, appId: string, manager?: EntityManager): Promise { + const newVersionQuery = ` + SELECT name, id, current_environment_id + FROM app_versions + WHERE current_environment_id IN ( + SELECT id + FROM app_environments + WHERE priority >= ( + SELECT priority + FROM app_environments + WHERE id = $1 + ) + ) + AND app_id = $2 + ORDER BY updated_at DESC + LIMIT 1; + `; + const result = await manager.query(newVersionQuery, [selectedEnvironmentId, appId]); + return result[0]; + } + + async processActions(action: string, actionParameters: AppEnvironmentActionParametersDto) { + const { editorEnvironmentId, deletedVersionId, editorVersionId, appId } = actionParameters; + + return await dbTransactionWrap(async (manager: EntityManager) => { + switch (action) { + case AppEnvironmentActions.VERSION_DELETED: { + const appEnvironmentResponse: Partial = {}; + const isUserDeletedTheCurrentVersion = editorVersionId === deletedVersionId; + /* + This is post action which is triggered when a version is deleted from the app version manager. + */ + + const multiEnvironmentsNotAvailable = !editorEnvironmentId; + if (multiEnvironmentsNotAvailable) { + const { shouldRenderPromoteButton, shouldRenderReleaseButton } = await this.calculateButtonVisibility( + false + ); + appEnvironmentResponse.shouldRenderPromoteButton = shouldRenderPromoteButton; + appEnvironmentResponse.shouldRenderReleaseButton = shouldRenderReleaseButton; + if (isUserDeletedTheCurrentVersion) { + const newVersionQuery = ` + SELECT name, id, current_environment_id + FROM app_versions + WHERE app_id = $1 + ORDER BY updated_at DESC + LIMIT 1; + `; + const selectedVersionQueryResponse = await manager.query(newVersionQuery, [appId]); + const selectedVersion = selectedVersionQueryResponse[0]; + const selectedEnvironment = await manager.findOneOrFail(AppEnvironment, { + where: { id: selectedVersion.current_environment_id }, + }); + appEnvironmentResponse.editorEnvironment = selectedEnvironment; + appEnvironmentResponse.editorVersion = selectedVersion; + appEnvironmentResponse.appVersionEnvironment = selectedEnvironment; + } + return appEnvironmentResponse; + } + + /* If the editorEnvironment is null then the method will return all the versions of an App */ + const versionsCountOfEnvironment = await manager.count(AppVersion, { + where: { currentEnvironmentId: editorEnvironmentId, appId }, + }); + const environmentDoensNotHaveVersions = versionsCountOfEnvironment === 0; + if (environmentDoensNotHaveVersions) { + /* Send back new editor environment and version */ + const newEnvironmentQuery = ` + SELECT * + FROM app_environments + WHERE priority < ( + SELECT priority + FROM app_environments + WHERE id = $1 + ) + ORDER BY priority DESC + LIMIT 1; + `; + const selectedEnvironmentResponse = await manager.query(newEnvironmentQuery, [editorEnvironmentId]); + const selectedEnvironment = selectedEnvironmentResponse[0]; + const selectedVersion = await this.getSelectedVersion(selectedEnvironment.id, appId, manager); + appEnvironmentResponse.editorEnvironment = selectedEnvironment; + appEnvironmentResponse.editorVersion = selectedVersion; + /* Add extra things to respons */ + } else if (isUserDeletedTheCurrentVersion) { + const selectedEnvironment = await manager.findOneOrFail(AppEnvironment, { + id: editorEnvironmentId, + }); + /* User deleted current editor version. Client needs new editor version */ + if (selectedEnvironment) { + const selectedVersion = await this.getSelectedVersion(editorEnvironmentId, appId, manager); + const appVersionEnvironment = await manager.findOneOrFail(AppEnvironment, { + where: { id: selectedVersion.current_environment_id }, + }); + appEnvironmentResponse.editorVersion = selectedVersion; + appEnvironmentResponse.editorEnvironment = selectedEnvironment; + appEnvironmentResponse.appVersionEnvironment = appVersionEnvironment; + } + } + return appEnvironmentResponse; + } + case AppEnvironmentActions.ENVIROMENT_CHANGED: { + const appEnvironmentResponse: Partial = {}; + appEnvironmentResponse.editorVersion = await this.getSelectedVersion(editorEnvironmentId, appId, manager); + return appEnvironmentResponse; + } + default: + break; + } + }); + } + async get( organizationId: string, id?: string, @@ -52,7 +205,10 @@ export class AppEnvironmentService { ): Promise { return await dbTransactionWrap(async (manager: EntityManager) => { const condition: FindOneOptions = { - where: { organizationId, ...(id ? { id } : !priorityCheck && { isDefault: true }) }, + where: { + organizationId, + ...(id ? { id } : !priorityCheck && { isDefault: true }), + }, ...(priorityCheck && { order: { priority: 'ASC' } }), }; return await manager.findOneOrFail(AppEnvironment, condition); @@ -65,7 +221,9 @@ export class AppEnvironmentService { if (!environmentId) { envId = (await this.get(organizationId, null, false, manager)).id; } - return await manager.findOneOrFail(DataSourceOptions, { where: { environmentId: envId, dataSourceId } }); + return await manager.findOneOrFail(DataSourceOptions, { + where: { environmentId: envId, dataSourceId }, + }); }); } @@ -91,7 +249,7 @@ export class AppEnvironmentService { }, manager); } - async getAll(organizationId: string, manager?: EntityManager, appId?: string): Promise { + async getAll(organizationId: string, manager?: EntityManager, appId?: string): Promise { return await dbTransactionWrap(async (manager: EntityManager) => { const appEnvironments = await manager.find(AppEnvironment, { where: { @@ -107,7 +265,9 @@ export class AppEnvironmentService { for (const appEnvironment of appEnvironments) { const count = await manager.count(AppVersion, { where: { - ...(appEnvironment.priority !== 1 && { currentEnvironmentId: appEnvironment.id }), + ...(appEnvironment.priority !== 1 && { + currentEnvironmentId: appEnvironment.id, + }), appId, }, }); @@ -120,7 +280,12 @@ export class AppEnvironmentService { }, manager); } - async getVersionsByEnvironment(organizationId: string, appId: string, currentEnvironmentId?: string) { + async getVersionsByEnvironment( + organizationId: string, + appId: string, + currentEnvironmentId?: string, + manager?: EntityManager + ) { return await dbTransactionWrap(async (manager: EntityManager) => { const conditions = { appId }; if (currentEnvironmentId) { @@ -151,7 +316,7 @@ export class AppEnvironmentService { }, select: ['id', 'name', 'appId'], }); - }); + }, manager); } async updateOptions(options: object, environmentId: string, dataSourceId: string, manager?: EntityManager) { @@ -247,7 +412,11 @@ export class AppEnvironmentService { envId = (await this.get(organizationId, environmentId, false, manager)).id; } - const constantId = (await manager.findOne(OrganizationConstant, { where: { constantName, organizationId } })).id; + const constantId = ( + await manager.findOne(OrganizationConstant, { + where: { constantName, organizationId }, + }) + ).id; return await manager.findOneOrFail(OrgEnvironmentConstantValue, { where: { organizationConstantId: constantId, environmentId: envId }, diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts index 91e5c99a6e..df039c2ad1 100644 --- a/server/src/services/auth.service.ts +++ b/server/src/services/auth.service.ts @@ -203,6 +203,7 @@ export class AuthService { return decamelizeKeys({ currentOrganizationId: user.organizationId, currentOrganizationSlug: organization.slug, + currentOrganizationName: organization.name, admin: await this.usersService.hasGroup(user, 'admin', null, manager), groupPermissions: await this.usersService.groupPermissions(user, manager), appGroupPermissions: await this.usersService.appGroupPermissions(user, null, manager), @@ -555,18 +556,29 @@ export class AuthService { }; } - generateSessionPayload(user: User, currentOrganization: Organization) { - return decamelizeKeys({ - id: user.id, - email: user.email, - firstName: user.firstName, - lastName: user.lastName, - currentOrganizationSlug: currentOrganization?.slug, - currentOrganizationId: currentOrganization?.id + async generateSessionPayload(user: User, currentOrganization: Organization) { + return dbTransactionWrap(async (manager: EntityManager) => { + const currentOrganizationId = currentOrganization?.id ? currentOrganization?.id : user?.organizationIds?.includes(user?.defaultOrganizationId) ? user.defaultOrganizationId - : user?.organizationIds?.[0], + : user?.organizationIds?.[0]; + const organizationDetails = currentOrganization + ? currentOrganization + : await manager.findOneOrFail(Organization, { + where: { id: currentOrganizationId }, + select: ['slug', 'name', 'id'], + }); + + return decamelizeKeys({ + id: user.id, + email: user.email, + firstName: user.firstName, + lastName: user.lastName, + currentOrganizationSlug: organizationDetails.slug, + currentOrganizationName: organizationDetails.name, + currentOrganizationId, + }); }); }