Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(operators): Add Migrate Organization Tool #6748

Merged
merged 5 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
wdoconnell marked this conversation as resolved.
Show resolved Hide resolved
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a notification here - like "please enter an account to migrate to"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's a good point. This is a pretty blind failure. It seems like I can pull use the useDispatch() hook with notify to put up an error. Does that make sense?

Also, remocal is failing me... so I'm pushing up these changes to run in CI 😅 🤞

}

try {
const resp = await getOperatorAccount({accountId: toAccountId})
if (resp.status !== 200) {
dispatch(notify(getAccountError(toAccountId)))
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same - could we add a notification letting the user know there was an error retrieving the ToAccount?

}
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