Skip to content

Commit

Permalink
feat(operators): Add Migrate Organization Tool (#6748)
Browse files Browse the repository at this point in the history
* feat(operators): Add Migrate Organization Tool

* fix: Update SHA

* fix: Code review feedback

* fix: pocket lint

* fix: Use correct label component
  • Loading branch information
abshierjoel authored Jul 11, 2023
1 parent 99db872 commit 7cc762b
Show file tree
Hide file tree
Showing 11 changed files with 481 additions and 5 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"prettier:fix": "pretty-quick --config .prettierrc.json --write '{src,cypress}/**/*.{ts,tsx}'",
"tsc": "tsc -p ./tsconfig.json --noEmit --pretty --skipLibCheck",
"tsc:watch": "yarn tsc --watch",
"generate": "export SHA=d05381fbcee0dd5d88833e71057a4af647e0d169 && export REMOTE=https://raw.githubusercontent.com/influxdata/openapi/${SHA}/ && yarn generate-meta",
"generate": "export SHA=96ac07e89b65d81b5f84ffbc7279e8bf36353ddb && export REMOTE=https://raw.githubusercontent.com/influxdata/openapi/${SHA}/ && yarn generate-meta",
"generate-local": "export REMOTE=../openapi/ && yarn generate-meta",
"generate-local-cloud": "export REMOTE=../openapi/ && yarn generate-meta-cloud",
"generate-meta": "if [ -z \"${CLOUD_URL}\" ]; then yarn generate-meta-oss; else yarn generate-meta-cloud; fi",
Expand Down
86 changes: 86 additions & 0 deletions src/operator/MigrateOrg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
AlignItems,
Button,
ButtonType,
ComponentColor,
ComponentSize,
ComponentStatus,
FlexBox,
FlexDirection,
Form,
Input,
} from '@influxdata/clockface'
import React, {ChangeEvent, FC, useContext, useState} from 'react'
import {OverlayContext} from 'src/operator/context/overlay'
import {OperatorAccount, getOperatorAccount} from 'src/client/unityRoutes'
import {MigrateOrgOverlay} from './MigrateOrgOverlay'
import {useDispatch} from 'react-redux'
import {notify} from 'src/shared/actions/notifications'
import {getAccountError} from 'src/shared/copy/notifications'

export const MigrateOrg: FC = () => {
const {organization, setMigrateOverlayVisible} = useContext(OverlayContext)
const [toAccountId, setToAccountId] = useState('')
const [toAccount, setToAccount] = useState<OperatorAccount>(null)
const dispatch = useDispatch()

const changeToAccountId = (event: ChangeEvent<HTMLInputElement>) => {
setToAccountId(event.target.value)
}

const submit = async () => {
if (toAccountId === '') {
return
}

try {
const resp = await getOperatorAccount({accountId: toAccountId})
if (resp.status !== 200) {
dispatch(notify(getAccountError(toAccountId)))
return
}
setToAccount(resp.data)
setMigrateOverlayVisible(true)
} catch (error) {
console.error(error)
}
}

const canSubmit = toAccountId !== ''

return organization?.state !== 'provisioned' ? (
<></>
) : (
<>
<MigrateOrgOverlay toAccount={toAccount} />
<h4 data-testid="migrate-org--title">Migrate Org to another Account</h4>
<Form onSubmit={submit}>
<FlexBox
direction={FlexDirection.Row}
alignItems={AlignItems.FlexStart}
margin={ComponentSize.Large}
>
<Input
placeholder="Account ID to migrate resources to"
inputStyle={{width: '400px'}}
style={{width: 'auto'}}
value={toAccountId}
onChange={changeToAccountId}
testID="accounts-migrate--account-id"
/>
<Button
text="migrate"
color={ComponentColor.Primary}
type={ButtonType.Submit}
testID="accounts-migrate--button"
className="accounts-migrate--button"
active={canSubmit}
status={
canSubmit ? ComponentStatus.Default : ComponentStatus.Disabled
}
/>
</FlexBox>
</Form>
</>
)
}
92 changes: 92 additions & 0 deletions src/operator/MigrateOrgOverlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, {FC, useContext} from 'react'
import {
Alert,
ButtonBase,
ButtonShape,
ComponentColor,
ComponentStatus,
Gradients,
IconFont,
Overlay,
RemoteDataState,
} from '@influxdata/clockface'
import {OperatorAccount} from 'src/client/unityRoutes'
import {OverlayContext} from './context/overlay'

interface Props {
toAccount: OperatorAccount
}

const MigrateOrgOverlay: FC<Props> = ({toAccount}) => {
const {
handleMigrateOrg,
migrateOverlayVisible,
migrateStatus,
organization,
setMigrateOverlayVisible,
} = useContext(OverlayContext)

const migrateOrg = () => {
try {
handleMigrateOrg(organization.idpeId, toAccount.id.toString())
} catch (e) {
setMigrateOverlayVisible(false)
}
}

const message = `
This action will migrate the following organization and users
from ${organization.account.id ?? 'N/A'} to
${toAccount?.name} (${toAccount?.id})`

const active = migrateStatus === RemoteDataState.NotStarted

return (
<Overlay
visible={migrateOverlayVisible}
renderMaskElement={() => (
<Overlay.Mask gradient={Gradients.DangerDark} style={{opacity: 0.5}} />
)}
testID="migrate-overlay"
transitionDuration={0}
>
<Overlay.Container maxWidth={600}>
<Overlay.Header
title="Migrate Organization"
style={{color: '#FFFFFF'}}
onDismiss={() => setMigrateOverlayVisible(false)}
/>
<Overlay.Body>
<Alert color={ComponentColor.Danger} icon={IconFont.AlertTriangle}>
This action cannot be undone
</Alert>
<h4 style={{color: '#FFFFFF'}}>
<strong>Warning</strong>
</h4>
{message}
<br />
<strong>Organizations:</strong>
<ul>
<li>
{organization.name ?? 'N/A'} ({organization.idpeId})
</li>
</ul>
</Overlay.Body>
<Overlay.Footer>
<ButtonBase
color={ComponentColor.Primary}
shape={ButtonShape.Default}
onClick={migrateOrg}
testID="migrate-organization--confirmation-button"
active={active}
status={active ? ComponentStatus.Default : ComponentStatus.Disabled}
>
I understand, migrate the organization
</ButtonBase>
</Overlay.Footer>
</Overlay.Container>
</Overlay>
)
}

export {MigrateOrgOverlay}
10 changes: 7 additions & 3 deletions src/operator/OrgOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ import LimitsField from 'src/operator/LimitsField'

// Constants
import {TOOLS_URL} from 'src/shared/constants'
import {MigrateOrg} from './MigrateOrg'

const viewUsageButtonStyles = {marginRight: '12px'}
const reactivateOrgButtonStyles = {marginTop: '8px'}

const OrgOverlay: FC = () => {
export const OrgOverlay: FC = () => {
const {
limits,
limitsStatus,
Expand Down Expand Up @@ -359,6 +360,11 @@ const OrgOverlay: FC = () => {
/>
</Grid.Column>
</Grid.Row>
<Grid.Row>
<Grid.Column widthMD={Columns.Twelve}>
<MigrateOrg />
</Grid.Column>
</Grid.Row>
</SpinnerContainer>
</Grid>
</Panel.Body>
Expand Down Expand Up @@ -393,5 +399,3 @@ const OrgOverlay: FC = () => {
</Overlay>
)
}

export default OrgOverlay
2 changes: 1 addition & 1 deletion src/operator/OrgOverlayWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, {FC} from 'react'
import OverlayProvider from 'src/operator/context/overlay'
import OrgOverlay from 'src/operator/OrgOverlay'
import {OrgOverlay} from 'src/operator/OrgOverlay'

const OrgOverlayWrapper: FC = () => (
<OverlayProvider>
Expand Down
2 changes: 2 additions & 0 deletions src/operator/account/AccountView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import AccountViewHeader from 'src/operator/account/AccountViewHeader'
import AccountGrid from 'src/operator/account/AccountGrid'
import {AccountContext} from 'src/operator/context/account'
import PageSpinner from 'src/perf/components/PageSpinner'
import {MigrateOrgsTool} from './MigrateOrgs'

const AccountView: FC = () => {
const {account, accountStatus} = useContext(AccountContext)
Expand Down Expand Up @@ -45,6 +46,7 @@ const AccountView: FC = () => {
Associated Organizations
</h2>
<AssociatedOrgsTable />
<MigrateOrgsTool />
</Page.Contents>
</Page>
</AppWrapper>
Expand Down
92 changes: 92 additions & 0 deletions src/operator/account/MigrateOrgs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {
AlignItems,
Button,
ButtonType,
ComponentColor,
ComponentSize,
ComponentStatus,
FlexBox,
FlexDirection,
Form,
Input,
} from '@influxdata/clockface'
import React, {ChangeEvent, FC, useContext, useState} from 'react'
import {AccountContext} from '../context/account'
import {MigrateOrgsOverlay} from './MigrateOrgsOverlay'
import {OperatorAccount, getOperatorAccount} from 'src/client/unityRoutes'
import {useDispatch} from 'react-redux'
import {notify} from 'src/shared/actions/notifications'
import {getAccountError} from 'src/shared/copy/notifications'

export const MigrateOrgsTool: FC = () => {
const {account, setMigrateOverlayVisible} = useContext(AccountContext)
const [toAccountId, setToAccountId] = useState('')
const [toAccount, setToAccount] = useState<OperatorAccount>(null)
const dispatch = useDispatch()

const changeToAccountId = (event: ChangeEvent<HTMLInputElement>) => {
setToAccountId(event.target.value)
}

const submit = async () => {
if (toAccountId === '') {
return
}

try {
const resp = await getOperatorAccount({accountId: toAccountId})
if (resp.status !== 200) {
dispatch(notify(getAccountError(toAccountId)))
return
}
setToAccount(resp.data)
setMigrateOverlayVisible(true)
} catch (error) {
console.error(error)
}
}

const canSubmit = toAccountId !== ''

return account.type === 'cancelled' ? (
<></>
) : (
<>
<MigrateOrgsOverlay toAccount={toAccount} />
<h2 data-testid="migrate-resources--title">
Migrate Organizations and Users
</h2>
<Form onSubmit={submit}>
<Form.Label
id="accounts-migrate--account-id-label"
label="Please enter an Account ID to migrate to:"
/>
<FlexBox
direction={FlexDirection.Row}
alignItems={AlignItems.FlexStart}
margin={ComponentSize.Large}
>
<Input
placeholder="Account ID to migrate resources to"
inputStyle={{width: '400px'}}
style={{width: 'auto'}}
value={toAccountId}
onChange={changeToAccountId}
testID="accounts-migrate--account-id"
/>
<Button
text="migrate"
color={ComponentColor.Primary}
type={ButtonType.Submit}
testID="accounts-migrate--button"
className="accounts-migrate--button"
active={canSubmit}
status={
canSubmit ? ComponentStatus.Default : ComponentStatus.Disabled
}
/>
</FlexBox>
</Form>
</>
)
}
Loading

0 comments on commit 7cc762b

Please sign in to comment.