Skip to content

Commit

Permalink
feat: Deletes provider and displays warning in selected env (#5085)
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagoapolo authored Feb 11, 2025
1 parent 5951c27 commit f2cc990
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 71 deletions.
2 changes: 1 addition & 1 deletion frontend/common/services/useHealthProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const healthProviderService = service
invalidatesTags: [{ id: 'LIST', type: 'HealthProviders' }],
query: (query: Req['deleteHealthProvider']) => ({
method: 'DELETE',
url: `projects/${query.projectId}/feature-health/providers/${query.providerId}/`,
url: `projects/${query.projectId}/feature-health/providers/${query.name}/`,
}),
},
),
Expand Down
2 changes: 1 addition & 1 deletion frontend/common/types/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export type Req = {
getHealthEvents: { projectId: number | string }
getHealthProviders: { projectId: number }
createHealthProvider: { projectId: number; name: string }
deleteHealthProvider: { projectId: number; providerId: number }
deleteHealthProvider: { projectId: number; name: string }
updateTag: { projectId: string; tag: Tag }
deleteTag: {
id: number
Expand Down
8 changes: 6 additions & 2 deletions frontend/common/types/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ export type APIKey = {
name: string
}

export type TagType = 'STALE' | 'UNHEALTHY' | 'NONE'

export type Tag = {
id: number
color: string
Expand All @@ -352,7 +354,7 @@ export type Tag = {
label: string
is_system_tag: boolean
is_permanent: boolean
type: 'STALE' | 'UNHEALTHY' | 'NONE'
type: TagType
}

export type MultivariateFeatureStateValue = {
Expand Down Expand Up @@ -637,13 +639,15 @@ export type SAMLAttributeMapping = {
idp_attribute_name: string
}

export type HealthEventType = 'HEALTHY' | 'UNHEALTHY'

export type HealthEvent = {
created_at: string
environment: number
feature: number
provider_name: string
reason: string
type: 'HEALTHY' | 'UNHEALTHY'
type: HealthEventType
}

export type HealthProvider = {
Expand Down
3 changes: 1 addition & 2 deletions frontend/web/components/EditHealthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,7 @@ const EditHealthProvider: FC<EditHealthProviderType> = ({
onClick={(e) => {
e.stopPropagation()
e.preventDefault()
// TODO: API Needs to expose provider id
// deleteProvider({ projectId, providerId: provider.id })
deleteProvider({ name, projectId })
}}
className='btn btn-with-icon'
>
Expand Down
17 changes: 2 additions & 15 deletions frontend/web/components/FeatureRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import SegmentOverridesIcon from './SegmentOverridesIcon'
import IdentityOverridesIcon from './IdentityOverridesIcon'
import StaleFlagWarning from './StaleFlagWarning'
import UnhealthyFlagWarning from './UnhealthyFlagWarning'
import { getTags } from 'common/services/useTag'
import { getStore } from 'common/store'

export const width = [200, 70, 55, 70, 450]

Expand All @@ -43,14 +41,6 @@ class TheComponent extends Component {
this.state = {
unhealthyTagId: undefined,
}

getTags(getStore(), {
projectId: `${this.props.projectId}`,
}).then((res) => {
this.setState({
unhealthyTagId: res.data?.find((tag) => tag?.type === 'UNHEALTHY')?.id,
})
})
}

confirmToggle = () => {
Expand Down Expand Up @@ -83,7 +73,7 @@ class TheComponent extends Component {

componentDidMount() {
const { environmentFlags, projectFlag } = this.props
const { feature, tab } = Utils.fromParam()
const { feature } = Utils.fromParam()
const { id } = projectFlag
if (`${id}` === feature) {
this.editFeature(projectFlag, environmentFlags[id])
Expand Down Expand Up @@ -113,9 +103,6 @@ class TheComponent extends Component {
return
}
API.trackEvent(Constants.events.VIEW_FEATURE)
const hideTags = this.state.unhealthyTagId
? [this.state.unhealthyTagId]
: []
history.replaceState(
{},
null,
Expand All @@ -137,7 +124,7 @@ class TheComponent extends Component {
</Button>
</Row>,
<CreateFlagModal
hideTags={hideTags}
hideTagsByType={['UNHEALTHY']}
history={this.context.router.history}
environmentId={this.props.environmentId}
projectId={this.props.projectId}
Expand Down
24 changes: 17 additions & 7 deletions frontend/web/components/UnhealthyFlagWarning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,23 @@ const UnhealthyFlagWarning: FC<UnhealthyFlagWarningType> = ({
return null

return (
<div className='fs-caption' style={{ color: Constants.tagColors[16] }}>
{/* TODO: Provider info and link to issue will be provided by reason via the API */}
{latestHealthEvent.reason}
{latestHealthEvent.reason && (
<IonIcon style={{ marginBottom: -2 }} className='ms-1' icon={warning} />
)}
</div>
<Tooltip
title={
<div className='fs-caption' style={{ color: Constants.tagColors[16] }}>
{/* TODO: Provider info and link to issue will be provided by reason via the API */}
{latestHealthEvent.reason}
{latestHealthEvent.reason && (
<IonIcon
style={{ marginBottom: -2 }}
className='ms-1'
icon={warning}
/>
)}
</div>
}
>
This feature is tagged as unhealthy in one or more environments.
</Tooltip>
)
}

Expand Down
5 changes: 3 additions & 2 deletions frontend/web/components/modals/CreateFlag.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const CreateFlag = class extends Component {
multivariate_options: [],
}
const { allowEditDescription } = this.props
const hideTags = this.props.hideTags || []
const hideTagsByType = this.props.hideTagsByType || []
if (this.props.projectFlag) {
this.userOverridesPage(1)
}
Expand Down Expand Up @@ -107,7 +107,7 @@ const CreateFlag = class extends Component {
name,
period: 30,
selectedIdentity: null,
tags: tags?.filter((tag) => !hideTags.includes(tag)) || [],
tags: tags?.filter((tag) => hideTagsByType.includes(tag.type)) || [],
}
}

Expand Down Expand Up @@ -604,6 +604,7 @@ const CreateFlag = class extends Component {
tooltip={Constants.strings.TAGS_DESCRIPTION}
component={
<AddEditTags
hideTagsByType={['UNHEALTHY']}
readOnly={!!identity || !createFeature}
projectId={`${this.props.projectId}`}
value={this.state.tags}
Expand Down
113 changes: 84 additions & 29 deletions frontend/web/components/pages/HomeAside.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { ComponentProps, FC, useEffect, useMemo, useState } from 'react'
import ProjectStore from 'common/stores/project-store'
import ChangeRequestStore from 'common/stores/change-requests-store'
import Utils from 'common/utils/utils'
import { Environment, Project } from 'common/types/responses'
import { Environment } from 'common/types/responses'
import ConfigProvider from 'common/providers/ConfigProvider'
import Permission from 'common/providers/Permission'
import { Link, NavLink } from 'react-router-dom'
Expand All @@ -17,7 +17,6 @@ import { AsyncStorage } from 'polyfill-react-native'
import AccountStore from 'common/stores/account-store'
import AppActions from 'common/dispatcher/app-actions'
import { RouterChildContext } from 'react-router'
import Constants from 'common/constants'
import EnvironmentSelect from 'components/EnvironmentSelect'
import { components } from 'react-select'
import SettingsIcon from 'components/svg/SettingsIcon'
Expand All @@ -30,18 +29,39 @@ type HomeAsideType = {
history: RouterChildContext['router']['history']
}

type OptionProps = ComponentProps<typeof components.Option>
type EnvSelectOptionProps = OptionProps & {
type CustomOptionProps = ComponentProps<typeof components.Option> & {
hasWarning?: boolean
}

const EnvSelectOption = ({ hasWarning, ...rest }: EnvSelectOptionProps) => {
type CustomSingleValueProps = ComponentProps<typeof components.SingleValue> & {
hasWarning?: boolean
}

const TooltipWrapper = ({
showWarning,
title,
}: {
title: React.ReactElement
showWarning: boolean
}) => {
return showWarning ? (
<Tooltip place='bottom' title={title} effect='solid' renderInPortal>
One or more environments have unhealthy features
</Tooltip>
) : (
title
)
}

const CustomOption = ({ hasWarning, ...rest }: CustomOptionProps) => {
const showWarning =
Utils.getFlagsmithHasFeature('feature_health') && hasWarning
return (
<components.Option {...rest}>
<div className='d-flex align-items-center'>
{rest.children}
<div className='d-flex flex-1 align-items-center justify-content-between '>
{Utils.getFlagsmithHasFeature('feature_health') && hasWarning && (
{showWarning && (
<Tooltip
title={
<div className='flex ml-1'>
Expand All @@ -63,6 +83,25 @@ const EnvSelectOption = ({ hasWarning, ...rest }: EnvSelectOptionProps) => {
)
}

const CustomSingleValue = ({ hasWarning, ...rest }: CustomSingleValueProps) => {
const showWarning =
Utils.getFlagsmithHasFeature('feature_health') && hasWarning
return (
<components.SingleValue {...rest}>
<div className='d-flex align-items-center'>
<div>{rest.children}</div>
<div>
{showWarning && (
<div className='flex ml-1'>
<IonIcon className='text-warning' icon={warning} />
</div>
)}
</div>
</div>
</components.SingleValue>
)
}

const HomeAside: FC<HomeAsideType> = ({
environmentId,
history,
Expand All @@ -89,19 +128,20 @@ const HomeAside: FC<HomeAsideType> = ({
//eslint-disable-next-line
}, [])

const unhealthyEnvironments = useMemo(() => {
return healthEvents
?.filter((event) => event.type === 'UNHEALTHY')
.map(
(event) =>
(
ProjectStore.getEnvironmentById(
event.environment,
) as Environment | null
)?.api_key,
)
}, [healthEvents])
const unhealthyEnvironments = healthEvents
?.filter((event) => event?.type === 'UNHEALTHY' && !!event?.environment)
.map(
(event) =>
(
ProjectStore.getEnvironmentById(
event.environment,
) as Environment | null
)?.api_key,
)

const hasUnhealthyEnvironments =
Utils.getFlagsmithHasFeature('feature_health') &&
!!unhealthyEnvironments?.length
const environment: Environment | null =
environmentId === 'create'
? null
Expand Down Expand Up @@ -158,24 +198,39 @@ const HomeAside: FC<HomeAsideType> = ({
styles={{
container: (base: any) => ({
...base,
border: 'none',
border: hasUnhealthyEnvironments
? '1px solid #D35400'
: 'none',
borderRadius: 6,
padding: 0,
}),
}}
label={environment.name}
value={environmentId}
projectId={projectId}
components={{
Menu: ({ ...props }: any) => {
return (
<components.Menu {...props}>
{props.children}
{createEnvironmentButton}
</components.Menu>
)
},
Option: (props: OptionProps) => (
<EnvSelectOption
Control: (props) => (
<TooltipWrapper
showWarning={hasUnhealthyEnvironments}
title={<components.Control {...props} />}
/>
),
Menu: ({ ...props }: any) => (
<components.Menu {...props}>
{props.children}
{createEnvironmentButton}
</components.Menu>
),
Option: (props) => (
<CustomOption
{...props}
hasWarning={unhealthyEnvironments?.includes(
props.data.value,
)}
/>
),
SingleValue: (props) => (
<CustomSingleValue
{...props}
hasWarning={unhealthyEnvironments?.includes(
props.data.value,
Expand Down
15 changes: 12 additions & 3 deletions frontend/web/components/tables/TableTagFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,20 @@ const TableTagFilter: FC<TableFilterType> = ({
}) => {
const [filter, setFilter] = useState('')
const { data } = useGetTagsQuery({ projectId })

const isFeatureHealthEnabled = Utils.getFlagsmithHasFeature('feature_health')
const flagGatedTags = useMemo(() => {
if (!isFeatureHealthEnabled)
return data?.filter((tag) => tag.type !== 'UNHEALTHY')

return data
}, [data, isFeatureHealthEnabled])

const filteredTags = useMemo(() => {
return filter
? data?.filter((v) => v.label.toLowerCase().includes(filter))
: data
}, [data, filter])
? flagGatedTags?.filter((v) => v.label.toLowerCase().includes(filter))
: flagGatedTags?.filter((tag) => tag)
}, [flagGatedTags, filter])
const length = (value?.length || 0) + (showArchived ? 1 : 0)
return (
<div className={isLoading ? 'disabled' : ''}>
Expand Down
Loading

0 comments on commit f2cc990

Please sign in to comment.