Skip to content

Commit 7cc762b

Browse files
authored
feat(operators): Add Migrate Organization Tool (#6748)
* feat(operators): Add Migrate Organization Tool * fix: Update SHA * fix: Code review feedback * fix: pocket lint * fix: Use correct label component
1 parent 99db872 commit 7cc762b

File tree

11 files changed

+481
-5
lines changed

11 files changed

+481
-5
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"prettier:fix": "pretty-quick --config .prettierrc.json --write '{src,cypress}/**/*.{ts,tsx}'",
5353
"tsc": "tsc -p ./tsconfig.json --noEmit --pretty --skipLibCheck",
5454
"tsc:watch": "yarn tsc --watch",
55-
"generate": "export SHA=d05381fbcee0dd5d88833e71057a4af647e0d169 && export REMOTE=https://raw.githubusercontent.com/influxdata/openapi/${SHA}/ && yarn generate-meta",
55+
"generate": "export SHA=96ac07e89b65d81b5f84ffbc7279e8bf36353ddb && export REMOTE=https://raw.githubusercontent.com/influxdata/openapi/${SHA}/ && yarn generate-meta",
5656
"generate-local": "export REMOTE=../openapi/ && yarn generate-meta",
5757
"generate-local-cloud": "export REMOTE=../openapi/ && yarn generate-meta-cloud",
5858
"generate-meta": "if [ -z \"${CLOUD_URL}\" ]; then yarn generate-meta-oss; else yarn generate-meta-cloud; fi",

src/operator/MigrateOrg.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {
2+
AlignItems,
3+
Button,
4+
ButtonType,
5+
ComponentColor,
6+
ComponentSize,
7+
ComponentStatus,
8+
FlexBox,
9+
FlexDirection,
10+
Form,
11+
Input,
12+
} from '@influxdata/clockface'
13+
import React, {ChangeEvent, FC, useContext, useState} from 'react'
14+
import {OverlayContext} from 'src/operator/context/overlay'
15+
import {OperatorAccount, getOperatorAccount} from 'src/client/unityRoutes'
16+
import {MigrateOrgOverlay} from './MigrateOrgOverlay'
17+
import {useDispatch} from 'react-redux'
18+
import {notify} from 'src/shared/actions/notifications'
19+
import {getAccountError} from 'src/shared/copy/notifications'
20+
21+
export const MigrateOrg: FC = () => {
22+
const {organization, setMigrateOverlayVisible} = useContext(OverlayContext)
23+
const [toAccountId, setToAccountId] = useState('')
24+
const [toAccount, setToAccount] = useState<OperatorAccount>(null)
25+
const dispatch = useDispatch()
26+
27+
const changeToAccountId = (event: ChangeEvent<HTMLInputElement>) => {
28+
setToAccountId(event.target.value)
29+
}
30+
31+
const submit = async () => {
32+
if (toAccountId === '') {
33+
return
34+
}
35+
36+
try {
37+
const resp = await getOperatorAccount({accountId: toAccountId})
38+
if (resp.status !== 200) {
39+
dispatch(notify(getAccountError(toAccountId)))
40+
return
41+
}
42+
setToAccount(resp.data)
43+
setMigrateOverlayVisible(true)
44+
} catch (error) {
45+
console.error(error)
46+
}
47+
}
48+
49+
const canSubmit = toAccountId !== ''
50+
51+
return organization?.state !== 'provisioned' ? (
52+
<></>
53+
) : (
54+
<>
55+
<MigrateOrgOverlay toAccount={toAccount} />
56+
<h4 data-testid="migrate-org--title">Migrate Org to another Account</h4>
57+
<Form onSubmit={submit}>
58+
<FlexBox
59+
direction={FlexDirection.Row}
60+
alignItems={AlignItems.FlexStart}
61+
margin={ComponentSize.Large}
62+
>
63+
<Input
64+
placeholder="Account ID to migrate resources to"
65+
inputStyle={{width: '400px'}}
66+
style={{width: 'auto'}}
67+
value={toAccountId}
68+
onChange={changeToAccountId}
69+
testID="accounts-migrate--account-id"
70+
/>
71+
<Button
72+
text="migrate"
73+
color={ComponentColor.Primary}
74+
type={ButtonType.Submit}
75+
testID="accounts-migrate--button"
76+
className="accounts-migrate--button"
77+
active={canSubmit}
78+
status={
79+
canSubmit ? ComponentStatus.Default : ComponentStatus.Disabled
80+
}
81+
/>
82+
</FlexBox>
83+
</Form>
84+
</>
85+
)
86+
}

src/operator/MigrateOrgOverlay.tsx

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import React, {FC, useContext} from 'react'
2+
import {
3+
Alert,
4+
ButtonBase,
5+
ButtonShape,
6+
ComponentColor,
7+
ComponentStatus,
8+
Gradients,
9+
IconFont,
10+
Overlay,
11+
RemoteDataState,
12+
} from '@influxdata/clockface'
13+
import {OperatorAccount} from 'src/client/unityRoutes'
14+
import {OverlayContext} from './context/overlay'
15+
16+
interface Props {
17+
toAccount: OperatorAccount
18+
}
19+
20+
const MigrateOrgOverlay: FC<Props> = ({toAccount}) => {
21+
const {
22+
handleMigrateOrg,
23+
migrateOverlayVisible,
24+
migrateStatus,
25+
organization,
26+
setMigrateOverlayVisible,
27+
} = useContext(OverlayContext)
28+
29+
const migrateOrg = () => {
30+
try {
31+
handleMigrateOrg(organization.idpeId, toAccount.id.toString())
32+
} catch (e) {
33+
setMigrateOverlayVisible(false)
34+
}
35+
}
36+
37+
const message = `
38+
This action will migrate the following organization and users
39+
from ${organization.account.id ?? 'N/A'} to
40+
${toAccount?.name} (${toAccount?.id})`
41+
42+
const active = migrateStatus === RemoteDataState.NotStarted
43+
44+
return (
45+
<Overlay
46+
visible={migrateOverlayVisible}
47+
renderMaskElement={() => (
48+
<Overlay.Mask gradient={Gradients.DangerDark} style={{opacity: 0.5}} />
49+
)}
50+
testID="migrate-overlay"
51+
transitionDuration={0}
52+
>
53+
<Overlay.Container maxWidth={600}>
54+
<Overlay.Header
55+
title="Migrate Organization"
56+
style={{color: '#FFFFFF'}}
57+
onDismiss={() => setMigrateOverlayVisible(false)}
58+
/>
59+
<Overlay.Body>
60+
<Alert color={ComponentColor.Danger} icon={IconFont.AlertTriangle}>
61+
This action cannot be undone
62+
</Alert>
63+
<h4 style={{color: '#FFFFFF'}}>
64+
<strong>Warning</strong>
65+
</h4>
66+
{message}
67+
<br />
68+
<strong>Organizations:</strong>
69+
<ul>
70+
<li>
71+
{organization.name ?? 'N/A'} ({organization.idpeId})
72+
</li>
73+
</ul>
74+
</Overlay.Body>
75+
<Overlay.Footer>
76+
<ButtonBase
77+
color={ComponentColor.Primary}
78+
shape={ButtonShape.Default}
79+
onClick={migrateOrg}
80+
testID="migrate-organization--confirmation-button"
81+
active={active}
82+
status={active ? ComponentStatus.Default : ComponentStatus.Disabled}
83+
>
84+
I understand, migrate the organization
85+
</ButtonBase>
86+
</Overlay.Footer>
87+
</Overlay.Container>
88+
</Overlay>
89+
)
90+
}
91+
92+
export {MigrateOrgOverlay}

src/operator/OrgOverlay.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@ import LimitsField from 'src/operator/LimitsField'
3434

3535
// Constants
3636
import {TOOLS_URL} from 'src/shared/constants'
37+
import {MigrateOrg} from './MigrateOrg'
3738

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

41-
const OrgOverlay: FC = () => {
42+
export const OrgOverlay: FC = () => {
4243
const {
4344
limits,
4445
limitsStatus,
@@ -359,6 +360,11 @@ const OrgOverlay: FC = () => {
359360
/>
360361
</Grid.Column>
361362
</Grid.Row>
363+
<Grid.Row>
364+
<Grid.Column widthMD={Columns.Twelve}>
365+
<MigrateOrg />
366+
</Grid.Column>
367+
</Grid.Row>
362368
</SpinnerContainer>
363369
</Grid>
364370
</Panel.Body>
@@ -393,5 +399,3 @@ const OrgOverlay: FC = () => {
393399
</Overlay>
394400
)
395401
}
396-
397-
export default OrgOverlay

src/operator/OrgOverlayWrapper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, {FC} from 'react'
22
import OverlayProvider from 'src/operator/context/overlay'
3-
import OrgOverlay from 'src/operator/OrgOverlay'
3+
import {OrgOverlay} from 'src/operator/OrgOverlay'
44

55
const OrgOverlayWrapper: FC = () => (
66
<OverlayProvider>

src/operator/account/AccountView.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import AccountViewHeader from 'src/operator/account/AccountViewHeader'
1414
import AccountGrid from 'src/operator/account/AccountGrid'
1515
import {AccountContext} from 'src/operator/context/account'
1616
import PageSpinner from 'src/perf/components/PageSpinner'
17+
import {MigrateOrgsTool} from './MigrateOrgs'
1718

1819
const AccountView: FC = () => {
1920
const {account, accountStatus} = useContext(AccountContext)
@@ -45,6 +46,7 @@ const AccountView: FC = () => {
4546
Associated Organizations
4647
</h2>
4748
<AssociatedOrgsTable />
49+
<MigrateOrgsTool />
4850
</Page.Contents>
4951
</Page>
5052
</AppWrapper>
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {
2+
AlignItems,
3+
Button,
4+
ButtonType,
5+
ComponentColor,
6+
ComponentSize,
7+
ComponentStatus,
8+
FlexBox,
9+
FlexDirection,
10+
Form,
11+
Input,
12+
} from '@influxdata/clockface'
13+
import React, {ChangeEvent, FC, useContext, useState} from 'react'
14+
import {AccountContext} from '../context/account'
15+
import {MigrateOrgsOverlay} from './MigrateOrgsOverlay'
16+
import {OperatorAccount, getOperatorAccount} from 'src/client/unityRoutes'
17+
import {useDispatch} from 'react-redux'
18+
import {notify} from 'src/shared/actions/notifications'
19+
import {getAccountError} from 'src/shared/copy/notifications'
20+
21+
export const MigrateOrgsTool: FC = () => {
22+
const {account, setMigrateOverlayVisible} = useContext(AccountContext)
23+
const [toAccountId, setToAccountId] = useState('')
24+
const [toAccount, setToAccount] = useState<OperatorAccount>(null)
25+
const dispatch = useDispatch()
26+
27+
const changeToAccountId = (event: ChangeEvent<HTMLInputElement>) => {
28+
setToAccountId(event.target.value)
29+
}
30+
31+
const submit = async () => {
32+
if (toAccountId === '') {
33+
return
34+
}
35+
36+
try {
37+
const resp = await getOperatorAccount({accountId: toAccountId})
38+
if (resp.status !== 200) {
39+
dispatch(notify(getAccountError(toAccountId)))
40+
return
41+
}
42+
setToAccount(resp.data)
43+
setMigrateOverlayVisible(true)
44+
} catch (error) {
45+
console.error(error)
46+
}
47+
}
48+
49+
const canSubmit = toAccountId !== ''
50+
51+
return account.type === 'cancelled' ? (
52+
<></>
53+
) : (
54+
<>
55+
<MigrateOrgsOverlay toAccount={toAccount} />
56+
<h2 data-testid="migrate-resources--title">
57+
Migrate Organizations and Users
58+
</h2>
59+
<Form onSubmit={submit}>
60+
<Form.Label
61+
id="accounts-migrate--account-id-label"
62+
label="Please enter an Account ID to migrate to:"
63+
/>
64+
<FlexBox
65+
direction={FlexDirection.Row}
66+
alignItems={AlignItems.FlexStart}
67+
margin={ComponentSize.Large}
68+
>
69+
<Input
70+
placeholder="Account ID to migrate resources to"
71+
inputStyle={{width: '400px'}}
72+
style={{width: 'auto'}}
73+
value={toAccountId}
74+
onChange={changeToAccountId}
75+
testID="accounts-migrate--account-id"
76+
/>
77+
<Button
78+
text="migrate"
79+
color={ComponentColor.Primary}
80+
type={ButtonType.Submit}
81+
testID="accounts-migrate--button"
82+
className="accounts-migrate--button"
83+
active={canSubmit}
84+
status={
85+
canSubmit ? ComponentStatus.Default : ComponentStatus.Disabled
86+
}
87+
/>
88+
</FlexBox>
89+
</Form>
90+
</>
91+
)
92+
}

0 commit comments

Comments
 (0)