Skip to content

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

Merged
merged 13 commits into from
Sep 24, 2024
Merged

feat: integrate backend with idp sync page #14755

merged 13 commits into from
Sep 24, 2024

Conversation

jaaydenh
Copy link
Contributor

@jaaydenh jaaydenh commented Sep 20, 2024

resolves #14424

  • Populate the groups field, roles field, group regex filter, auto create groups boolean with data
  • Populate groups and role sync settings mapping tables
  • Display group UUID in an error state when a mapped coder group doesn't exist
  • Use tabs to separate groups and roles UI
  • Display disabled UI state for group and role sync fields when they are empty
  • Check for correct experiment, license and permissions
  • Add storybook stories for data driven UI states
  • Handle 'export Policy' button download behavior for groups and roles

idp sync empty
idp sync groups

@jaaydenh jaaydenh self-assigned this Sep 20, 2024
@jaaydenh jaaydenh marked this pull request as ready for review September 23, 2024 14:54
Copy link
Member

@Parkreiner Parkreiner left a comment

Choose a reason for hiding this comment

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

A lot of this looks good, but if this gets pushed through as-is, it will blow up the Coder app at some point

Tried to leave as much feedback as I could, but I think the design/styling issues can be pushed back in favor of making sure the code problems are addressed

@@ -141,6 +141,32 @@ export const provisionerDaemonGroups = (organization: string) => {
};
};

export const getGroupIdpSyncSettingsKey = (organization: string) => [
"organization",
Copy link
Member

Choose a reason for hiding this comment

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

Not sure what we want to go with, but we have some inconsistencies in our query keys right now. We want all query keys that involve organizations to have the exact same prefix so that they can grouped and invalidated together

Right now we're using organization (singular) and organizations (plural). My gut feeling would be to use the plural version for everything, like you would for an HTTP route, but I don't have enough context

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@aslilac Any preference for changing all query keys to be consistent?

Copy link
Member

Choose a reason for hiding this comment

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

I try to match the routes on the backend (which all do include the s)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@aslilac ok I will use plural here, but there are still several keys that do not match the routes, organizationMembersKey, getProvisionerDaemonGroupsKey,

@@ -74,7 +74,7 @@ export const AppearanceSettingsPageView: FC<
<PopoverContent css={{ transform: "translateY(-28px)" }}>
<PopoverPaywall
message="Appearance"
description="With a Premium license, you can customize the appearance of your deployment."
description="With a Premium license, you can customize the appearance and branding of your deployment."
Copy link
Member

Choose a reason for hiding this comment

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

Just curious: since we now have the branding role, have there been any conversations about extending that down the line to work with customer-specific branding?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nothing yet, so far this has purely been focused on the enterprise and premium badges and banners.

Copy link
Member

Choose a reason for hiding this comment

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

nit: theme.branding is not a role, it lives outside of theme.roles, it's just an object/namespace

@@ -29,12 +29,12 @@ import { Link as RouterLink, useNavigate } from "react-router-dom";
import { docs } from "utils/docs";
import { PermissionPillsList } from "./PermissionPillsList";

export type CustomRolesPageViewProps = {
interface CustomRolesPageViewProps {
Copy link
Member

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

Copy link
Contributor Author

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.

Copy link
Member

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 think interface WibbleProps extends WobbleProps {} is a lot clearer than type WibbleProps = WobbleProps & {} in the places where we need it. and I think we just generally use it in more places rn.

],
"idp-group-2": ["fbd2116a-8961-4954-87ae-e4575bd29ce0"],
},
regex_filter: "@[a-zA-Z0-9_]+",
Copy link
Member

Choose a reason for hiding this comment

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

I know it's mock data, but just in case it has any bearing on regex code, this regex can be simplified as @\w+. The word class includes digits and underscores by default

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the regex really can be anything, I did want to see a regex with a bit longer length.

policy,
type,
organization,
download = saveAs,
Copy link
Member

Choose a reason for hiding this comment

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

Not sure how big of a deal this is, but the current implementation means that we can't add additional functionality without completely overriding the default saveAs function. Every consumer would have to import saveAs themselves, and then wire up the functionality for component implementation

How often would we have a use case where we'd want to get rid of the saveAs functionality vs wanting to always have saveAs, and then potentially add more functionality on top?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was meant to just support the Storybook test case. Is there a better way to handle this?

Comment on lines 68 to 75
const rolePolicy =
roleSyncSettings?.field && roleSyncSettings.mapping
? JSON.stringify(roleSyncSettings, null, 2)
: null;
const groupPolicy =
groupSyncSettings?.field && groupSyncSettings.mapping
? JSON.stringify(groupSyncSettings, null, 2)
: null;
Copy link
Member

Choose a reason for hiding this comment

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

This is dangerous – we need to be super, super careful with any JSON.stringify calls inside components:

  1. Stringifying on every render can be slow
  2. JSON.stringify can throw, so if it fails, we might blow up the entire app

organization: Organization;
}

export const IdpSyncPageView: FC<IdpSyncPageViewProps> = ({
Copy link
Member

Choose a reason for hiding this comment

The 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 IdpField component. Since we're using two different typefaces, their baselines are slightly different. And when you place them right next to each other on the same line, there's enough variation that the text looks sloppy

You might have to open this in a new tab, but the baselines are ever so slightly off, and the cap-heights are, too:
Screenshot 2024-09-23 at 12 21 28 PM

Copy link
Member

Choose a reason for hiding this comment

The 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

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

{fieldText ? (
<p css={styles.fieldText}>{fieldText}</p>
) : (
showStatusIndicator && (
Copy link
Member

Choose a reason for hiding this comment

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

Is this the right name for the prop if it's only being used to show disabled statuses?

Copy link
Contributor Author

@jaaydenh jaaydenh Sep 23, 2024

Choose a reason for hiding this comment

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

I thought it may be used for more in the future but Ill rename it for now.

Copy link
Member

@aslilac aslilac left a comment

Choose a reason for hiding this comment

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

looks good, just a few small things!

Comment on lines +716 to +718
/**
* @param organization Can be the organization's ID or name
*/
Copy link
Member

Choose a reason for hiding this comment

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

can you add this above the getGroup... function too pls?

@@ -141,6 +141,32 @@ export const provisionerDaemonGroups = (organization: string) => {
};
};

export const getGroupIdpSyncSettingsKey = (organization: string) => [
"organization",
Copy link
Member

Choose a reason for hiding this comment

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

I try to match the routes on the backend (which all do include the s)

@@ -74,7 +74,7 @@ export const AppearanceSettingsPageView: FC<
<PopoverContent css={{ transform: "translateY(-28px)" }}>
<PopoverPaywall
message="Appearance"
description="With a Premium license, you can customize the appearance of your deployment."
description="With a Premium license, you can customize the appearance and branding of your deployment."
Copy link
Member

Choose a reason for hiding this comment

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

nit: theme.branding is not a role, it lives outside of theme.roles, it's just an object/namespace

@@ -29,12 +29,12 @@ import { Link as RouterLink, useNavigate } from "react-router-dom";
import { docs } from "utils/docs";
import { PermissionPillsList } from "./PermissionPillsList";

export type CustomRolesPageViewProps = {
interface CustomRolesPageViewProps {
Copy link
Member

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 think interface WibbleProps extends WobbleProps {} is a lot clearer than type WibbleProps = WobbleProps & {} in the places where we need it. and I think we just generally use it in more places rn.

`${MockOrganization.name}_groups-policy.json`,
),
);
const blob: Blob = (args.download as jest.Mock).mock.calls[0][0];
Copy link
Member

Choose a reason for hiding this comment

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

oh this is so gross (but I don't know what to suggest to make it better)

could you add a comment above it to elaborate why this incantation is justified/necessary? I think I understand what it's doing, but clarity is good, and more junior devs might not get it!

Copy link
Member

@Parkreiner Parkreiner left a comment

Choose a reason for hiding this comment

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

Not sure if you ended up pushing up the lastCalled change (it's not showing up in GitHub), but the changes look good overall!

I'll make a separate issue about the accessibility issues for PopoverTrigger

@jaaydenh jaaydenh merged commit a3ebcd7 into main Sep 24, 2024
27 checks passed
@jaaydenh jaaydenh deleted the jaaydenh/idp-sync branch September 24, 2024 02:07
@github-actions github-actions bot locked and limited conversation to collaborators Sep 24, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

UI: Integrate backend data with skeleton UI for IDP Sync
3 participants