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

Allow widget visibility functions to be used #69

Merged

Conversation

CodyWMitchell
Copy link
Contributor

@CodyWMitchell CodyWMitchell commented Apr 26, 2024

Description

Check the permissions defined in the backend widget configuration using chrome's visibilityFunctions. Remove widgets from the layout and widget drawer that the user does not have permissions to use.

RHCLOUD-32312

Checklist ☑️

  • PR only fixes one issue or story
  • Change reviewed for extraneous code
  • UI best practices adhered to
  • Commits squashed and meaningfully named
  • All PR checks pass locally (build, lint, test, E2E)

  • (Optional) QE: Needs QE attention (OUIA changed, perceived impact to tests, no test coverage)
  • (Optional) QE: Has been mentioned
  • (Optional) UX: Needs UX attention (end user UX modified, missing designs)
  • (Optional) UX: Has been mentioned

Copy link
Contributor

@karelhala karelhala left a comment

Choose a reason for hiding this comment

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

I like how you cleaned the previous isOrg attempt! Two adjustments are needed to fully utilize all of the visibility functions.

Comment on lines 52 to 57
for (const key of Object.keys(mapping)) {
const widgetConfig = mapping[key].config;
if (widgetConfig && widgetConfig.permissions && !(await checkPermissions(widgetConfig.permissions as WidgetPermission[]))) {
delete mapping[key]; // remove widget from mapping if user does not have permissions for it
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to use for with nested for. It's a bit more elegant with reduce function, moreover the checkPermissions function returns Promise and Boolean(Promise) == true. We'd not really filter out the values.

Object.entries(mapping).reduce(async (acc, [key, value]) => {
  // calculate visibility
  if (await isVisible) {
    acc[key] = value;
  }
  return acc;
}, {});

Comment on lines 25 to 40
console.log('Checking permissions: ', permissions);
for (const permission of permissions) {
if (permission.method === 'permissions' && permission.args && !(await visibilityFunctions.hasPermissions(permission.args))) {
return false;
}

if (permission.method === 'loosePermissions' && permission.args && !(await visibilityFunctions.loosePermissions(permission.args))) {
return false;
}

if (permission.method === 'isOrgAdmin' && !(await visibilityFunctions.isOrgAdmin())) {
return false;
}
}

return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

This for loop only allows 3 methods. We want all of those available in chrome. This PR exports the type RedHatInsights/frontend-components#1999 Also, it might be better to use every, without it we'd only check for one permission.

const resolved = Promise.all(permissions.map(async ({ method, args }) => (method && await (visibilityFunctions[method] as any))(...(args || [])))).every(Boolean)

@CodyWMitchell CodyWMitchell requested a review from karelhala April 29, 2024 19:48
@CodyWMitchell
Copy link
Contributor Author

@karelhala This is working for isOrgAdmin coming from the back-end. I also tested this with featureFlag by adding the following to the backend (only locally for testing purposes) and it is grabbing the arguments and successfully only adding the widget when the flag is enabled.

const (
	OrgAdmin    WidgetPermissionMethods = "isOrgAdmin"
	FeatureFlag WidgetPermissionMethods = "featureFlag"
)
		models.Edge: models.ModuleFederationMetadata{
			Scope:    "landing",
			Module:   "./EdgeWidget",
			Defaults: models.BaseWidgetDimensions.InitDimensions(models.BaseWidgetDimensions{}, 1, 4, 4, 1),
			Config: models.WidgetConfiguration{
				Icon:  models.EdgeIcon,
				Title: "Edge Management",
				Permissions: []models.WidgetPermission{
					models.WidgetPermission{
						Method: models.FeatureFlag,
						Args: []any{
							"chrome-service.subscriptions-widget.enabled", // used this flag as a test
							true,
						},
					},
				},
			},
		},

@CodyWMitchell CodyWMitchell marked this pull request as ready for review May 1, 2024 13:54
Copy link
Contributor

@Hyperkid123 Hyperkid123 left a comment

Choose a reason for hiding this comment

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

Small nit

const checkPermissions = async (permissions: WidgetPermission[]): Promise<boolean> => {
const permissionResults = await Promise.all(
permissions.map(async (permission) => {
const { method, args } = permission;
Copy link
Contributor

Choose a reason for hiding this comment

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

Might want to wrap the inner map function with try ... catch and return false from the catch branch.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea! Added this in.

Copy link
Contributor

@karelhala karelhala left a comment

Choose a reason for hiding this comment

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

Looking really good! As pointed out by @Hyperkid123 we might want to wrap the inner part of the map function with try ... catch

@Hyperkid123 Hyperkid123 merged commit 15b8f7e into RedHatInsights:master May 3, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants