mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-24 01:18:01 +02:00
chore: [Gitar] Cleaning up stale feature flag: sdkReporting with value true (#6717)
Co-authored-by: Gitar Bot <noreply@gitar.co> Co-authored-by: sjaanus <sellinjaanus@gmail.com>
This commit is contained in:
parent
c1e37bf7d3
commit
ca6946df86
@ -28,8 +28,6 @@ import useToast from 'hooks/useToast';
|
||||
import { formatDateYMD } from 'utils/formatDate';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { ApplicationEdit } from './ApplicationEdit/ApplicationEdit';
|
||||
import ApplicationOverview from './ApplicationOverview';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
|
||||
@ -70,7 +68,6 @@ const StyledTab = styled(Tab)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
export const Application = () => {
|
||||
const useOldApplicationScreen = !useUiFlag('sdkReporting');
|
||||
const navigate = useNavigate();
|
||||
const name = useRequiredPathParam('name');
|
||||
const { application, loading } = useApplication(name);
|
||||
@ -89,10 +86,6 @@ export const Application = () => {
|
||||
setShowDialog(!showDialog);
|
||||
};
|
||||
|
||||
if (useOldApplicationScreen) {
|
||||
return <ApplicationEdit />;
|
||||
}
|
||||
|
||||
const formatDate = (v: string) => formatDateYMD(v, locationSettings.locale);
|
||||
|
||||
const onDeleteApplication = async (evt: React.SyntheticEvent) => {
|
||||
|
@ -1,197 +0,0 @@
|
||||
/* eslint react/no-multi-comp:off */
|
||||
import type React from 'react';
|
||||
import { useContext, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Avatar,
|
||||
Icon,
|
||||
IconButton,
|
||||
LinearProgress,
|
||||
Link,
|
||||
Tab,
|
||||
Tabs,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import LinkIcon from '@mui/icons-material/Link';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { UPDATE_APPLICATION } from 'component/providers/AccessProvider/permissions';
|
||||
import { ApplicationUpdate } from '../ApplicationUpdate/ApplicationUpdate';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import useApplicationsApi from 'hooks/api/actions/useApplicationsApi/useApplicationsApi';
|
||||
import useApplication from 'hooks/api/getters/useApplication/useApplication';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||
import useToast from 'hooks/useToast';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
import { formatDateYMD } from 'utils/formatDate';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { TabPanel } from 'component/common/TabNav/TabPanel/TabPanel';
|
||||
import { ApplicationView } from '../ApplicationView/ApplicationView';
|
||||
|
||||
export const ApplicationEdit = () => {
|
||||
const navigate = useNavigate();
|
||||
const name = useRequiredPathParam('name');
|
||||
const { application, loading } = useApplication(name);
|
||||
const { appName, url, description, icon = 'apps', createdAt } = application;
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const { deleteApplication } = useApplicationsApi();
|
||||
const { locationSettings } = useLocationSettings();
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
|
||||
const [showDialog, setShowDialog] = useState(false);
|
||||
|
||||
const toggleModal = () => {
|
||||
setShowDialog(!showDialog);
|
||||
};
|
||||
|
||||
const formatDate = (v: string) => formatDateYMD(v, locationSettings.locale);
|
||||
|
||||
const onDeleteApplication = async (evt: React.SyntheticEvent) => {
|
||||
evt.preventDefault();
|
||||
try {
|
||||
await deleteApplication(appName);
|
||||
setToastData({
|
||||
title: 'Deleted Successfully',
|
||||
text: 'Application deleted successfully',
|
||||
type: 'success',
|
||||
});
|
||||
navigate('/applications');
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
};
|
||||
|
||||
const renderModal = () => (
|
||||
<Dialogue
|
||||
open={showDialog}
|
||||
onClose={toggleModal}
|
||||
onClick={onDeleteApplication}
|
||||
title='Are you sure you want to delete this application?'
|
||||
/>
|
||||
);
|
||||
const tabData = [
|
||||
{
|
||||
label: 'Application overview',
|
||||
component: <ApplicationView />,
|
||||
},
|
||||
{
|
||||
label: 'Edit application',
|
||||
component: <ApplicationUpdate application={application} />,
|
||||
},
|
||||
];
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div>
|
||||
<p>Loading...</p>
|
||||
<LinearProgress />
|
||||
</div>
|
||||
);
|
||||
} else if (!application) {
|
||||
return <p>Application ({appName}) not found</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContent>
|
||||
<PageHeader
|
||||
titleElement={
|
||||
<span
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Avatar style={{ marginRight: '8px' }}>
|
||||
<Icon>{icon || 'apps'}</Icon>
|
||||
</Avatar>
|
||||
{appName}
|
||||
</span>
|
||||
}
|
||||
title={appName}
|
||||
actions={
|
||||
<>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(url)}
|
||||
show={
|
||||
<IconButton
|
||||
component={Link}
|
||||
href={url}
|
||||
size='large'
|
||||
>
|
||||
<LinkIcon titleAccess={url} />
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
|
||||
<PermissionButton
|
||||
tooltipProps={{ title: 'Delete application' }}
|
||||
onClick={toggleModal}
|
||||
permission={UPDATE_APPLICATION}
|
||||
>
|
||||
Delete
|
||||
</PermissionButton>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Box sx={(theme) => ({ marginTop: theme.spacing(1) })}>
|
||||
<Typography variant='body1'>{description || ''}</Typography>
|
||||
<Typography variant='body2'>
|
||||
Created: <strong>{formatDate(createdAt)}</strong>
|
||||
</Typography>
|
||||
</Box>
|
||||
</PageContent>
|
||||
<br />
|
||||
<PageContent
|
||||
withTabs
|
||||
header={
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onChange={(_, tabId) => {
|
||||
setActiveTab(tabId);
|
||||
}}
|
||||
indicatorColor='primary'
|
||||
textColor='primary'
|
||||
>
|
||||
{tabData.map((tab, index) => (
|
||||
<Tab
|
||||
key={`${tab.label}_${index}`}
|
||||
label={tab.label}
|
||||
id={`tab-${index}`}
|
||||
aria-controls={`tabpanel-${index}`}
|
||||
sx={{
|
||||
minWidth: {
|
||||
lg: 160,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={hasAccess(UPDATE_APPLICATION)}
|
||||
show={
|
||||
<div>
|
||||
{renderModal()}
|
||||
{tabData.map((tab, index) => (
|
||||
<TabPanel
|
||||
key={index}
|
||||
value={activeTab}
|
||||
index={index}
|
||||
>
|
||||
{tab.component}
|
||||
</TabPanel>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</PageContent>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,215 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
Grid,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
Typography,
|
||||
Divider,
|
||||
} from '@mui/material';
|
||||
import Extension from '@mui/icons-material/Extension';
|
||||
import FlagRounded from '@mui/icons-material/FlagRounded';
|
||||
import Report from '@mui/icons-material/Report';
|
||||
import Timeline from '@mui/icons-material/Timeline';
|
||||
import {
|
||||
CREATE_FEATURE,
|
||||
CREATE_STRATEGY,
|
||||
} from 'component/providers/AccessProvider/permissions';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { getTogglePath } from 'utils/routePathHelpers';
|
||||
import useApplication from 'hooks/api/getters/useApplication/useApplication';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import { formatDateYMDHMS } from 'utils/formatDate';
|
||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
|
||||
export const ApplicationView = () => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const name = useRequiredPathParam('name');
|
||||
const { application } = useApplication(name);
|
||||
const { locationSettings } = useLocationSettings();
|
||||
const { instances, strategies, seenToggles } = application;
|
||||
|
||||
const notFoundListItem = ({
|
||||
createUrl,
|
||||
name,
|
||||
permission,
|
||||
}: {
|
||||
createUrl: string;
|
||||
name: string;
|
||||
permission: string;
|
||||
}) => (
|
||||
<ConditionallyRender
|
||||
key={`not_found_conditional_${name}`}
|
||||
condition={hasAccess(permission)}
|
||||
show={
|
||||
<ListItem key={`not_found_${name}`}>
|
||||
<ListItemAvatar>
|
||||
<Report style={{ color: 'red' }} />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={<Link to={`${createUrl}`}>{name}</Link>}
|
||||
secondary={'Missing, want to create?'}
|
||||
/>
|
||||
</ListItem>
|
||||
}
|
||||
elseShow={
|
||||
<ListItem key={`not_found_${name}`}>
|
||||
<ListItemAvatar>
|
||||
<Report />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={name}
|
||||
secondary={`Could not find feature toggle with name ${name}`}
|
||||
/>
|
||||
</ListItem>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
const foundListItem = ({
|
||||
viewUrl,
|
||||
name,
|
||||
description,
|
||||
Icon,
|
||||
i,
|
||||
}: {
|
||||
viewUrl: string;
|
||||
name: string;
|
||||
description: string;
|
||||
Icon: React.ElementType;
|
||||
i: number;
|
||||
}) => (
|
||||
<ListItem key={`found_${name}-${i}`}>
|
||||
<ListItemAvatar>
|
||||
<Icon />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<Link
|
||||
to={`${viewUrl}/${name}`}
|
||||
style={{ wordBreak: 'break-all' }}
|
||||
>
|
||||
{name}
|
||||
</Link>
|
||||
}
|
||||
secondary={description}
|
||||
/>
|
||||
</ListItem>
|
||||
);
|
||||
|
||||
return (
|
||||
<Grid container style={{ margin: 0 }}>
|
||||
<Grid item xl={6} md={6} xs={12}>
|
||||
<Typography variant='subtitle1' style={{ padding: '1rem 0' }}>
|
||||
Toggles
|
||||
</Typography>
|
||||
<Divider />
|
||||
<List>
|
||||
{seenToggles.map(
|
||||
({ name, description, notFound, project }, i) => (
|
||||
<ConditionallyRender
|
||||
key={`toggle_conditional_${name}`}
|
||||
condition={notFound}
|
||||
show={notFoundListItem({
|
||||
createUrl: `/projects/default/create-toggle?name=${name}`,
|
||||
name,
|
||||
permission: CREATE_FEATURE,
|
||||
})}
|
||||
elseShow={foundListItem({
|
||||
viewUrl: getTogglePath(project, name),
|
||||
name,
|
||||
description,
|
||||
Icon: FlagRounded,
|
||||
i,
|
||||
})}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid item xl={6} md={6} xs={12}>
|
||||
<Typography variant='subtitle1' style={{ padding: '1rem 0' }}>
|
||||
Implemented strategies
|
||||
</Typography>
|
||||
<Divider />
|
||||
<List>
|
||||
{strategies.map(
|
||||
({ name, description, notFound }, i: number) => (
|
||||
<ConditionallyRender
|
||||
key={`strategies_conditional_${name}`}
|
||||
condition={notFound}
|
||||
show={notFoundListItem({
|
||||
createUrl: '/strategies/create',
|
||||
name,
|
||||
permission: CREATE_STRATEGY,
|
||||
})}
|
||||
elseShow={foundListItem({
|
||||
viewUrl: '/strategies',
|
||||
name,
|
||||
Icon: Extension,
|
||||
description,
|
||||
i,
|
||||
})}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid item xl={12} md={12}>
|
||||
<Typography variant='subtitle1' style={{ padding: '1rem 0' }}>
|
||||
{instances.length} Instances registered
|
||||
</Typography>
|
||||
<Divider />
|
||||
<List>
|
||||
{instances.map(
|
||||
({
|
||||
instanceId,
|
||||
clientIp,
|
||||
lastSeen,
|
||||
sdkVersion,
|
||||
}: {
|
||||
instanceId: string;
|
||||
clientIp: string;
|
||||
lastSeen: string;
|
||||
sdkVersion: string;
|
||||
}) => (
|
||||
<ListItem key={`${instanceId}`}>
|
||||
<ListItemAvatar>
|
||||
<Timeline />
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
primary={
|
||||
<ConditionallyRender
|
||||
key={`${instanceId}_conditional`}
|
||||
condition={Boolean(sdkVersion)}
|
||||
show={
|
||||
<span>
|
||||
{instanceId} {sdkVersion}
|
||||
</span>
|
||||
}
|
||||
elseShow={<span>{instanceId}</span>}
|
||||
/>
|
||||
}
|
||||
secondary={
|
||||
<span>
|
||||
{clientIp} last seen at{' '}
|
||||
<small>
|
||||
{formatDateYMDHMS(
|
||||
lastSeen,
|
||||
locationSettings.locale,
|
||||
)}
|
||||
</small>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
),
|
||||
)}
|
||||
</List>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
@ -133,7 +133,6 @@ export const Project = () => {
|
||||
title: 'Applications',
|
||||
path: `${basePath}/applications`,
|
||||
name: 'applications',
|
||||
flag: 'sdkReporting',
|
||||
},
|
||||
{
|
||||
title: 'Event log',
|
||||
|
@ -76,7 +76,6 @@ export type UiFlags = {
|
||||
showInactiveUsers?: boolean;
|
||||
featureSearchFeedbackPosting?: boolean;
|
||||
userAccessUIEnabled?: boolean;
|
||||
sdkReporting?: boolean;
|
||||
outdatedSdksBanner?: boolean;
|
||||
projectOverviewRefactor?: string;
|
||||
collectTrafficDataUsage?: boolean;
|
||||
|
@ -138,7 +138,6 @@ exports[`should create default config 1`] = `
|
||||
"responseTimeWithAppNameKillSwitch": false,
|
||||
"returnGlobalFrontendApiCache": false,
|
||||
"scimApi": false,
|
||||
"sdkReporting": false,
|
||||
"showInactiveUsers": false,
|
||||
"signals": false,
|
||||
"strictSchemaValidation": false,
|
||||
|
@ -38,7 +38,6 @@ beforeAll(async () => {
|
||||
experimental: {
|
||||
flags: {
|
||||
strictSchemaValidation: true,
|
||||
sdkReporting: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -37,7 +37,6 @@ import {
|
||||
projectApplicationsSchema,
|
||||
type ProjectApplicationsSchema,
|
||||
} from '../../openapi/spec/project-applications-schema';
|
||||
import { NotFoundError } from '../../error';
|
||||
import { projectApplicationsQueryParameters } from '../../openapi/spec/project-applications-query-parameters';
|
||||
import { normalizeQueryParams } from '../feature-search/search-utils';
|
||||
import ProjectInsightsController from '../project-insights/project-insights-controller';
|
||||
@ -267,10 +266,6 @@ export default class ProjectController extends Controller {
|
||||
req: IAuthRequest,
|
||||
res: Response<ProjectApplicationsSchema>,
|
||||
): Promise<void> {
|
||||
if (!this.flagResolver.isEnabled('sdkReporting')) {
|
||||
throw new NotFoundError();
|
||||
}
|
||||
|
||||
const { projectId } = req.params;
|
||||
|
||||
const {
|
||||
|
@ -24,7 +24,6 @@ beforeAll(async () => {
|
||||
experimental: {
|
||||
flags: {
|
||||
strictSchemaValidation: true,
|
||||
sdkReporting: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -17,7 +17,6 @@ import type { CreateApplicationSchema } from '../../openapi/spec/create-applicat
|
||||
import type { IAuthRequest } from '../unleash-types';
|
||||
import { extractUserIdFromUser } from '../../util';
|
||||
import { type IFlagResolver, serializeDates } from '../../types';
|
||||
import { NotFoundError } from '../../error';
|
||||
import {
|
||||
type ApplicationOverviewSchema,
|
||||
applicationOverviewSchema,
|
||||
@ -285,9 +284,6 @@ class MetricsController extends Controller {
|
||||
req: Request<{ appName: string }>,
|
||||
res: Response<ApplicationOverviewSchema>,
|
||||
): Promise<void> {
|
||||
if (!this.flagResolver.isEnabled('sdkReporting')) {
|
||||
throw new NotFoundError();
|
||||
}
|
||||
const { appName } = req.params;
|
||||
const overview =
|
||||
await this.clientInstanceService.getApplicationOverview(appName);
|
||||
@ -301,9 +297,6 @@ class MetricsController extends Controller {
|
||||
}
|
||||
|
||||
async getOutdatedSdks(req: Request, res: Response<OutdatedSdksSchema>) {
|
||||
if (!this.flagResolver.isEnabled('sdkReporting')) {
|
||||
throw new NotFoundError();
|
||||
}
|
||||
const outdatedSdks = await this.clientInstanceService.getOutdatedSdks();
|
||||
|
||||
this.openApiService.respondWithValidation(
|
||||
@ -318,9 +311,6 @@ class MetricsController extends Controller {
|
||||
req: Request<{ appName: string; environment: string }>,
|
||||
res: Response<ApplicationEnvironmentInstancesSchema>,
|
||||
): Promise<void> {
|
||||
if (!this.flagResolver.isEnabled('sdkReporting')) {
|
||||
throw new NotFoundError();
|
||||
}
|
||||
const { appName, environment } = req.params;
|
||||
const instances =
|
||||
await this.clientInstanceService.getApplicationEnvironmentInstances(
|
||||
|
@ -49,7 +49,6 @@ export type IFlagKey =
|
||||
| 'userAccessUIEnabled'
|
||||
| 'disableUpdateMaxRevisionId'
|
||||
| 'disablePublishUnannouncedEvents'
|
||||
| 'sdkReporting'
|
||||
| 'outdatedSdksBanner'
|
||||
| 'responseTimeMetricsFix'
|
||||
| 'scimApi'
|
||||
@ -196,10 +195,6 @@ const flags: IFlags = {
|
||||
process.env.UNLEASH_EXPERIMENTAL_EXECUTIVE_DASHBOARD_UI,
|
||||
false,
|
||||
),
|
||||
sdkReporting: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_SDK_REPORTING,
|
||||
false,
|
||||
),
|
||||
outdatedSdksBanner: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_OUTDATED_SDKS_BANNER,
|
||||
false,
|
||||
|
@ -49,7 +49,6 @@ process.nextTick(async () => {
|
||||
executiveDashboard: true,
|
||||
executiveDashboardUI: true,
|
||||
userAccessUIEnabled: true,
|
||||
sdkReporting: true,
|
||||
outdatedSdksBanner: true,
|
||||
globalFrontendApiCache: true,
|
||||
returnGlobalFrontendApiCache: false,
|
||||
|
@ -56,7 +56,6 @@ beforeAll(async () => {
|
||||
experimental: {
|
||||
flags: {
|
||||
strictSchemaValidation: true,
|
||||
sdkReporting: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user