Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit2e05329

Browse files
authored
feat: add custom roles (#14069)
* feat: initial commit custom roles* feat: add page to create and edit custom roles* feat: add assign org role permission* feat: wip* feat: cleanup* fix: role name is disabled when editing the role* fix: assign role context menu falls back to name when no display_name* feat: add helper text to let users know that role name is immutable* fix: format* feat: - hide custom roles tab if experiment is not enabled* fix: use custom TableLoader* fix: fix custom roles text* fix: use PatchRoleRequest* fix: use addIcon to create roles* feat: add cancel and save buttons to top of page* fix: use nameValidator for name* chore: cleanup* feat: add show all permissions checkbox* fix: update sidebar for roles* fix: fix format* fix: custom roles is not needed outside orgs* fix: fix sidebar stories* feat: add custom roles page stories* fix: use organization permissions* feat: add stories for CreateEditRolePageView* fix: design improvements for the create edit role form* feat: add show all resources checkbox to bottom of table* feat: improve spacing
1 parent238e995 commit2e05329

14 files changed

+921
-1
lines changed

‎site/src/api/api.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,24 @@ class ApiMethods {
600600
returnresponse.data;
601601
};
602602

603+
patchOrganizationRole=async(
604+
organizationId:string,
605+
role:TypesGen.Role,
606+
):Promise<TypesGen.Role>=>{
607+
constresponse=awaitthis.axios.patch<TypesGen.Role>(
608+
`/api/v2/organizations/${organizationId}/members/roles`,
609+
role,
610+
);
611+
612+
returnresponse.data;
613+
};
614+
615+
deleteOrganizationRole=async(organizationId:string,roleName:string)=>{
616+
awaitthis.axios.delete(
617+
`/api/v2/organizations/${organizationId}/members/roles/${roleName}`,
618+
);
619+
};
620+
603621
/**
604622
*@param organization Can be the organization's ID or name
605623
*/

‎site/src/api/queries/organizations.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,13 @@ export const organizationPermissions = (organizationId: string | undefined) => {
177177
},
178178
action:"read",
179179
},
180+
assignOrgRole:{
181+
object:{
182+
resource_type:"assign_org_role",
183+
organization_id:organizationId,
184+
},
185+
action:"create",
186+
},
180187
},
181188
}),
182189
};

‎site/src/api/queries/roles.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1+
importtype{QueryClient}from"react-query";
12
import{API}from"api/api";
3+
importtype{Role}from"api/typesGenerated";
4+
5+
constgetRoleQueryKey=(organizationId:string,roleName:string)=>[
6+
"organization",
7+
organizationId,
8+
"role",
9+
roleName,
10+
];
211

312
exportconstroles=()=>{
413
return{
@@ -13,3 +22,30 @@ export const organizationRoles = (organizationId: string) => {
1322
queryFn:()=>API.getOrganizationRoles(organizationId),
1423
};
1524
};
25+
26+
exportconstpatchOrganizationRole=(
27+
queryClient:QueryClient,
28+
organizationId:string,
29+
)=>{
30+
return{
31+
mutationFn:(request:Role)=>
32+
API.patchOrganizationRole(organizationId,request),
33+
onSuccess:async(updatedRole:Role)=>
34+
awaitqueryClient.invalidateQueries(
35+
getRoleQueryKey(organizationId,updatedRole.name),
36+
),
37+
};
38+
};
39+
40+
exportconstdeleteRole=(
41+
queryClient:QueryClient,
42+
organizationId:string,
43+
)=>{
44+
return{
45+
mutationFn:API.deleteOrganizationRole,
46+
onSuccess:async(_:void,roleName:string)=>
47+
awaitqueryClient.invalidateQueries(
48+
getRoleQueryKey(organizationId,roleName),
49+
),
50+
};
51+
};
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
importtype{FC}from"react";
2+
import{Helmet}from"react-helmet-async";
3+
import{useMutation,useQuery,useQueryClient}from"react-query";
4+
import{useNavigate,useParams}from"react-router-dom";
5+
import{getErrorMessage}from"api/errors";
6+
import{organizationPermissions}from"api/queries/organizations";
7+
import{patchOrganizationRole,organizationRoles}from"api/queries/roles";
8+
importtype{PatchRoleRequest}from"api/typesGenerated";
9+
import{displayError}from"components/GlobalSnackbar/utils";
10+
import{Loader}from"components/Loader/Loader";
11+
import{pageTitle}from"utils/page";
12+
import{useOrganizationSettings}from"../ManagementSettingsLayout";
13+
importCreateEditRolePageViewfrom"./CreateEditRolePageView";
14+
15+
exportconstCreateEditRolePage:FC=()=>{
16+
constqueryClient=useQueryClient();
17+
constnavigate=useNavigate();
18+
const{organization:organizationName, roleName}=useParams()as{
19+
organization:string;
20+
roleName:string;
21+
};
22+
const{ organizations}=useOrganizationSettings();
23+
constorganization=organizations?.find((o)=>o.name===organizationName);
24+
constpermissionsQuery=useQuery(organizationPermissions(organization?.id));
25+
constpatchOrganizationRoleMutation=useMutation(
26+
patchOrganizationRole(queryClient,organizationName),
27+
);
28+
const{data:roleData, isLoading}=useQuery(
29+
organizationRoles(organizationName),
30+
);
31+
constrole=roleData?.find((role)=>role.name===roleName);
32+
constpermissions=permissionsQuery.data;
33+
34+
if(isLoading||!permissions){
35+
return<Loader/>;
36+
}
37+
38+
return(
39+
<>
40+
<Helmet>
41+
<title>
42+
{pageTitle(
43+
role!==undefined ?"Edit Custom Role" :"Create Custom Role",
44+
)}
45+
</title>
46+
</Helmet>
47+
48+
<CreateEditRolePageView
49+
role={role}
50+
onSubmit={async(data:PatchRoleRequest)=>{
51+
try{
52+
awaitpatchOrganizationRoleMutation.mutateAsync(data);
53+
navigate(`/organizations/${organizationName}/roles`);
54+
}catch(error){
55+
displayError(
56+
getErrorMessage(error,"Failed to update custom role"),
57+
);
58+
}
59+
}}
60+
error={patchOrganizationRoleMutation.error}
61+
isLoading={patchOrganizationRoleMutation.isLoading}
62+
organizationName={organizationName}
63+
canAssignOrgRole={permissions.assignOrgRole}
64+
/>
65+
</>
66+
);
67+
};
68+
69+
exportdefaultCreateEditRolePage;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
importtype{Meta,StoryObj}from"@storybook/react";
2+
import{
3+
mockApiError,
4+
MockRoleWithOrgPermissions,
5+
assignableRole,
6+
}from"testHelpers/entities";
7+
import{CreateEditRolePageView}from"./CreateEditRolePageView";
8+
9+
constmeta:Meta<typeofCreateEditRolePageView>={
10+
title:"pages/OrganizationCreateEditRolePage",
11+
component:CreateEditRolePageView,
12+
};
13+
14+
exportdefaultmeta;
15+
typeStory=StoryObj<typeofCreateEditRolePageView>;
16+
17+
exportconstDefault:Story={
18+
args:{
19+
role:assignableRole(MockRoleWithOrgPermissions,true),
20+
onSubmit:()=>null,
21+
error:undefined,
22+
isLoading:false,
23+
organizationName:"my-org",
24+
canAssignOrgRole:true,
25+
},
26+
};
27+
28+
exportconstWithError:Story={
29+
args:{
30+
role:assignableRole(MockRoleWithOrgPermissions,true),
31+
onSubmit:()=>null,
32+
error:mockApiError({
33+
message:"A role named new-role already exists.",
34+
validations:[{field:"name",detail:"Role names must be unique"}],
35+
}),
36+
isLoading:false,
37+
organizationName:"my-org",
38+
canAssignOrgRole:true,
39+
},
40+
};
41+
42+
exportconstCannotEdit:Story={
43+
args:{
44+
role:assignableRole(MockRoleWithOrgPermissions,true),
45+
onSubmit:()=>null,
46+
error:undefined,
47+
isLoading:false,
48+
organizationName:"my-org",
49+
canAssignOrgRole:false,
50+
},
51+
};
52+
53+
exportconstShowAllResources:Story={
54+
args:{
55+
role:assignableRole(MockRoleWithOrgPermissions,true),
56+
onSubmit:()=>null,
57+
error:undefined,
58+
isLoading:false,
59+
organizationName:"my-org",
60+
canAssignOrgRole:true,
61+
allResources:true,
62+
},
63+
};

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp