1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-03-04 00:18:40 +01:00

Permission for variant environments (#2490)

adds permissions for variants per environment
This commit is contained in:
Simon Hornby 2022-11-22 11:54:04 +02:00 committed by GitHub
parent 137d2caaa4
commit 2a4ca96da2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 72 additions and 12 deletions

View File

@ -1,11 +1,13 @@
import { Add, CloudCircle } from '@mui/icons-material'; import { Add, CloudCircle } from '@mui/icons-material';
import { Button, Divider, styled } from '@mui/material'; import { Divider, styled } from '@mui/material';
import { IFeatureEnvironment, IFeatureVariant } from 'interfaces/featureToggle'; import { IFeatureEnvironment, IFeatureVariant } from 'interfaces/featureToggle';
import { EnvironmentVariantsTable } from './EnvironmentVariantsTable/EnvironmentVariantsTable'; import { EnvironmentVariantsTable } from './EnvironmentVariantsTable/EnvironmentVariantsTable';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect'; import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
import { useMemo } from 'react'; import { useMemo } from 'react';
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext'; import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from 'component/providers/AccessProvider/permissions';
const StyledCard = styled('div')(({ theme }) => ({ const StyledCard = styled('div')(({ theme }) => ({
padding: theme.spacing(3), padding: theme.spacing(3),
@ -58,6 +60,7 @@ const StyledGeneralSelect = styled(GeneralSelect)(({ theme }) => ({
})); }));
interface IEnvironmentVariantsCardProps { interface IEnvironmentVariantsCardProps {
projectId: string;
environment: IFeatureEnvironment; environment: IFeatureEnvironment;
searchValue: string; searchValue: string;
onAddVariant: () => void; onAddVariant: () => void;
@ -68,6 +71,7 @@ interface IEnvironmentVariantsCardProps {
} }
export const EnvironmentVariantsCard = ({ export const EnvironmentVariantsCard = ({
projectId,
environment, environment,
searchValue, searchValue,
onAddVariant, onAddVariant,
@ -127,13 +131,16 @@ export const EnvironmentVariantsCard = ({
onEditVariant={onEditVariant} onEditVariant={onEditVariant}
onDeleteVariant={onDeleteVariant} onDeleteVariant={onDeleteVariant}
/> />
<Button <PermissionButton
permission={UPDATE_FEATURE_ENVIRONMENT_VARIANTS}
projectId={projectId}
environmentId={environment.name}
onClick={onAddVariant} onClick={onAddVariant}
variant="text" variant="text"
startIcon={<Add />} startIcon={<Add />}
> >
add variant add variant
</Button> </PermissionButton>
<ConditionallyRender <ConditionallyRender
condition={variants.length > 1} condition={variants.length > 1}
show={ show={

View File

@ -111,6 +111,7 @@ export const EnvironmentVariantsTable = ({
<VariantsActionCell <VariantsActionCell
variant={original} variant={original}
projectId={projectId} projectId={projectId}
environmentId={environment.name}
editVariant={onEditVariant} editVariant={onEditVariant}
deleteVariant={onDeleteVariant} deleteVariant={onDeleteVariant}
/> />

View File

@ -1,11 +1,12 @@
import { Edit, Delete } from '@mui/icons-material'; import { Edit, Delete } from '@mui/icons-material';
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell'; import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
import { UPDATE_FEATURE_VARIANTS } from 'component/providers/AccessProvider/permissions'; import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from 'component/providers/AccessProvider/permissions';
import { IFeatureVariant } from 'interfaces/featureToggle'; import { IFeatureVariant } from 'interfaces/featureToggle';
interface IVarintsActionCellProps { interface IVarintsActionCellProps {
projectId: string; projectId: string;
environmentId: string;
variant: IFeatureVariant; variant: IFeatureVariant;
editVariant: (variant: IFeatureVariant) => void; editVariant: (variant: IFeatureVariant) => void;
deleteVariant: (variant: IFeatureVariant) => void; deleteVariant: (variant: IFeatureVariant) => void;
@ -13,6 +14,7 @@ interface IVarintsActionCellProps {
export const VariantsActionCell = ({ export const VariantsActionCell = ({
projectId, projectId,
environmentId,
variant, variant,
editVariant, editVariant,
deleteVariant, deleteVariant,
@ -22,8 +24,9 @@ export const VariantsActionCell = ({
<PermissionIconButton <PermissionIconButton
size="large" size="large"
data-testid={`VARIANT_EDIT_BUTTON_${variant.name}`} data-testid={`VARIANT_EDIT_BUTTON_${variant.name}`}
permission={UPDATE_FEATURE_VARIANTS} permission={UPDATE_FEATURE_ENVIRONMENT_VARIANTS}
projectId={projectId} projectId={projectId}
environmentId={environmentId}
onClick={() => editVariant(variant)} onClick={() => editVariant(variant)}
tooltipProps={{ tooltipProps={{
title: 'Edit variant', title: 'Edit variant',
@ -33,9 +36,10 @@ export const VariantsActionCell = ({
</PermissionIconButton> </PermissionIconButton>
<PermissionIconButton <PermissionIconButton
size="large" size="large"
permission={UPDATE_FEATURE_VARIANTS} permission={UPDATE_FEATURE_ENVIRONMENT_VARIANTS}
data-testid={`VARIANT_DELETE_BUTTON_${variant.name}`} data-testid={`VARIANT_DELETE_BUTTON_${variant.name}`}
projectId={projectId} projectId={projectId}
environmentId={environmentId}
onClick={() => deleteVariant(variant)} onClick={() => deleteVariant(variant)}
tooltipProps={{ tooltipProps={{
title: 'Delete variant', title: 'Delete variant',

View File

@ -14,6 +14,7 @@ interface IEnvironmentVariantsCopyFromProps {
environment: IFeatureEnvironment; environment: IFeatureEnvironment;
permission: string; permission: string;
projectId: string; projectId: string;
environmentId: string;
onCopyVariantsFrom: ( onCopyVariantsFrom: (
fromEnvironment: IFeatureEnvironment, fromEnvironment: IFeatureEnvironment,
toEnvironment: IFeatureEnvironment toEnvironment: IFeatureEnvironment
@ -25,6 +26,7 @@ export const EnvironmentVariantsCopyFrom = ({
environment, environment,
permission, permission,
projectId, projectId,
environmentId,
onCopyVariantsFrom, onCopyVariantsFrom,
otherEnvsWithVariants, otherEnvsWithVariants,
}: IEnvironmentVariantsCopyFromProps) => { }: IEnvironmentVariantsCopyFromProps) => {
@ -48,6 +50,7 @@ export const EnvironmentVariantsCopyFrom = ({
variant="outlined" variant="outlined"
permission={permission} permission={permission}
projectId={projectId} projectId={projectId}
environmentId={environmentId}
> >
Copy variants from Copy variants from
</PermissionButton> </PermissionButton>

View File

@ -7,7 +7,7 @@ import { PageHeader } from 'component/common/PageHeader/PageHeader';
import PermissionButton from 'component/common/PermissionButton/PermissionButton'; import PermissionButton from 'component/common/PermissionButton/PermissionButton';
import { Search } from 'component/common/Search/Search'; import { Search } from 'component/common/Search/Search';
import { updateWeight } from 'component/common/util'; import { updateWeight } from 'component/common/util';
import { UPDATE_FEATURE_VARIANTS } from 'component/providers/AccessProvider/permissions'; import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from 'component/providers/AccessProvider/permissions';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { IFeatureEnvironment, IFeatureVariant } from 'interfaces/featureToggle'; import { IFeatureEnvironment, IFeatureVariant } from 'interfaces/featureToggle';
@ -232,6 +232,7 @@ export const FeatureEnvironmentVariants = () => {
return ( return (
<EnvironmentVariantsCard <EnvironmentVariantsCard
key={environment.name} key={environment.name}
projectId={projectId}
environment={environment} environment={environment}
searchValue={searchValue} searchValue={searchValue}
onAddVariant={() => addVariant(environment)} onAddVariant={() => addVariant(environment)}
@ -252,15 +253,21 @@ export const FeatureEnvironmentVariants = () => {
<PermissionButton <PermissionButton
onClick={() => addVariant(environment)} onClick={() => addVariant(environment)}
variant="outlined" variant="outlined"
permission={UPDATE_FEATURE_VARIANTS} permission={
UPDATE_FEATURE_ENVIRONMENT_VARIANTS
}
projectId={projectId} projectId={projectId}
environmentId={environment.name}
> >
Add variant Add variant
</PermissionButton> </PermissionButton>
<EnvironmentVariantsCopyFrom <EnvironmentVariantsCopyFrom
environment={environment} environment={environment}
permission={UPDATE_FEATURE_VARIANTS} permission={
UPDATE_FEATURE_ENVIRONMENT_VARIANTS
}
projectId={projectId} projectId={projectId}
environmentId={environment.name}
onCopyVariantsFrom={onCopyVariantsFrom} onCopyVariantsFrom={onCopyVariantsFrom}
otherEnvsWithVariants={ otherEnvsWithVariants={
otherEnvsWithVariants otherEnvsWithVariants

View File

@ -27,6 +27,8 @@ export const UPDATE_FEATURE_STRATEGY = 'UPDATE_FEATURE_STRATEGY';
export const DELETE_FEATURE_STRATEGY = 'DELETE_FEATURE_STRATEGY'; export const DELETE_FEATURE_STRATEGY = 'DELETE_FEATURE_STRATEGY';
export const UPDATE_FEATURE_ENVIRONMENT = 'UPDATE_FEATURE_ENVIRONMENT'; export const UPDATE_FEATURE_ENVIRONMENT = 'UPDATE_FEATURE_ENVIRONMENT';
export const UPDATE_FEATURE_VARIANTS = 'UPDATE_FEATURE_VARIANTS'; export const UPDATE_FEATURE_VARIANTS = 'UPDATE_FEATURE_VARIANTS';
export const UPDATE_FEATURE_ENVIRONMENT_VARIANTS =
'UPDATE_FEATURE_ENVIRONMENT_VARIANTS';
export const MOVE_FEATURE_TOGGLE = 'MOVE_FEATURE_TOGGLE'; export const MOVE_FEATURE_TOGGLE = 'MOVE_FEATURE_TOGGLE';
export const CREATE_SEGMENT = 'CREATE_SEGMENT'; export const CREATE_SEGMENT = 'CREATE_SEGMENT';
export const UPDATE_SEGMENT = 'UPDATE_SEGMENT'; export const UPDATE_SEGMENT = 'UPDATE_SEGMENT';

View File

@ -5,7 +5,11 @@ import { IUnleashConfig } from '../../../types/option';
import { IUnleashServices } from '../../../types'; import { IUnleashServices } from '../../../types';
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { Operation } from 'fast-json-patch'; import { Operation } from 'fast-json-patch';
import { NONE, UPDATE_FEATURE_VARIANTS } from '../../../types/permissions'; import {
NONE,
UPDATE_FEATURE_ENVIRONMENT_VARIANTS,
UPDATE_FEATURE_VARIANTS,
} from '../../../types/permissions';
import { IVariant } from '../../../types/model'; import { IVariant } from '../../../types/model';
import { extractUsername } from '../../../util/extract-user'; import { extractUsername } from '../../../util/extract-user';
import { IAuthRequest } from '../../unleash-types'; import { IAuthRequest } from '../../unleash-types';
@ -108,7 +112,7 @@ export default class VariantsController extends Controller {
this.route({ this.route({
method: 'patch', method: 'patch',
path: ENV_PREFIX, path: ENV_PREFIX,
permission: UPDATE_FEATURE_VARIANTS, permission: UPDATE_FEATURE_ENVIRONMENT_VARIANTS,
handler: this.patchVariantsOnEnv, handler: this.patchVariantsOnEnv,
middleware: [ middleware: [
openApiService.validPath({ openApiService.validPath({
@ -124,7 +128,7 @@ export default class VariantsController extends Controller {
this.route({ this.route({
method: 'put', method: 'put',
path: ENV_PREFIX, path: ENV_PREFIX,
permission: UPDATE_FEATURE_VARIANTS, permission: UPDATE_FEATURE_ENVIRONMENT_VARIANTS,
handler: this.overwriteVariantsOnEnv, handler: this.overwriteVariantsOnEnv,
middleware: [ middleware: [
openApiService.validPath({ openApiService.validPath({

View File

@ -33,6 +33,8 @@ export const READ_API_TOKEN = 'READ_API_TOKEN';
export const UPDATE_TAG_TYPE = 'UPDATE_TAG_TYPE'; export const UPDATE_TAG_TYPE = 'UPDATE_TAG_TYPE';
export const DELETE_TAG_TYPE = 'DELETE_TAG_TYPE'; export const DELETE_TAG_TYPE = 'DELETE_TAG_TYPE';
export const UPDATE_FEATURE_VARIANTS = 'UPDATE_FEATURE_VARIANTS'; export const UPDATE_FEATURE_VARIANTS = 'UPDATE_FEATURE_VARIANTS';
export const UPDATE_FEATURE_ENVIRONMENT_VARIANTS =
'UPDATE_FEATURE_ENVIRONMENT_VARIANTS';
export const MOVE_FEATURE_TOGGLE = 'MOVE_FEATURE_TOGGLE'; export const MOVE_FEATURE_TOGGLE = 'MOVE_FEATURE_TOGGLE';
export const CREATE_SEGMENT = 'CREATE_SEGMENT'; export const CREATE_SEGMENT = 'CREATE_SEGMENT';
export const UPDATE_SEGMENT = 'UPDATE_SEGMENT'; export const UPDATE_SEGMENT = 'UPDATE_SEGMENT';

View File

@ -0,0 +1,30 @@
'use strict';
exports.up = function (db, callback) {
db.runSql(
`
INSERT INTO permissions (permission, display_name, type)
values('UPDATE_FEATURE_ENVIRONMENT_VARIANTS', 'Update variants on environment', 'environment');
INSERT INTO role_permission (role_id, permission_id, environment)
(WITH perm_id as (SELECT id from permissions WHERE permission = 'UPDATE_FEATURE_ENVIRONMENT_VARIANTS')
SELECT rp.role_id, perm_id.id, rp.environment FROM perm_id, role_permission as rp
JOIN permissions p ON p.id = rp.permission_id
WHERE p.permission = 'UPDATE_FEATURE_ENVIRONMENT'
);
`,
callback,
);
};
exports.down = function (db, callback) {
db.runSql(
`
DELETE FROM role_permission WHERE permission_id =
(SELECT id FROM permissions WHERE permission = 'UPDATE_FEATURE_ENVIRONMENT_VARIANTS');
DELETE FROM permissions WHERE permission = 'UPDATE_FEATURE_ENVIRONMENT_VARIANTS';
`,
callback,
);
};