-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
🪟 🎉 Multi-cloud: edit connection & workspace data geographies in the UI #18611
Changes from 25 commits
e3c4e53
511d2e3
3396376
790520c
2002be5
59efe5d
5c6c347
52266f6
2c13e99
b37db85
231182a
496f5cd
e84242a
4355e4e
c8648f6
8c5f984
2e82f61
2cbf4d4
cbbef13
1ba3167
a62ff9b
cf88059
10fbc49
c15946a
bf21502
71db34a
c7d0fe4
eb44d45
27edabf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
@use "scss/variables"; | ||
|
||
.flexRow { | ||
display: flex; | ||
flex-direction: row; | ||
justify-content: flex-start; | ||
align-items: flex-start; | ||
gap: variables.$spacing-md; | ||
} | ||
|
||
.leftFieldCol { | ||
flex: 1; | ||
max-width: 640px; | ||
padding-right: 30px; | ||
} | ||
|
||
.rightFieldCol { | ||
flex: 1; | ||
max-width: 300px; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { Field, FieldProps, useFormikContext } from "formik"; | ||
import { FormattedMessage, useIntl } from "react-intl"; | ||
|
||
import { ControlLabels } from "components/LabeledControl"; | ||
import { DropDown } from "components/ui/DropDown"; | ||
|
||
import { useAvailableGeographies } from "packages/cloud/services/geographies/GeographiesService"; | ||
import { links } from "utils/links"; | ||
import { Section } from "views/Connection/ConnectionForm/components/Section"; | ||
|
||
import styles from "./DataResidency.module.scss"; | ||
|
||
interface DataResidencyProps { | ||
name?: string; | ||
} | ||
|
||
export const DataResidency: React.FC<DataResidencyProps> = ({ name = "geography" }) => { | ||
const { formatMessage } = useIntl(); | ||
const { setFieldValue } = useFormikContext(); | ||
const { geographies } = useAvailableGeographies(); | ||
|
||
return ( | ||
<Section title={formatMessage({ id: "connection.geographyTitle" })}> | ||
<Field name={name}> | ||
{({ field, form }: FieldProps<string>) => ( | ||
<div className={styles.flexRow}> | ||
<div className={styles.leftFieldCol}> | ||
<ControlLabels | ||
nextLine | ||
label={<FormattedMessage id="connection.geographyTitle" />} | ||
message={ | ||
<FormattedMessage | ||
id="connection.geographyDescription" | ||
values={{ | ||
lnk: (node: React.ReactNode) => ( | ||
<a href={links.cloudAllowlistIPsLink} target="_blank" rel="noreferrer"> | ||
{node} | ||
</a> | ||
), | ||
}} | ||
/> | ||
} | ||
/> | ||
</div> | ||
<div className={styles.rightFieldCol}> | ||
<DropDown | ||
isDisabled={form.isSubmitting} | ||
options={geographies.map((geography) => ({ | ||
label: formatMessage({ id: `connection.geography.${geography}` }), | ||
value: geography, | ||
}))} | ||
value={field.value} | ||
onChange={(geography) => setFieldValue(name, geography.value)} | ||
/> | ||
</div> | ||
</div> | ||
)} | ||
</Field> | ||
</Section> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,26 +3,11 @@ import React from "react"; | |
import { ControlLabels, ControlLabelsProps } from "components/LabeledControl"; | ||
import { Input, InputProps } from "components/ui/Input"; | ||
|
||
type LabeledInputProps = Pick<ControlLabelsProps, "success" | "message" | "label" | "labelAdditionLength"> & | ||
type LabeledInputProps = Pick<ControlLabelsProps, "success" | "message" | "label"> & | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
InputProps & { className?: string }; | ||
|
||
const LabeledInput: React.FC<LabeledInputProps> = ({ | ||
error, | ||
success, | ||
message, | ||
label, | ||
labelAdditionLength, | ||
className, | ||
...inputProps | ||
}) => ( | ||
<ControlLabels | ||
error={error} | ||
success={success} | ||
message={message} | ||
label={label} | ||
className={className} | ||
labelAdditionLength={labelAdditionLength} | ||
> | ||
const LabeledInput: React.FC<LabeledInputProps> = ({ error, success, message, label, className, ...inputProps }) => ( | ||
<ControlLabels error={error} success={success} message={message} label={label} className={className}> | ||
<Input {...inputProps} error={error} /> | ||
</ControlLabels> | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
.wrapper { | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
} | ||
|
||
.dropdownWrapper { | ||
display: flex; | ||
flex: 1 0 310px; | ||
} | ||
|
||
.spinner { | ||
width: 50px; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
} | ||
|
||
.dropdown { | ||
flex-grow: 1; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import React, { useState } from "react"; | ||
import { FormattedMessage, useIntl } from "react-intl"; | ||
|
||
import { ControlLabels } from "components/LabeledControl"; | ||
import { Card } from "components/ui/Card"; | ||
import { DropDown } from "components/ui/DropDown"; | ||
import { Spinner } from "components/ui/Spinner"; | ||
|
||
import { Geography } from "core/request/AirbyteClient"; | ||
import { useConnectionEditService } from "hooks/services/ConnectionEdit/ConnectionEditService"; | ||
import { useNotificationService } from "hooks/services/Notification"; | ||
import { useAvailableGeographies } from "packages/cloud/services/geographies/GeographiesService"; | ||
import { links } from "utils/links"; | ||
|
||
import styles from "./UpdateConnectionDataResidency.module.scss"; | ||
|
||
export const UpdateConnectionDataResidency: React.FC = () => { | ||
const { connection, updateConnection, connectionUpdating } = useConnectionEditService(); | ||
const { registerNotification } = useNotificationService(); | ||
const { formatMessage } = useIntl(); | ||
const [selectedValue, setSelectedValue] = useState<Geography>(); | ||
|
||
const { geographies } = useAvailableGeographies(); | ||
|
||
const handleSubmit = async ({ value }: { value: Geography }) => { | ||
try { | ||
setSelectedValue(value); | ||
await updateConnection({ | ||
connectionId: connection.connectionId, | ||
geography: value, | ||
}); | ||
} catch (e) { | ||
registerNotification({ | ||
id: "connection.geographyUpdateError", | ||
title: formatMessage({ id: "connection.geographyUpdateError" }), | ||
isError: true, | ||
}); | ||
} | ||
setSelectedValue(undefined); | ||
}; | ||
|
||
return ( | ||
<Card withPadding> | ||
<div className={styles.wrapper}> | ||
<div> | ||
<ControlLabels | ||
nextLine | ||
label={<FormattedMessage id="connection.geographyTitle" />} | ||
message={ | ||
<FormattedMessage | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be worth to point here out, when this will take effect? I assume it's not cancelling the current running sync and I also assume it doesn't require a reset, so simply the next sync/reset jobs will be done in that region? Maybe worth putting something like: "This will take effect for all syncs and resets starting after the change." (or something better worded). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is almost self-explanatory and not necessary to explain, but maybe @andyjih can weigh in. It's also something we could consider adding to the docs instead of directly in the UI. Here's a proposal for the wording if we do want to mention it:
|
||
id="connection.geographyDescription" | ||
values={{ | ||
lnk: (node: React.ReactNode) => ( | ||
<a href={links.cloudAllowlistIPsLink} target="_blank" rel="noreferrer"> | ||
{node} | ||
</a> | ||
), | ||
}} | ||
/> | ||
} | ||
/> | ||
</div> | ||
<div className={styles.dropdownWrapper}> | ||
<div className={styles.spinner}>{connectionUpdating && <Spinner small />}</div> | ||
josephkmh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<div className={styles.dropdown}> | ||
<DropDown | ||
isDisabled={connectionUpdating} | ||
options={geographies.map((geography) => ({ | ||
label: formatMessage({ id: `connection.geography.${geography}` }), | ||
josephkmh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
value: geography, | ||
}))} | ||
value={selectedValue || connection.geography} | ||
onChange={handleSubmit} | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
</Card> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { UpdateConnectionDataResidency } from "./UpdateConnectionDataResidency"; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,7 +27,7 @@ interface SideMenuProps { | |
} | ||
|
||
const Content = styled.nav` | ||
min-width: 147px; | ||
min-width: 155px; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The sidebar is ever-so-slightly too small for |
||
`; | ||
|
||
const Category = styled.div` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ export enum FeatureItem { | |
AllowUpdateConnectors = "ALLOW_UPDATE_CONNECTORS", | ||
AllowOAuthConnector = "ALLOW_OAUTH_CONNECTOR", | ||
AllowSync = "ALLOW_SYNC", | ||
AllowChangeDataGeographies = "ALLOW_CHANGE_DATA_GEOGRAPHIES", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ability to change data geographies is behind a feature flag. It's not available in OSS, and it is currently disabled in cloud. We will do a staged rollout in production to test that it's working as expected. |
||
AllowSyncSubOneHourCronExpressions = "ALLOW_SYNC_SUB_ONE_HOUR_CRON_EXPRESSIONS", | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is
false
for now, but can be overridden in LaunchDarkly for testing