Add limits to buckets
parent
843dbfe039
commit
6d6a194150
|
@ -6,10 +6,18 @@ import {client} from 'src/utils/api'
|
||||||
// Types
|
// Types
|
||||||
import {RemoteDataState, AppState, Bucket} from 'src/types'
|
import {RemoteDataState, AppState, Bucket} from 'src/types'
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import {isLimitError, extractMessage} from 'src/cloud/utils/limits'
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
import {notify} from 'src/shared/actions/notifications'
|
import {notify} from 'src/shared/actions/notifications'
|
||||||
import {getBucketsFailed} from 'src/shared/copy/notifications'
|
import {checkBucketLimits} from 'src/cloud/actions/limits'
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import {
|
||||||
|
getBucketsFailed,
|
||||||
|
resourceLimitReached,
|
||||||
|
} from 'src/shared/copy/notifications'
|
||||||
import {
|
import {
|
||||||
bucketCreateFailed,
|
bucketCreateFailed,
|
||||||
bucketUpdateFailed,
|
bucketUpdateFailed,
|
||||||
|
@ -106,10 +114,15 @@ export const createBucket = (bucket: Bucket) => async (
|
||||||
})
|
})
|
||||||
|
|
||||||
dispatch(addBucket(createdBucket))
|
dispatch(addBucket(createdBucket))
|
||||||
} catch (e) {
|
dispatch(checkBucketLimits())
|
||||||
console.error(e)
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
if (isLimitError(error)) {
|
||||||
|
const message = extractMessage(error)
|
||||||
|
dispatch(notify(resourceLimitReached('buckets', message)))
|
||||||
|
} else {
|
||||||
dispatch(notify(bucketCreateFailed()))
|
dispatch(notify(bucketCreateFailed()))
|
||||||
throw e
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +162,7 @@ export const deleteBucket = (id: string, name: string) => async (
|
||||||
await client.buckets.delete(id)
|
await client.buckets.delete(id)
|
||||||
|
|
||||||
dispatch(removeBucket(id))
|
dispatch(removeBucket(id))
|
||||||
|
dispatch(checkBucketLimits())
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
dispatch(notify(bucketDeleteFailed(name)))
|
dispatch(notify(bucketDeleteFailed(name)))
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {connect} from 'react-redux'
|
||||||
// Components
|
// Components
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
import {Input, Button, EmptyState} from '@influxdata/clockface'
|
import {Input, Button, EmptyState} from '@influxdata/clockface'
|
||||||
import {Overlay, Tabs} from 'src/clockface'
|
import {Overlay, Tabs, ComponentStatus} from 'src/clockface'
|
||||||
import FilterList from 'src/shared/components/Filter'
|
import FilterList from 'src/shared/components/Filter'
|
||||||
import BucketList from 'src/buckets/components/BucketList'
|
import BucketList from 'src/buckets/components/BucketList'
|
||||||
import {PrettyBucket} from 'src/buckets/components/BucketRow'
|
import {PrettyBucket} from 'src/buckets/components/BucketRow'
|
||||||
|
@ -14,6 +14,10 @@ import CreateBucketOverlay from 'src/buckets/components/CreateBucketOverlay'
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
import {createBucket, updateBucket, deleteBucket} from 'src/buckets/actions'
|
import {createBucket, updateBucket, deleteBucket} from 'src/buckets/actions'
|
||||||
|
import {
|
||||||
|
checkBucketLimits as checkBucketLimitsAction,
|
||||||
|
LimitStatus,
|
||||||
|
} from 'src/cloud/actions/limits'
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import {prettyBuckets} from 'src/shared/utils/prettyBucket'
|
import {prettyBuckets} from 'src/shared/utils/prettyBucket'
|
||||||
|
@ -32,12 +36,14 @@ import {SortTypes} from 'src/shared/utils/sort'
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
org: Organization
|
org: Organization
|
||||||
buckets: Bucket[]
|
buckets: Bucket[]
|
||||||
|
limitStatus: LimitStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
createBucket: typeof createBucket
|
createBucket: typeof createBucket
|
||||||
updateBucket: typeof updateBucket
|
updateBucket: typeof updateBucket
|
||||||
deleteBucket: typeof deleteBucket
|
deleteBucket: typeof deleteBucket
|
||||||
|
checkBucketLimits: typeof checkBucketLimitsAction
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
|
@ -66,6 +72,10 @@ class BucketsTab extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
this.props.checkBucketLimits()
|
||||||
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {org, buckets} = this.props
|
const {org, buckets} = this.props
|
||||||
const {
|
const {
|
||||||
|
@ -93,6 +103,8 @@ class BucketsTab extends PureComponent<Props, State> {
|
||||||
color={ComponentColor.Primary}
|
color={ComponentColor.Primary}
|
||||||
onClick={this.handleOpenModal}
|
onClick={this.handleOpenModal}
|
||||||
testID="Create Bucket"
|
testID="Create Bucket"
|
||||||
|
status={this.createButtonStatus}
|
||||||
|
titleText={this.createButtonTitleText}
|
||||||
/>
|
/>
|
||||||
</Tabs.TabContentsHeader>
|
</Tabs.TabContentsHeader>
|
||||||
<FilterList<PrettyBucket>
|
<FilterList<PrettyBucket>
|
||||||
|
@ -163,6 +175,20 @@ class BucketsTab extends PureComponent<Props, State> {
|
||||||
this.setState({searchTerm})
|
this.setState({searchTerm})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get createButtonStatus(): ComponentStatus {
|
||||||
|
if (this.props.limitStatus === LimitStatus.EXCEEDED) {
|
||||||
|
return ComponentStatus.Disabled
|
||||||
|
}
|
||||||
|
return ComponentStatus.Default
|
||||||
|
}
|
||||||
|
|
||||||
|
private get createButtonTitleText(): string {
|
||||||
|
if (this.props.limitStatus === LimitStatus.EXCEEDED) {
|
||||||
|
return 'This account has the maximum number of buckets allowed'
|
||||||
|
}
|
||||||
|
return 'Create a bucket'
|
||||||
|
}
|
||||||
|
|
||||||
private get emptyState(): JSX.Element {
|
private get emptyState(): JSX.Element {
|
||||||
const {searchTerm} = this.state
|
const {searchTerm} = this.state
|
||||||
|
|
||||||
|
@ -191,17 +217,25 @@ class BucketsTab extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mstp = ({buckets, orgs}: AppState): StateProps => {
|
const mstp = ({
|
||||||
return {
|
buckets,
|
||||||
|
orgs,
|
||||||
|
cloud: {
|
||||||
|
limits: {
|
||||||
|
buckets: {limitStatus},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}: AppState): StateProps => ({
|
||||||
buckets: buckets.list,
|
buckets: buckets.list,
|
||||||
org: orgs.org,
|
org: orgs.org,
|
||||||
}
|
limitStatus,
|
||||||
}
|
})
|
||||||
|
|
||||||
const mdtp = {
|
const mdtp = {
|
||||||
createBucket,
|
createBucket,
|
||||||
updateBucket,
|
updateBucket,
|
||||||
deleteBucket,
|
deleteBucket,
|
||||||
|
checkBucketLimits: checkBucketLimitsAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect<StateProps, DispatchProps, {}>(
|
export default connect<StateProps, DispatchProps, {}>(
|
||||||
|
|
|
@ -15,6 +15,7 @@ import GetResources, {ResourceTypes} from 'src/shared/components/GetResources'
|
||||||
// Types
|
// Types
|
||||||
import {Organization} from '@influxdata/influx'
|
import {Organization} from '@influxdata/influx'
|
||||||
import {AppState} from 'src/types'
|
import {AppState} from 'src/types'
|
||||||
|
import GetAssetLimits from 'src/cloud/components/GetAssetLimits'
|
||||||
|
|
||||||
interface StateProps {
|
interface StateProps {
|
||||||
org: Organization
|
org: Organization
|
||||||
|
@ -41,8 +42,10 @@ class BucketsIndex extends Component<StateProps> {
|
||||||
>
|
>
|
||||||
<GetResources resource={ResourceTypes.Buckets}>
|
<GetResources resource={ResourceTypes.Buckets}>
|
||||||
<GetResources resource={ResourceTypes.Telegrafs}>
|
<GetResources resource={ResourceTypes.Telegrafs}>
|
||||||
|
<GetAssetLimits>
|
||||||
<BucketsTab />
|
<BucketsTab />
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
</GetAssetLimits>
|
||||||
</GetResources>
|
</GetResources>
|
||||||
</GetResources>
|
</GetResources>
|
||||||
</TabbedPageSection>
|
</TabbedPageSection>
|
||||||
|
|
|
@ -46,9 +46,14 @@ export enum ActionTypes {
|
||||||
SetLimits = 'SET_LIMITS',
|
SetLimits = 'SET_LIMITS',
|
||||||
SetLimitsStatus = 'SET_LIMITS_STATUS',
|
SetLimitsStatus = 'SET_LIMITS_STATUS',
|
||||||
SetDashboardLimitStatus = 'SET_DASHBOARD_LIMIT_STATUS',
|
SetDashboardLimitStatus = 'SET_DASHBOARD_LIMIT_STATUS',
|
||||||
|
SetBucketLimitStatus = 'SET_BUCKET_LIMIT_STATUS',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Actions = SetLimits | SetLimitsStatus | SetDashboardLimitStatus
|
export type Actions =
|
||||||
|
| SetLimits
|
||||||
|
| SetLimitsStatus
|
||||||
|
| SetDashboardLimitStatus
|
||||||
|
| SetBucketLimitStatus
|
||||||
|
|
||||||
export interface SetLimits {
|
export interface SetLimits {
|
||||||
type: ActionTypes.SetLimits
|
type: ActionTypes.SetLimits
|
||||||
|
@ -76,6 +81,20 @@ export const setDashboardLimitStatus = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SetBucketLimitStatus {
|
||||||
|
type: ActionTypes.SetBucketLimitStatus
|
||||||
|
payload: {limitStatus: LimitStatus}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setBucketLimitStatus = (
|
||||||
|
limitStatus: LimitStatus
|
||||||
|
): SetBucketLimitStatus => {
|
||||||
|
return {
|
||||||
|
type: ActionTypes.SetBucketLimitStatus,
|
||||||
|
payload: {limitStatus},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface SetLimitsStatus {
|
export interface SetLimitsStatus {
|
||||||
type: ActionTypes.SetLimitsStatus
|
type: ActionTypes.SetLimitsStatus
|
||||||
payload: {
|
payload: {
|
||||||
|
@ -154,3 +173,30 @@ export const checkDashboardLimits = () => (
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const checkBucketLimits = () => async (
|
||||||
|
dispatch,
|
||||||
|
getState: () => AppState
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
buckets: {list},
|
||||||
|
cloud: {
|
||||||
|
limits: {
|
||||||
|
buckets: {maxAllowed},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} = getState()
|
||||||
|
|
||||||
|
const bucketsCount = list.length
|
||||||
|
|
||||||
|
if (maxAllowed <= bucketsCount) {
|
||||||
|
dispatch(setBucketLimitStatus(LimitStatus.EXCEEDED))
|
||||||
|
dispatch(notify(resourceLimitReached('buckets')))
|
||||||
|
} else {
|
||||||
|
dispatch(setBucketLimitStatus(LimitStatus.OK))
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -57,6 +57,10 @@ export const limitsReducer = (
|
||||||
draftState.dashboards.limitStatus = action.payload.limitStatus
|
draftState.dashboards.limitStatus = action.payload.limitStatus
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case ActionTypes.SetBucketLimitStatus: {
|
||||||
|
draftState.buckets.limitStatus = action.payload.limitStatus
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue