mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-24 09:28:31 +00:00
Added initial commit - Added lazy load for versions
This commit is contained in:
parent
858dff3606
commit
7a109fb1cd
18 changed files with 389 additions and 87 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import cx from 'classnames';
|
||||
import { appVersionService } from '@/_services';
|
||||
import { CustomSelect } from './CustomSelect';
|
||||
|
|
@ -6,6 +6,7 @@ import { toast } from 'react-hot-toast';
|
|||
import { shallow } from 'zustand/shallow';
|
||||
import { useAppVersionStore } from '@/_stores/appVersionStore';
|
||||
import { useEditorStore } from '@/_stores/editorStore';
|
||||
import { useEnvironmentsAndVersionsStore } from '@/_stores/environmentsAndVersionsStore';
|
||||
|
||||
const appVersionLoadingStatus = Object.freeze({
|
||||
loading: 'loading',
|
||||
|
|
@ -20,18 +21,56 @@ export const AppVersionsManager = function ({
|
|||
isEditable = true,
|
||||
isViewer,
|
||||
}) {
|
||||
const [appVersionStatus, setGetAppVersionStatus] = useState(appVersionLoadingStatus.loading);
|
||||
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);
|
||||
const [deleteVersion, setDeleteVersion] = useState({
|
||||
versionId: '',
|
||||
versionName: '',
|
||||
showModal: false,
|
||||
});
|
||||
const [forceMenuOpen, setForceMenuOpen] = useState(false);
|
||||
|
||||
const { releasedVersionId, editingVersion, appVersions, setAppVersions } = useAppVersionStore(
|
||||
const { releasedVersionId, editingVersion } = useAppVersionStore(
|
||||
(state) => ({
|
||||
editingVersion: state.editingVersion,
|
||||
appVersions: state.appVersions,
|
||||
setAppVersions: state.actions?.setAppVersions,
|
||||
releasedVersionId: state.releasedVersionId,
|
||||
}),
|
||||
shallow
|
||||
|
|
@ -43,16 +82,6 @@ export const AppVersionsManager = function ({
|
|||
shallow
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (appVersions && appVersions.length > 0) {
|
||||
setGetAppVersionStatus(appVersionLoadingStatus.loaded);
|
||||
}
|
||||
|
||||
return () => {
|
||||
setGetAppVersionStatus(appVersionLoadingStatus.loading);
|
||||
};
|
||||
}, [appVersions]);
|
||||
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
|
||||
const selectVersion = (id) => {
|
||||
|
|
@ -93,13 +122,12 @@ export const AppVersionsManager = function ({
|
|||
})
|
||||
.finally(() => {
|
||||
appVersionService.getAll(appId, true).then((data) => {
|
||||
setAppVersions(data.versions);
|
||||
onVersionDelete();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const options = appVersions.map((appVersion) => ({
|
||||
const options = versionsPromotedToEnvironment.map((appVersion) => ({
|
||||
value: appVersion.id,
|
||||
isReleasedVersion: appVersion.id === releasedVersionId,
|
||||
appVersionName: appVersion.name,
|
||||
|
|
@ -139,10 +167,17 @@ export const AppVersionsManager = function ({
|
|||
),
|
||||
}));
|
||||
|
||||
const onMenuOpen = async () => {
|
||||
if (!appVersionsLazyLoaded) {
|
||||
setGetAppVersionStatus(appVersionLoadingStatus.loading);
|
||||
await lazyLoadAppVersions(appId);
|
||||
setGetAppVersionStatus(appVersionLoadingStatus.loaded);
|
||||
}
|
||||
setForceMenuOpen(!forceMenuOpen);
|
||||
};
|
||||
|
||||
const customSelectProps = {
|
||||
appId,
|
||||
appVersions,
|
||||
setAppVersions,
|
||||
setAppDefinitionFromVersion,
|
||||
editingVersion,
|
||||
setDeleteVersion,
|
||||
|
|
@ -175,6 +210,9 @@ export const AppVersionsManager = function ({
|
|||
{...customSelectProps}
|
||||
className={` ${darkMode && 'dark-theme'}`}
|
||||
isEditable={isEditable}
|
||||
onMenuOpen={onMenuOpen}
|
||||
onMenuClose={() => setForceMenuOpen(false)}
|
||||
menuIsOpen={forceMenuOpen}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -707,8 +707,6 @@ const EditorComponent = (props) => {
|
|||
useAppVersionStore.getState().actions.updateReleasedVersionId(data.current_version_id);
|
||||
}
|
||||
|
||||
const appVersions = await appEnvironmentService.getVersionsByEnvironment(data?.id);
|
||||
setAppVersions(appVersions.appVersions);
|
||||
const currentOrgId = data?.organization_id || data?.organizationId;
|
||||
|
||||
updateState({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
import { useEnvironmentsAndVersionsActions } from '@/_stores/environmentsAndVersionsStore';
|
||||
import React, { useEffect } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { useAppVersionStore } from '@/_stores/appVersionStore';
|
||||
|
||||
const EnvironmentManager = () => {
|
||||
const { editingVersionId } = useAppVersionStore(
|
||||
(state) => ({
|
||||
editingVersionId: state?.editingVersion?.id,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
const { init, setEnvironmentDropdownStatus } = useEnvironmentsAndVersionsActions();
|
||||
useEffect(() => {
|
||||
initComponent();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const initComponent = async () => {
|
||||
await init(editingVersionId);
|
||||
setEnvironmentDropdownStatus(true);
|
||||
};
|
||||
|
||||
return <div></div>;
|
||||
};
|
||||
|
||||
export default EnvironmentManager;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import EnvironmentManager from './EnvironmentsManager';
|
||||
export default EnvironmentManager;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
const PromoteVersionButton = () => {
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export default PromoteVersionButton;
|
||||
|
|
@ -8,7 +8,7 @@ import { ConfirmDialog } from '@/_components/ConfirmDialog';
|
|||
import { shallow } from 'zustand/shallow';
|
||||
import { ButtonSolid } from '@/_ui/AppButton/AppButton';
|
||||
|
||||
export const ReleaseVersionButton = function DeployVersionButton({ appId, appName, fetchApp, onVersionRelease }) {
|
||||
export const ReleaseVersionButton = function DeployVersionButton({ onVersionRelease }) {
|
||||
const [isReleasing, setIsReleasing] = useState(false);
|
||||
const { isVersionReleased, editingVersion } = useAppVersionStore(
|
||||
(state) => ({
|
||||
|
|
@ -24,17 +24,15 @@ export const ReleaseVersionButton = function DeployVersionButton({ appId, appNam
|
|||
setShowPageDeletionConfirmation(false);
|
||||
setIsReleasing(true);
|
||||
|
||||
const { id: versionToBeReleased, name, app_id } = editingVersion;
|
||||
|
||||
appsService
|
||||
.saveApp(appId, {
|
||||
name: appName,
|
||||
current_version_id: editingVersion.id,
|
||||
})
|
||||
.releaseVersion(app_id, versionToBeReleased)
|
||||
.then(() => {
|
||||
toast(`Version ${editingVersion.name} released`, {
|
||||
toast(`Version ${name} released`, {
|
||||
icon: '🚀',
|
||||
});
|
||||
fetchApp && fetchApp();
|
||||
onVersionRelease(editingVersion.id);
|
||||
onVersionRelease(versionToBeReleased);
|
||||
setIsReleasing(false);
|
||||
})
|
||||
.catch((_error) => {
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { ReleaseVersionButton } from './ReleaseVersionButton';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useAppInfo, useAppDataActions } from '@/_stores/appDataStore';
|
||||
import { ManageAppUsers } from './ManageAppUsers';
|
||||
import { useAppVersionStore } from '@/_stores/appVersionStore';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import queryString from 'query-string';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useCurrentStateStore } from '@/_stores/currentStateStore';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
import { useEnvironmentsAndVersionsStore } from '@/_stores/environmentsAndVersionsStore';
|
||||
import PromoteVersionButton from './PromoteVersionButton';
|
||||
|
||||
const RightTopHeaderButtons = ({ onVersionRelease }) => {
|
||||
return (
|
||||
<div className="d-flex justify-content-end navbar-right-section" style={{ width: '300px', paddingRight: '12px' }}>
|
||||
<div className=" release-buttons navbar-nav flex-row">
|
||||
<PreviewAndShareIcons />
|
||||
<PromoteAndReleaseButton onVersionRelease={onVersionRelease} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const PreviewAndShareIcons = () => {
|
||||
const { appId, app, slug, isPublic, appVersionPreviewLink, currentVersionId } = useAppInfo();
|
||||
const { setAppPreviewLink } = useAppDataActions();
|
||||
const { isVersionReleased, editingVersion } = useAppVersionStore(
|
||||
(state) => ({
|
||||
isVersionReleased: state.isVersionReleased,
|
||||
editingVersion: state.editingVersion,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
const { pageHandle } = useCurrentStateStore(
|
||||
(state) => ({
|
||||
pageHandle: state?.page?.handle,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
|
||||
useEffect(() => {
|
||||
const previewQuery = queryString.stringify({ version: editingVersion.name });
|
||||
const appVersionPreviewLink = editingVersion.id
|
||||
? `/applications/${slug || appId}/${pageHandle}${!isEmpty(previewQuery) ? `?${previewQuery}` : ''}`
|
||||
: '';
|
||||
setAppPreviewLink(appVersionPreviewLink);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [slug, currentVersionId, editingVersion]);
|
||||
|
||||
return (
|
||||
<div className="preview-share-wrap navbar-nav flex-row" style={{ gap: '4px' }}>
|
||||
<div className="nav-item">
|
||||
{appId && (
|
||||
<ManageAppUsers
|
||||
app={app}
|
||||
appId={appId}
|
||||
slug={slug}
|
||||
darkMode={darkMode}
|
||||
isVersionReleased={isVersionReleased}
|
||||
pageHandle={pageHandle}
|
||||
isPublic={isPublic ?? false}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="nav-item">
|
||||
<Link
|
||||
title="Preview"
|
||||
to={appVersionPreviewLink}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
data-cy="preview-link-button"
|
||||
className="editor-header-icon tj-secondary-btn"
|
||||
>
|
||||
<SolidIcon name="eyeopen" width="14" fill="#3E63DD" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const PromoteAndReleaseButton = ({ onVersionRelease }) => {
|
||||
const { shouldRenderPromoteButton, shouldRenderReleaseButton } = useEnvironmentsAndVersionsStore(
|
||||
(state) => ({
|
||||
shouldRenderPromoteButton: state.shouldRenderPromoteButton,
|
||||
shouldRenderReleaseButton: state.shouldRenderReleaseButton,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="nav-item dropdown promote-release-btn">
|
||||
{shouldRenderPromoteButton && <PromoteVersionButton />}
|
||||
{shouldRenderReleaseButton && <ReleaseVersionButton onVersionRelease={onVersionRelease} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RightTopHeaderButtons;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import RightTopHeaderButtons from './RightTopHeaderButtons';
|
||||
export default RightTopHeaderButtons;
|
||||
|
|
@ -1,12 +1,8 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import AppLogo from '@/_components/AppLogo';
|
||||
import EditAppName from './EditAppName';
|
||||
import HeaderActions from './HeaderActions';
|
||||
import RealtimeAvatars from '../RealtimeAvatars';
|
||||
import { AppVersionsManager } from '@/Editor/AppVersionsManager/AppVersionsManager';
|
||||
import { ManageAppUsers } from '../ManageAppUsers';
|
||||
import { ReleaseVersionButton } from '../ReleaseVersionButton';
|
||||
import cx from 'classnames';
|
||||
import config from 'config';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
|
|
@ -16,10 +12,11 @@ import { useCurrentStateStore } from '@/_stores/currentStateStore';
|
|||
import { shallow } from 'zustand/shallow';
|
||||
import { useAppDataActions, useAppInfo, useCurrentUser } from '@/_stores/appDataStore';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
import { redirectToDashboard } from '@/_helpers/routes';
|
||||
import queryString from 'query-string';
|
||||
import { isEmpty } from 'lodash';
|
||||
import LogoNavDropdown from '@/_components/LogoNavDropdown';
|
||||
import RightTopHeaderButtons from './RightTopHeaderButtons';
|
||||
import EnvironmentManager from './RightTopHeaderButtons/EnvironmentManager';
|
||||
|
||||
export default function EditorHeader({
|
||||
M,
|
||||
|
|
@ -31,15 +28,13 @@ export default function EditorHeader({
|
|||
onNameChanged,
|
||||
setAppDefinitionFromVersion,
|
||||
onVersionRelease,
|
||||
saveEditingVersion,
|
||||
onVersionDelete,
|
||||
slug,
|
||||
darkMode,
|
||||
isSocketOpen,
|
||||
}) {
|
||||
const currentUser = useCurrentUser();
|
||||
|
||||
const { isSaving, appId, appName, app, isPublic, appVersionPreviewLink, currentVersionId } = useAppInfo();
|
||||
const { isSaving, appId, appName, isPublic, currentVersionId } = useAppInfo();
|
||||
const { setAppPreviewLink } = useAppDataActions();
|
||||
const { isVersionReleased, editingVersion } = useAppVersionStore(
|
||||
(state) => ({
|
||||
|
|
@ -148,6 +143,8 @@ export default function EditorHeader({
|
|||
</div>
|
||||
<div className="navbar-seperator"></div>
|
||||
|
||||
<EnvironmentManager />
|
||||
|
||||
{editingVersion && (
|
||||
<AppVersionsManager
|
||||
appId={appId}
|
||||
|
|
@ -157,52 +154,7 @@ export default function EditorHeader({
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="d-flex justify-content-end navbar-right-section"
|
||||
style={{ width: '300px', paddingRight: '12px' }}
|
||||
>
|
||||
<div className=" release-buttons navbar-nav flex-row">
|
||||
<div className="preview-share-wrap navbar-nav flex-row" style={{ gap: '4px' }}>
|
||||
<div className="nav-item">
|
||||
{appId && (
|
||||
<ManageAppUsers
|
||||
app={app}
|
||||
appId={appId}
|
||||
slug={slug}
|
||||
darkMode={darkMode}
|
||||
isVersionReleased={isVersionReleased}
|
||||
pageHandle={pageHandle}
|
||||
M={M}
|
||||
isPublic={isPublic ?? false}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="nav-item">
|
||||
<Link
|
||||
title="Preview"
|
||||
to={appVersionPreviewLink}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
data-cy="preview-link-button"
|
||||
className="editor-header-icon tj-secondary-btn"
|
||||
>
|
||||
<SolidIcon name="eyeopen" width="14" fill="#3E63DD" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isSocketOpen && (
|
||||
<div className="nav-item dropdown promote-release-btn">
|
||||
<ReleaseVersionButton
|
||||
appId={appId}
|
||||
appName={appName}
|
||||
onVersionRelease={onVersionRelease}
|
||||
saveEditingVersion={saveEditingVersion}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<RightTopHeaderButtons onVersionRelease={onVersionRelease} />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import queryString from 'query-string';
|
|||
export const appEnvironmentService = {
|
||||
getAllEnvironments,
|
||||
getVersionsByEnvironment,
|
||||
init,
|
||||
};
|
||||
|
||||
function getAllEnvironments() {
|
||||
|
|
@ -29,3 +30,9 @@ function getVersionsByEnvironment(appId, environmentId /* not needed for CE */)
|
|||
requestOptions
|
||||
).then(handleResponse);
|
||||
}
|
||||
|
||||
function init(editing_version_id = null) {
|
||||
const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' };
|
||||
const query = queryString.stringify({ editing_version_id });
|
||||
return fetch(`${config.apiUrl}/app-environments/init?${query}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ export const appsService = {
|
|||
getAppUsers,
|
||||
getVersions,
|
||||
getTables,
|
||||
releaseVersion,
|
||||
};
|
||||
|
||||
function validateReleasedApp(slug) {
|
||||
|
|
@ -204,3 +205,14 @@ function getTables(id) {
|
|||
const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' };
|
||||
return fetch(`${config.apiUrl}/apps/${id}/tables`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function releaseVersion(appId, versionToBeReleased) {
|
||||
const requestOptions = {
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
body: JSON.stringify({ versionToBeReleased }),
|
||||
credentials: 'include',
|
||||
};
|
||||
|
||||
return fetch(`${config.apiUrl}/v2/apps/${appId}/release`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
|
|
|||
60
frontend/src/_stores/environmentsAndVersionsStore.js
Normal file
60
frontend/src/_stores/environmentsAndVersionsStore.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import create from 'zustand';
|
||||
import { zustandDevTools } from './utils';
|
||||
import { appEnvironmentService } from '../_services/app_environment.service';
|
||||
|
||||
const initialState = {
|
||||
selectedVersion: null,
|
||||
selectedEnvironment: null,
|
||||
appVersionEnvironment: null,
|
||||
versionsPromotedToEnvironment: [],
|
||||
environments: [],
|
||||
shouldRenderPromoteButton: false,
|
||||
shouldRenderReleaseButton: false,
|
||||
initializedEnvironmentDropdown: false,
|
||||
initializedVersionsDropdown: false,
|
||||
environmentsLazyLoaded: false,
|
||||
appVersionsLazyLoaded: false,
|
||||
};
|
||||
|
||||
export const useEnvironmentsAndVersionsStore = create(
|
||||
zustandDevTools(
|
||||
(set, get) => ({
|
||||
...initialState,
|
||||
actions: {
|
||||
init: async (editingVersionId) => {
|
||||
try {
|
||||
const response = await appEnvironmentService.init(editingVersionId);
|
||||
set((state) => ({
|
||||
...state,
|
||||
selectedEnvironment: response.editorEnvironment,
|
||||
selectedVersion: response.editorVersion,
|
||||
appVersionEnvironment: response.appVersionEnvironment,
|
||||
shouldRenderPromoteButton: response.shouldRenderPromoteButton,
|
||||
shouldRenderReleaseButton: response.shouldRenderReleaseButton,
|
||||
environments: [response.editorEnvironment],
|
||||
versionsPromotedToEnvironment: [response.editorVersion],
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error while initializing the environment dropdown', error);
|
||||
}
|
||||
},
|
||||
setEnvironmentDropdownStatus: (state) => set({ initializedEnvironmentDropdown: state }),
|
||||
lazyLoadAppVersions: async (appId) => {
|
||||
try {
|
||||
const response = await appEnvironmentService.getVersionsByEnvironment(appId, get().selectedEnvironment.id);
|
||||
set((state) => ({
|
||||
...state,
|
||||
versionsPromotedToEnvironment: response.appVersions,
|
||||
appVersionsLazyLoaded: true,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error while getting the versions', error);
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
{ name: 'App Version Manager Store' }
|
||||
)
|
||||
);
|
||||
|
||||
export const useEnvironmentsAndVersionsActions = () => useEnvironmentsAndVersionsStore((state) => state.actions);
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
|
||||
import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common';
|
||||
import { decamelizeKeys } from 'humps';
|
||||
import { JwtAuthGuard } from '../modules/auth/jwt-auth.guard';
|
||||
import { ForbiddenException } from '@nestjs/common';
|
||||
|
|
@ -17,6 +17,16 @@ export class AppEnvironmentsController {
|
|||
private orgEnvironmentVariablesAbilityFactory: OrgEnvironmentVariablesAbilityFactory
|
||||
) {}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('init')
|
||||
async init(@User() user, @Query('editing_version_id') editingVersionId: string) {
|
||||
/*
|
||||
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);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get()
|
||||
async index(@User() user, @Query('app_id') appId: string) {
|
||||
|
|
@ -39,9 +49,13 @@ export class AppEnvironmentsController {
|
|||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('versions')
|
||||
async getVersions(@User() user, @Query('app_id') appId: string) {
|
||||
const appVersions = await this.appEnvironmentServices.getVersionsByEnvironment(user?.organizationId, appId);
|
||||
@Get(':id/versions')
|
||||
async getVersionsByEnvironment(@User() user, @Param('id') environmentId: string, @Query('app_id') appId: string) {
|
||||
const appVersions = await this.appEnvironmentServices.getVersionsByEnvironment(
|
||||
user?.organizationId,
|
||||
appId,
|
||||
environmentId
|
||||
);
|
||||
return { appVersions };
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import { PageService } from '@services/page.service';
|
|||
import { EventsService } from '@services/events_handler.service';
|
||||
import { AppVersionUpdateDto } from '@dto/app-version-update.dto';
|
||||
import { CreateEventHandlerDto, UpdateEventHandlerDto } from '@dto/event-handler.dto';
|
||||
import { VersionReleaseDto } from '@dto/version-release.dto';
|
||||
|
||||
@Controller({
|
||||
path: 'apps',
|
||||
|
|
@ -499,4 +500,20 @@ export class AppsControllerV2 {
|
|||
|
||||
return await this.eventService.deleteEvent(eventId, versionId);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@UseInterceptors(ValidAppInterceptor)
|
||||
@Put(':id/release')
|
||||
async releaseVersion(
|
||||
@User() user,
|
||||
@Param('id') id,
|
||||
@AppDecorator() app: App,
|
||||
@Body() versionReleaseDto: VersionReleaseDto
|
||||
) {
|
||||
const ability = await this.appsAbilityFactory.appsActions(user, app.id);
|
||||
if (!ability.can('updateParams', app)) {
|
||||
throw new ForbiddenException('You do not have permissions to perform this action');
|
||||
}
|
||||
return await this.appsService.releaseVersion(app.id, versionReleaseDto);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
server/src/dto/version-release.dto.ts
Normal file
10
server/src/dto/version-release.dto.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
import { Transform } from 'class-transformer';
|
||||
import { sanitizeInput } from 'src/helpers/utils.helper';
|
||||
|
||||
export class VersionReleaseDto {
|
||||
@IsNotEmpty()
|
||||
@IsUUID()
|
||||
@Transform(({ value }) => sanitizeInput(value))
|
||||
versionToBeReleased: string;
|
||||
}
|
||||
|
|
@ -7,8 +7,43 @@ import { OrganizationConstant } from 'src/entities/organization_constants.entity
|
|||
import { EntityManager, FindOneOptions, In, DeleteResult } from 'typeorm';
|
||||
import { AppVersion } from 'src/entities/app_version.entity';
|
||||
|
||||
export interface AppEnvironmentResponse {
|
||||
editorVersion: Partial<AppVersion>;
|
||||
editorEnvironment: AppEnvironment;
|
||||
appVersionEnvironment: AppEnvironment;
|
||||
shouldRenderPromoteButton: boolean;
|
||||
shouldRenderReleaseButton: boolean;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AppEnvironmentService {
|
||||
async init(editingVersionId: string, manager?: EntityManager): Promise<AppEnvironmentResponse> {
|
||||
return await dbTransactionWrap(async (manager: EntityManager) => {
|
||||
const editorVersion = await manager.findOne(AppVersion, {
|
||||
select: ['id', 'name', 'currentEnvironmentId'],
|
||||
where: { id: editingVersionId },
|
||||
});
|
||||
const editorEnvironment = await manager.findOne(AppEnvironment, { id: editorVersion.currentEnvironmentId });
|
||||
const { shouldRenderPromoteButton, shouldRenderReleaseButton } =
|
||||
this.calculateButtonVisibility(editorEnvironment);
|
||||
const response: AppEnvironmentResponse = {
|
||||
editorVersion,
|
||||
editorEnvironment,
|
||||
appVersionEnvironment: editorEnvironment,
|
||||
shouldRenderPromoteButton,
|
||||
shouldRenderReleaseButton,
|
||||
};
|
||||
return response;
|
||||
}, manager);
|
||||
}
|
||||
|
||||
calculateButtonVisibility(appVersionEnvironment: AppEnvironment) {
|
||||
/* Further conditions can handle from here */
|
||||
const shouldRenderPromoteButton = false;
|
||||
const shouldRenderReleaseButton = true;
|
||||
return { shouldRenderPromoteButton, shouldRenderReleaseButton };
|
||||
}
|
||||
|
||||
async get(
|
||||
organizationId: string,
|
||||
id?: string,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import { Layout } from 'src/entities/layout.entity';
|
|||
|
||||
import { Component } from 'src/entities/component.entity';
|
||||
import { EventHandler } from 'src/entities/event_handler.entity';
|
||||
import { VersionReleaseDto } from '@dto/version-release.dto';
|
||||
|
||||
const uuid = require('uuid');
|
||||
@Injectable()
|
||||
|
|
@ -1044,4 +1045,25 @@ export class AppsService {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
async releaseVersion(appId: string, versionReleaseDto: VersionReleaseDto, manager?: EntityManager) {
|
||||
return await dbTransactionWrap(async (manager: EntityManager) => {
|
||||
const { versionToBeReleased } = versionReleaseDto;
|
||||
//check if the app version is eligible for release
|
||||
const currentEnvironment: AppEnvironment = await manager
|
||||
.createQueryBuilder(AppEnvironment, 'app_environments')
|
||||
.select(['app_environments.id', 'app_environments.isDefault'])
|
||||
.innerJoinAndSelect('app_versions', 'app_versions', 'app_versions.current_environment_id = app_environments.id')
|
||||
.where('app_versions.id = :versionToBeReleased', {
|
||||
versionToBeReleased,
|
||||
})
|
||||
.getOne();
|
||||
|
||||
if (!currentEnvironment?.isDefault) {
|
||||
throw new BadRequestException('You can only release when the version is promoted to production');
|
||||
}
|
||||
|
||||
return await manager.update(App, appId, { currentVersionId: versionToBeReleased });
|
||||
}, manager);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue