Skip to content

Commit 208f6b7

Browse files
Merge pull request #1326 from devtron-labs/env_filter_app_overview_deployment_metrics
feat: Implemented env filter on app details, overview and deployment metrics
2 parents 122ea62 + d2c89da commit 208f6b7

File tree

7 files changed

+124
-74
lines changed

7 files changed

+124
-74
lines changed

src/components/app/Overview/Overview.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState } from 'react'
1+
import React, { useEffect, useMemo, useState } from 'react'
22
import moment from 'moment'
33
import { Link, useParams } from 'react-router-dom'
44
import { ModuleNameMap, Moment12HourFormat, URLS } from '../../../config'
@@ -44,7 +44,7 @@ import { DEFAULT_ENV } from '../details/triggerView/Constants'
4444
import GenericDescription from '../../common/Description/GenericDescription'
4545
const MandatoryTagWarning = importComponentFromFELibrary('MandatoryTagWarning')
4646

47-
export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverview }: AppOverviewProps) {
47+
export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverview, filteredEnvIds }: AppOverviewProps) {
4848
const { appId } = useParams<{ appId: string }>()
4949
const [isLoading, setIsLoading] = useState(true)
5050
const [currentLabelTags, setCurrentLabelTags] = useState<TagType[]>([])
@@ -94,6 +94,18 @@ export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverv
9494
}
9595
}, [appId])
9696

97+
const envList = useMemo(() => {
98+
if (otherEnvsResult?.[0]?.result?.length > 0) {
99+
const filteredEnvMap = filteredEnvIds?.split(',').reduce((agg, curr) => agg.set(+curr, true), new Map())
100+
return (
101+
otherEnvsResult[0].result
102+
.filter((env) => !filteredEnvMap || filteredEnvMap.get(env.environmentId))
103+
?.sort((a, b) => (a.environmentName > b.environmentName ? 1 : -1)) || []
104+
)
105+
}
106+
return []
107+
}, [filteredEnvIds, otherEnvsResult])
108+
97109
const getExternalLinksDetails = (): void => {
98110
getExternalLinks(0, appId, ExternalLinkIdentifierType.DevtronApp)
99111
.then((externalLinksRes) => {
@@ -289,8 +301,7 @@ export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverv
289301

290302
const renderDeploymentComponent = () => {
291303

292-
if (otherEnvsResult?.[0]?.result?.length > 0) {
293-
otherEnvsResult[0].result.sort((a, b) => (a.environmentName > b.environmentName ? 1 : -1))
304+
if (envList.length > 0) {
294305
return (
295306
<div className="env-deployments-info-wrapper w-100">
296307
<div
@@ -304,7 +315,7 @@ export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverv
304315
</div>
305316

306317
<div className="env-deployments-info-body">
307-
{otherEnvsResult[0].result.map(
318+
{envList.map(
308319
(_env, index) =>
309320
!_env.deploymentAppDeleteRequest && (
310321
<div
@@ -383,7 +394,7 @@ export default function AppOverview({ appMetaInfo, getAppMetaInfoRes, isJobOverv
383394
)
384395
}
385396
}
386-
397+
387398
const renderWorkflowComponent = () => {
388399
if (!Array.isArray(jobPipelines) || !jobPipelines.length) {
389400
return (

src/components/app/details/appDetails/AppDetails.tsx

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ const processVirtualEnvironmentDeploymentData = importComponentFromFELibrary(
106106
)
107107
let deploymentStatusTimer = null
108108

109-
export default function AppDetail() {
109+
export default function AppDetail({filteredEnvIds}:{filteredEnvIds?: string}) {
110110
const params = useParams<{ appId: string; envId?: string }>()
111111
const { push } = useHistory()
112112
const { path } = useRouteMatch()
@@ -116,25 +116,42 @@ export default function AppDetail() {
116116
const [commitInfo, showCommitInfo] = useState<boolean>(false)
117117
const isVirtualEnvRef = useRef(false)
118118

119-
useEffect(() => {
120-
if (otherEnvsLoading) return
121-
// If there is only one environment, redirect to that environment
122-
if (!params.envId && otherEnvsResult?.result?.length === 1) {
123-
const newUrl = getAppDetailsURL(params.appId, otherEnvsResult?.result[0].environmentId)
124-
push(newUrl)
125-
}
126-
if (
127-
!params.envId &&
128-
environmentId &&
129-
otherEnvsResult?.result?.map((env) => env.environmentId).includes(environmentId)
130-
) {
131-
const newUrl = getAppDetailsURL(params.appId, environmentId)
132-
push(newUrl)
133-
} else if (!otherEnvsResult?.result?.map((env) => env.environmentId).includes(+params.envId)) {
134-
setEnvironmentId(null)
135-
return
136-
}
137-
}, [otherEnvsLoading])
119+
const envList = useMemo(() => {
120+
if (otherEnvsResult?.result?.length > 0) {
121+
const filteredEnvMap = filteredEnvIds?.split(',').reduce((agg, curr) => agg.set(+curr, true), new Map())
122+
const _envList =
123+
otherEnvsResult.result
124+
.filter((env) => !filteredEnvMap || filteredEnvMap.get(env.environmentId))
125+
?.sort((a, b) => (a.environmentName > b.environmentName ? 1 : -1)) || []
126+
127+
if (_envList.length > 0) {
128+
let _envId
129+
if (!params.envId && _envList.length === 1) {
130+
_envId = _envList[0].environmentId
131+
} else if (
132+
!params.envId &&
133+
environmentId &&
134+
_envList.map((env) => env.environmentId).includes(environmentId)
135+
) {
136+
_envId = environmentId
137+
} else if (!_envList.map((env) => env.environmentId).includes(+params.envId)) {
138+
_envId = _envList[0].environmentId
139+
}
140+
if (_envId) {
141+
const newUrl = getAppDetailsURL(params.appId, _envId)
142+
push(newUrl)
143+
} else{
144+
setEnvironmentId(null)
145+
}
146+
} else {
147+
setEnvironmentId(null)
148+
}
149+
return _envList
150+
} else {
151+
setEnvironmentId(null)
152+
}
153+
return []
154+
}, [filteredEnvIds, otherEnvsResult])
138155

139156
useEffect(() => {
140157
if (!params.envId) return
@@ -147,26 +164,28 @@ export default function AppDetail() {
147164
otherEnvsResult &&
148165
!otherEnvsLoading && (
149166
<>
150-
{(!otherEnvsResult?.result || otherEnvsResult?.result?.length === 0) &&
167+
{(envList.length === 0) &&
151168
!isAppDeleted &&
152169
!isVirtualEnvRef.current && <AppNotConfigured />}
153-
{!params.envId && otherEnvsResult?.result?.length > 0 && !isVirtualEnvRef.current && (
154-
<EnvironmentNotConfigured environments={otherEnvsResult?.result} />
170+
{!params.envId && envList.length > 0 && !isVirtualEnvRef.current && (
171+
<EnvironmentNotConfigured environments={envList} />
155172
)}
156173
</>
157174
)
158175
)
159176
}
160177

161-
const environment = otherEnvsResult?.result?.find((env) => env.environmentId === +params.envId)
178+
const environment = useMemo(() => {
179+
return envList.find((env) => env.environmentId === +params.envId)
180+
}, [envList, params.envId])
162181

163182
return (
164183
<div data-testid="app-details-wrapper" className="app-details-page-wrapper">
165-
{!params.envId && otherEnvsResult?.result?.length > 0 && (
184+
{!params.envId && envList.length > 0 && (
166185
<div className="w-100 pt-16 pr-20 pb-20 pl-20">
167186
<SourceInfo
168187
appDetails={null}
169-
environments={otherEnvsResult?.result}
188+
environments={envList}
170189
environment={environment}
171190
refetchDeploymentStatus={noop}
172191
/>
@@ -179,7 +198,7 @@ export default function AppDetail() {
179198
appDetailsAPI={fetchAppDetailsInTime}
180199
isAppDeployment
181200
environment={environment}
182-
environments={otherEnvsResult?.result}
201+
environments={envList}
183202
setIsAppDeleted={setIsAppDeleted}
184203
commitInfo={commitInfo}
185204
showCommitInfo={showCommitInfo}

src/components/app/details/cdDetails/CDDetails.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export default function CDDetails({filteredEnvIds}:{filteredEnvIds: string}) {
201201
}
202202
})
203203

204-
if (envOptions.length === 1 && !envId && !isEnvDeleted) {
204+
if ((envOptions.length === 1 && !envId && !isEnvDeleted) || (envId && envOptions.length && !_selectedEnvironment)) {
205205
replace(generatePath(path, { appId, envId: envOptions[0].value, pipelineId: envOptions[0].pipelineId }))
206206
}
207207
setEnvOptions(envOptions)

src/components/app/details/main.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,19 +211,21 @@ export default function AppDetailsPage({ isV2 }: AppDetailsProps) {
211211
) : (
212212
<Route
213213
path={`${path}/${URLS.APP_DETAILS}/:envId(\\d+)?`}
214-
render={(props) => <AppDetails />}
214+
render={(props) => <AppDetails filteredEnvIds={_filteredEnvIds}/>}
215215
/>
216216
)}
217217
<Route path={`${path}/${URLS.APP_OVERVIEW}`}>
218-
<Overview appMetaInfo={appMetaInfo} getAppMetaInfoRes={getAppMetaInfoRes} />
218+
<Overview appMetaInfo={appMetaInfo} getAppMetaInfoRes={getAppMetaInfoRes} filteredEnvIds={_filteredEnvIds} />
219219
</Route>
220220
<Route path={`${path}/${URLS.APP_TRIGGER}`} render={(props) => <TriggerView />} />
221221
<Route path={`${path}/${URLS.APP_CI_DETAILS}/:pipelineId(\\d+)?/:buildId(\\d+)?`}>
222222
<CIDetails key={appId} />
223223
</Route>
224224
<Route
225225
path={`${path}/${URLS.APP_DEPLOYMENT_METRICS}/:envId(\\d+)?`}
226-
component={DeploymentMetrics}
226+
render={(props) => {
227+
return <DeploymentMetrics {...props} filteredEnvIds={_filteredEnvIds} />
228+
}}
227229
/>
228230
<Route
229231
path={`${path}/${URLS.APP_CD_DETAILS}/:envId(\\d+)?/:pipelineId(\\d+)?/:triggerId(\\d+)?`}

src/components/app/details/metrics/DeploymentMetrics.tsx

Lines changed: 53 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -95,17 +95,25 @@ export default class DeploymentMetrics extends Component<DeploymentMetricsProps,
9595
}
9696

9797
componentDidUpdate(prevProps, prevState) {
98-
if (prevProps.match.params.appId !== this.props.match.params.appId) {
99-
this.setState({ view: ViewType.LOADING, selectedEnvironment: undefined });
100-
this.callGetAppOtherEnv(prevProps.match.params.envId);
101-
}
102-
if (this.props.match.params.envId && prevProps.match.params.envId !== this.props.match.params.envId) {
103-
this.setState({ view: ViewType.LOADING });
104-
this.callGetDeploymentMetricsAPI(this.props.match.params.appId, this.props.match.params.envId)
105-
}
106-
if ((!prevState.startDate && this.state.startDate) || (!prevState.endDate && this.state.endDate) || (prevState.startDate?.valueOf() !== this.state.startDate?.valueOf()) || (prevState.endDate?.valueOf() !== this.state.endDate?.valueOf())) {
107-
this.callGetDeploymentMetricsAPI(this.props.match.params.appId, this.props.match.params.envId)
108-
}
98+
if (
99+
prevProps.match.params.appId !== this.props.match.params.appId ||
100+
prevProps.filteredEnvIds !== this.props.filteredEnvIds
101+
) {
102+
this.setState({ view: ViewType.LOADING, selectedEnvironment: undefined })
103+
this.callGetAppOtherEnv(prevProps.match.params.envId)
104+
}
105+
if (this.props.match.params.envId && prevProps.match.params.envId !== this.props.match.params.envId) {
106+
this.setState({ view: ViewType.LOADING })
107+
this.callGetDeploymentMetricsAPI(this.props.match.params.appId, this.props.match.params.envId)
108+
}
109+
if (
110+
(!prevState.startDate && this.state.startDate) ||
111+
(!prevState.endDate && this.state.endDate) ||
112+
prevState.startDate?.valueOf() !== this.state.startDate?.valueOf() ||
113+
prevState.endDate?.valueOf() !== this.state.endDate?.valueOf()
114+
) {
115+
this.callGetDeploymentMetricsAPI(this.props.match.params.appId, this.props.match.params.envId)
116+
}
109117
}
110118

111119
callGetDeploymentMetricsAPI(appId, envId) {
@@ -126,33 +134,42 @@ export default class DeploymentMetrics extends Component<DeploymentMetricsProps,
126134
}
127135

128136
callGetAppOtherEnv(prevEnvId: string | undefined) {
129-
getAppOtherEnvironmentMin(this.props.match.params.appId).then((envResponse) => {
130-
let allEnv= envResponse.result?.filter(env => env.prod).map((env) => {
131-
return {
132-
label: env.environmentName,
133-
value: env.environmentId,
134-
deploymentAppDeleteRequest: env.deploymentAppDeleteRequest,
137+
getAppOtherEnvironmentMin(this.props.match.params.appId)
138+
.then((envResponse) => {
139+
const filteredEnvMap = this.props.filteredEnvIds
140+
?.split(',')
141+
.reduce((agg, curr) => agg.set(+curr, true), new Map())
142+
let allEnv = envResponse.result
143+
?.filter((env) => env.prod && (!filteredEnvMap || filteredEnvMap.get(env.environmentId)))
144+
.map((env) => {
145+
return {
146+
label: env.environmentName,
147+
value: env.environmentId,
148+
deploymentAppDeleteRequest: env.deploymentAppDeleteRequest,
149+
}
150+
})
151+
allEnv = allEnv || []
152+
let callAPIOnEnvOfPrevApp = prevEnvId && allEnv.find((e) => Number(e.value) === Number(prevEnvId))
153+
this.setState({
154+
environments: allEnv,
155+
filteredEnvironment: allEnv.filter((_env) => !_env.deploymentAppDeleteRequest),
156+
view: this.props.match.params.envId || callAPIOnEnvOfPrevApp ? ViewType.LOADING : ViewType.FORM,
157+
})
158+
if (prevEnvId) {
159+
const isEnvExist = allEnv.find((e) => Number(e.value) === Number(prevEnvId))
160+
let url = generatePath(this.props.match.path, {
161+
appId: this.props.match.params.appId,
162+
envId: isEnvExist ? prevEnvId : allEnv[0].value,
163+
})
164+
this.props.history.push(url)
165+
} else if (this.props.match.params.envId) {
166+
this.callGetDeploymentMetricsAPI(this.props.match.params.appId, this.props.match.params.envId)
135167
}
136168
})
137-
allEnv = allEnv || [];
138-
let callAPIOnEnvOfPrevApp = prevEnvId && allEnv.find(e => Number(e.value) === Number(prevEnvId));
139-
this.setState({
140-
environments: allEnv,
141-
filteredEnvironment: allEnv.filter((_env) => !_env.deploymentAppDeleteRequest),
142-
view: this.props.match.params.envId || callAPIOnEnvOfPrevApp ? ViewType.LOADING : ViewType.FORM,
143-
144-
});
145-
if (prevEnvId && allEnv.find(e => Number(e.value) === Number(prevEnvId))) {
146-
let url = generatePath(this.props.match.path, { appId: this.props.match.params.appId, envId: prevEnvId });
147-
this.props.history.push(url);
148-
}
149-
else if (this.props.match.params.envId) {
150-
this.callGetDeploymentMetricsAPI(this.props.match.params.appId, this.props.match.params.envId)
151-
}
152-
}).catch((error) => {
153-
showError(error);
154-
this.setState({ code: error.code, view: ViewType.ERROR });
155-
})
169+
.catch((error) => {
170+
showError(error)
171+
this.setState({ code: error.code, view: ViewType.ERROR })
172+
})
156173
}
157174

158175
closeDeploymentTableModal(): void {

src/components/app/details/metrics/deploymentMetrics.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface StatisticsType {
1818
}
1919

2020
export interface DeploymentMetricsProps extends RouteComponentProps<{ appId: string; envId: string; }> {
21-
21+
filteredEnvIds?: string
2222
}
2323

2424
export interface Environment{

src/components/app/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ export interface AppOverviewProps {
423423
appMetaInfo: AppMetaInfo
424424
getAppMetaInfoRes: () => Promise<AppMetaInfo>
425425
isJobOverview?: boolean
426+
filteredEnvIds?: string
426427
}
427428

428429
export interface AboutAppInfoModalProps {

0 commit comments

Comments
 (0)