-
Notifications
You must be signed in to change notification settings - Fork 944
feat: integrate backend with idp sync page #14755
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
Changes from 1 commit
a40ba7d
7ff9a07
6e303f4
310726b
615c8d3
45f4d13
080b032
8e1e021
c863619
41631f0
3db9f48
431beea
f166784
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import DownloadOutlined from "@mui/icons-material/DownloadOutlined"; | ||
import Button from "@mui/material/Button"; | ||
import type { Organization } from "api/typesGenerated"; | ||
import { displayError } from "components/GlobalSnackbar/utils"; | ||
import { saveAs } from "file-saver"; | ||
import { type FC, useState } from "react"; | ||
|
||
interface DownloadPolicyButtonProps { | ||
policy: string | null; | ||
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. Is there any information on how policies are defined? I'm seeing that we're downloading just the policy itself when the user clicks the button 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 policy is meant to be a json representation of |
||
type: "groups" | "roles"; | ||
organization: Organization; | ||
} | ||
|
||
export const ExportPolicyButton: FC<DownloadPolicyButtonProps> = ({ | ||
policy, | ||
type, | ||
organization, | ||
}) => { | ||
const [isDownloading, setIsDownloading] = useState(false); | ||
|
||
return ( | ||
<Button | ||
startIcon={<DownloadOutlined />} | ||
disabled={!policy || isDownloading} | ||
onClick={async () => { | ||
if (policy) { | ||
try { | ||
setIsDownloading(true); | ||
const file = new Blob([policy], { | ||
type: "application/json", | ||
}); | ||
saveAs(file, `${organization.name}_${type}-policy.json`); | ||
} catch (e) { | ||
console.error(e); | ||
displayError("Failed to export policy json"); | ||
} finally { | ||
setIsDownloading(false); | ||
} | ||
} | ||
}} | ||
> | ||
Export Policy | ||
</Button> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
import type { Interpolation, Theme } from "@emotion/react"; | ||
import AddIcon from "@mui/icons-material/AddOutlined"; | ||
import LaunchOutlined from "@mui/icons-material/LaunchOutlined"; | ||
import Button from "@mui/material/Button"; | ||
import Skeleton from "@mui/material/Skeleton"; | ||
|
@@ -12,6 +11,7 @@ import TableRow from "@mui/material/TableRow"; | |
import type { | ||
Group, | ||
GroupSyncSettings, | ||
Organization, | ||
RoleSyncSettings, | ||
} from "api/typesGenerated"; | ||
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"; | ||
|
@@ -28,18 +28,21 @@ import type { FC } from "react"; | |
import { useSearchParams } from "react-router-dom"; | ||
import { MONOSPACE_FONT_FAMILY } from "theme/constants"; | ||
import { docs } from "utils/docs"; | ||
import { ExportPolicyButton } from "./ExportPolicyButton"; | ||
import { PillList } from "./PillList"; | ||
|
||
export type IdpSyncPageViewProps = { | ||
interface IdpSyncPageViewProps { | ||
groupSyncSettings: GroupSyncSettings | undefined; | ||
roleSyncSettings: RoleSyncSettings | undefined; | ||
groups: Group[] | undefined; | ||
}; | ||
organization: Organization; | ||
} | ||
|
||
export const IdpSyncPageView: FC<IdpSyncPageViewProps> = ({ | ||
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. Have to put the comment here because the code was already in place previously, and it's not selectable for review: I think we need to add some CSS optical adjustments to the key-value pairs for the You might have to open this in a new tab, but the baselines are ever so slightly off, and the cap-heights are, too: 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. Though a slightly easier and probably much more manageable alternative would be to set all the text in monospace 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 monospace font was supposed to be a temporary measure until this page becomes a form and these would become fields. I'll see if I can improve it for now. 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. as a temporary fix, I can add some padding to make the baselines match. I really wanted to use the monospace font on only the field value to signify that it is the value of the configuration. |
||
groupSyncSettings, | ||
roleSyncSettings, | ||
groups, | ||
organization, | ||
}) => { | ||
const [searchParams] = useSearchParams(); | ||
const groupsMap = new Map<string, string>(); | ||
|
@@ -62,6 +65,15 @@ export const IdpSyncPageView: FC<IdpSyncPageViewProps> = ({ | |
? Object.entries(roleSyncSettings.mapping).length | ||
: 0; | ||
|
||
const rolePolicy = | ||
roleSyncSettings?.field && roleSyncSettings.mapping | ||
? JSON.stringify(roleSyncSettings, null, 2) | ||
: null; | ||
const groupPolicy = | ||
groupSyncSettings?.field && groupSyncSettings.mapping | ||
? JSON.stringify(groupSyncSettings, null, 2) | ||
: null; | ||
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. This is dangerous – we need to be super, super careful with any
|
||
|
||
return ( | ||
<> | ||
<ChooseOne> | ||
|
@@ -77,7 +89,7 @@ export const IdpSyncPageView: FC<IdpSyncPageViewProps> = ({ | |
<Tabs | ||
active={tab} | ||
css={{ | ||
marginBottom: 24, | ||
marginBottom: 20, | ||
jaaydenh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}} | ||
> | ||
<TabsList> | ||
|
@@ -121,14 +133,11 @@ export const IdpSyncPageView: FC<IdpSyncPageViewProps> = ({ | |
css={styles.tableInfo} | ||
> | ||
<TableRowCount count={groupMappingCount} type="groups" /> | ||
<Button | ||
component="a" | ||
startIcon={<AddIcon />} | ||
// to="export" | ||
href={docs("/admin/auth#group-sync-enterprise")} | ||
> | ||
Export Policy | ||
</Button> | ||
<ExportPolicyButton | ||
policy={groupPolicy} | ||
organization={organization} | ||
type="groups" | ||
/> | ||
</Stack> | ||
<Stack spacing={6}> | ||
<IdpMappingTable | ||
|
@@ -164,14 +173,11 @@ export const IdpSyncPageView: FC<IdpSyncPageViewProps> = ({ | |
css={styles.tableInfo} | ||
> | ||
<TableRowCount count={roleMappingCount} type="roles" /> | ||
<Button | ||
component="a" | ||
startIcon={<AddIcon />} | ||
// to="export" | ||
href={docs("/admin/auth#group-sync-enterprise")} | ||
> | ||
Export Policy | ||
</Button> | ||
<ExportPolicyButton | ||
policy={rolePolicy} | ||
organization={organization} | ||
type="roles" | ||
/> | ||
</Stack> | ||
<IdpMappingTable | ||
type="Role" | ||
|
@@ -210,7 +216,7 @@ const IdpField: FC<IdpFieldProps> = ({ | |
}) => { | ||
return ( | ||
<span css={{ display: "flex", alignItems: "center", gap: "16px" }}> | ||
<p>{name}</p> | ||
<p css={styles.fieldLabel}>{name}</p> | ||
{fieldText ? ( | ||
<p css={styles.fieldText}>{fieldText}</p> | ||
) : ( | ||
|
@@ -365,12 +371,16 @@ const TableLoader = () => { | |
|
||
const styles = { | ||
fieldText: (theme) => ({ | ||
color: theme.palette.text.secondary, | ||
fontFamily: MONOSPACE_FONT_FAMILY, | ||
whiteSpace: "nowrap", | ||
}), | ||
fieldLabel: (theme) => ({ | ||
color: theme.palette.text.secondary, | ||
}), | ||
fields: () => ({ | ||
marginBottom: 20, | ||
marginBottom: 16, | ||
marginLeft: 16, | ||
fontSize: 14, | ||
}), | ||
tableInfo: () => ({ | ||
marginBottom: 16, | ||
|
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.
Have we decided on whether we prefer interfaces over type aliases? Asking because I generally avoid interfaces so that I can't do accidental interface merging, but I've been seeing a lot of recent code using interfaces
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.
My goal was just to be consistent and I saw more interfaces used for all the organization related pages. I don't personally have a strong preference other than being consistent.
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.
I like
interface
because I thinkinterface WibbleProps extends WobbleProps {}
is a lot clearer thantype WibbleProps = WobbleProps & {}
in the places where we need it. and I think we just generally use it in more places rn.