Platform-Editor API improvements (#9460)

* Editor header and sequence of API calls Improvements. (#9366)

* Added createVersion and updatedVersionName changes

* Added deff-call for version delete action

* removed callBack func(re-arranged as sub-functions)

* removed unwanted functions

* fixed merge issues, appVersioManager and environment store issues

* Resolved query-onload issue and release button bugs

* working on preview setting issues

* refactoring the code

* moved environment manager directory

* fixed import issues

* Refactoring the service functions

* Added environments details to the init response

* Added appVersionsCount to the response

* Added new post action to the controller

* Refactored recent commit

* Fixed query response array issue

* Fixed await issue

* Added extra release/promote button cases

* Removed organizations call from the authorizeWorkspace function

* added a length check to prevent calling the api again

* removed the loader and notification API call

* Fixed on blur issue of react-select using event handlers
This commit is contained in:
Muhsin Shah C P 2024-05-07 16:58:07 +05:30 committed by GitHub
parent eaa4b9c68c
commit b8ce0bbcda
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 635 additions and 302 deletions

View file

@ -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 ? (
<RealtimeEditor {...props} shouldLoadApp={shouldLoadApp} />
) : (
<Editor {...props} />
);
return config.ENABLE_MULTIPLAYER_EDITING ? <RealtimeEditor {...props} /> : <Editor {...props} />;
});
export const AppLoader = withTranslation()(AppLoaderComponent);

View file

@ -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 (
<RenderComponent
appId={appId}
setAppDefinitionFromVersion={setAppDefinitionFromVersion}
onVersionDelete={onVersionDelete}
isEditable={isEditable}
isViewer={isViewer}
versionsPromotedToEnvironment={versionsPromotedToEnvironment}
lazyLoadAppVersions={lazyLoadAppVersions}
appVersionsLazyLoaded={appVersionsLazyLoaded}
/>
);
} 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 (
<div
className="d-flex align-items-center p-0"
style={{ margin: isViewer && currentLayout === 'mobile' ? '0px' : '0 24px' }}
ref={clickedOutsideRef}
>
<div
className={cx('d-flex version-manager-container p-0', {
@ -216,7 +226,7 @@ const RenderComponent = ({
<CustomSelect
isLoading={appVersionStatus === 'loading'}
options={options}
value={editingVersion?.id}
value={selectedVersion?.id}
onChange={(id) => selectVersion(id)}
{...customSelectProps}
className={` ${darkMode && 'dark-theme'}`}

View file

@ -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 (

View file

@ -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 (

View file

@ -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);

View file

@ -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 (
<div className="apploader">
<div className="col col-* editor-center-wrapper">
<div className="editor-center">
<div className="canvas">
<div className="mt-5 d-flex flex-column">
<div className="mb-1">
<Skeleton width={'150px'} height={15} className="skeleton" />
</div>
{Array.from(Array(4)).map((_item, index) => (
<Skeleton key={index} width={'300px'} height={10} className="skeleton" />
))}
<div className="align-self-end">
<Skeleton width={'100px'} className="skeleton" />
</div>
<Skeleton className="skeleton mt-4" />
<Skeleton height={'150px'} className="skeleton mt-2" />
</div>
</div>
</div>
</div>
</div>
);
return <TJLoader />;
}
return (
<HotkeysProvider initiallyActiveScopes={['editor']}>
@ -1970,7 +1966,6 @@ const EditorComponent = (props) => {
setAppDefinitionFromVersion={setAppDefinitionFromVersion}
onVersionRelease={onVersionRelease}
saveEditingVersion={saveEditingVersion}
onVersionDelete={onVersionDelete}
isMaintenanceOn={isMaintenanceOn}
appName={appName}
appId={appId}

View file

@ -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 <div></div>;
return <></>;
};
export default EnvironmentManager;

View file

@ -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: '🚀',

View file

@ -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({
<AppVersionsManager
appId={appId}
setAppDefinitionFromVersion={setAppDefinitionFromVersion}
onVersionDelete={onVersionDelete}
isPublic={isPublic ?? false}
/>
)}

View file

@ -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 (
<LeftSidebarItem
commentBadge={notifications?.length > 0}

View file

@ -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
<div className="preview-settings-overlay" style={{ borderColor: darkMode ? '#2B3036' : '#E4E7EB' }}>
<span className="preview-settings-text">Preview settings</span>
<span>
<EnvironmentManager />
{editingVersion && (
<AppVersionsManager
appId={appId}
@ -88,6 +90,7 @@ const PreviewSettings = ({ isMobileLayout, setAppDefinitionFromVersion, showHead
{previewNavbar && (
<Offcanvas.Body>
<span>
<EnvironmentManager />
{editingVersion && (
<AppVersionsManager
appId={appId}

View file

@ -1,24 +1,31 @@
import React, { useState, useEffect } from 'react';
import React, { useEffect } from 'react';
import { authenticationService } from '@/_services';
import { CustomSelect } from './CustomSelect';
import { getAvatar } from '@/_helpers/utils';
import { appendWorkspaceId, getWorkspaceIdOrSlugFromURL } from '@/_helpers/routes';
import { ToolTip } from '@/_components';
import { useCurrentSessionStore } from '@/_stores/currentSessionStore';
import { shallow } from 'zustand/shallow';
/* TODO:
each workspace related component has organizations list component which can be moved to a single wrapper.
otherwise this component will intiate everytime we switch between pages
*/
export const OrganizationList = function () {
const { current_organization_id } = authenticationService.currentSessionValue;
const [organizationList, setOrganizationList] = useState([]);
const [getOrgStatus, setGetOrgStatus] = useState('');
const { fetchOrganizations, organizationList, isGettingOrganizations } = useCurrentSessionStore(
(state) => ({
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 (
<div className="org-select-container">
<CustomSelect
isLoading={getOrgStatus === 'loading'}
isLoading={isGettingOrganizations}
options={options}
value={current_organization_id}
onChange={(id) => switchOrganization(id)}

View file

@ -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 */

View file

@ -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() {

View file

@ -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);
}

View file

@ -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);

View file

@ -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' }

View file

@ -78,7 +78,7 @@ export class AppController {
currentOrganization = organization;
}
return this.authService.generateSessionPayload(user, currentOrganization);
return await this.authService.generateSessionPayload(user, currentOrganization);
}
@UseGuards(JwtAuthGuard)

View file

@ -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)

View file

@ -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;
}

View file

@ -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<AppVersion>;
@ -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<AppEnvironmentResponse> {
async init(
editingVersionId: string,
organizationId: string,
manager?: EntityManager
): Promise<AppEnvironmentResponse> {
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<any> {
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<AppEnvironmentResponse> = {};
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> = {};
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<AppEnvironment> {
return await dbTransactionWrap(async (manager: EntityManager) => {
const condition: FindOneOptions<AppEnvironment> = {
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<AppEnvironment[]> {
async getAll(organizationId: string, manager?: EntityManager, appId?: string): Promise<ExtendedEnvironment[]> {
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 },

View file

@ -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,
});
});
}