Skip to content
This repository has been archived by the owner on May 31, 2023. It is now read-only.

Commit

Permalink
Add Role management (#72)
Browse files Browse the repository at this point in the history
* Add Role management

* Misc fixes
  • Loading branch information
raplemie authored Sep 7, 2021
1 parent e2743cc commit 81451fa
Show file tree
Hide file tree
Showing 27 changed files with 2,526 additions and 26 deletions.
1 change: 1 addition & 0 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"@types/reach__router": "^1.3.7",
"@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0",
"msw": "~0.32.0",
"typescript": "^3.7.2"
},
"scripts": {
Expand Down
33 changes: 29 additions & 4 deletions packages/app/src/api/projects/generated/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ export interface RoleProjectsAPI {
* @type {string}
* @memberof RoleProjectsAPI
*/
name?: string;
displayName?: string;
/**
* The description for the role.
* @type {string}
Expand All @@ -496,6 +496,19 @@ export interface RoleProjectsAPI {
*/
permissions?: Array<string>;
}
/**
*
* @export
* @interface RoleResponseProjectsAPI
*/
export interface RoleResponseProjectsAPI {
/**
*
* @type {RoleProjectsAPI}
* @memberof RoleResponseProjectsAPI
*/
role?: RoleProjectsAPI;
}
/**
*
* @export
Expand Down Expand Up @@ -532,7 +545,13 @@ export interface RolesProjectsAPI {
* @type {Array<RoleProjectsAPI>}
* @memberof RolesProjectsAPI
*/
value?: Array<RoleProjectsAPI>;
roles?: Array<RoleProjectsAPI>;
/**
*
* @type {LinksProjectsAPI}
* @memberof RolesProjectsAPI
*/
_links?: LinksProjectsAPI;
}
/**
*
Expand Down Expand Up @@ -2018,7 +2037,10 @@ export const ProjectRolesApiFp = function(configuration?: Configuration) {
body?: RoleCreateProjectsAPI,
Accept?: string,
options?: any
): (fetch?: FetchAPI, basePath?: string) => Promise<RoleProjectsAPI> {
): (
fetch?: FetchAPI,
basePath?: string
) => Promise<RoleResponseProjectsAPI> {
const localVarFetchArgs = ProjectRolesApiFetchParamCreator(
configuration
).createProjectRole(id, Authorization, body, Accept, options);
Expand Down Expand Up @@ -2126,7 +2148,10 @@ export const ProjectRolesApiFp = function(configuration?: Configuration) {
body?: RoleUpdateProjectsAPI,
Accept?: string,
options?: any
): (fetch?: FetchAPI, basePath?: string) => Promise<RoleProjectsAPI> {
): (
fetch?: FetchAPI,
basePath?: string
) => Promise<RoleResponseProjectsAPI> {
const localVarFetchArgs = ProjectRolesApiFetchParamCreator(
configuration
).updateProjectRole(id, roleId, Authorization, body, Accept, options);
Expand Down
19 changes: 15 additions & 4 deletions packages/app/src/api/projects/openapi/projectsAdjusted.json
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Role"
"$ref": "#/components/schemas/role-response"
},
"example": {
"role": {
Expand Down Expand Up @@ -1948,7 +1948,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Role"
"$ref": "#/components/schemas/role-response"
},
"example": {
"role": {
Expand Down Expand Up @@ -2849,14 +2849,22 @@
}
}
},
"role-response": {
"type": "object",
"properties": {
"role": {
"$ref": "#/components/schemas/Role"
}
}
},
"Role": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The role id."
},
"name": {
"displayName": {
"type": "string",
"description": "The display name for the role."
},
Expand All @@ -2876,12 +2884,15 @@
"Roles": {
"type": "object",
"properties": {
"value": {
"roles": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Role"
},
"description": "List of roles."
},
"_links": {
"$ref": "#/components/schemas/Links"
}
}
},
Expand Down
83 changes: 83 additions & 0 deletions packages/app/src/api/projects/projectsClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
ProjectRolesApi,
ProjectsApi,
ProjectUsersApi,
RoleCreateProjectsAPI,
RoleUpdateProjectsAPI,
TeamMemberAddByNamesProjectsAPI,
} from "./generated";

Expand All @@ -31,6 +33,66 @@ export class ProjectsClient {
this.projectsApi = new ProjectsApi(undefined, baseUrl);
}

/**
* See {@link ProjectRolesApi.createProjectRole} for details.
* @param projectId
* @param role
* @returns
*/
async createProjectRole(projectId: string, role: RoleCreateProjectsAPI) {
return this.rolesApi.createProjectRole(
projectId,
this.accessToken,
role,
ACCEPT
);
}

/**
* See {@link ProjectRolesApi.updateProjectRole} for details.
* @param projectId
* @param roleId
* @param role
* @returns
*/
async updateProjectRole(
projectId: string,
roleId: string,
role: RoleUpdateProjectsAPI
) {
return this.rolesApi.updateProjectRole(
projectId,
roleId,
this.accessToken,
role,
ACCEPT
);
}

/**
* See {@link ProjectRolesApi.getProjectRoles} for details.
* @param projectId
* @returns
*/
async getProjectRoles(projectId: string) {
return this.rolesApi.getProjectRoles(projectId, this.accessToken, ACCEPT);
}

/**
* See {@link ProjectRolesApi.deleteProjectRole} for details.
* @param projectId
* @param roleId
* @returns
*/
async deleteProjectRole(projectId: string, roleId: string) {
return this.rolesApi.deleteProjectRole(
projectId,
roleId,
this.accessToken,
ACCEPT
);
}

/**
* See {@link ProjectUsersApi.getProjectTeamMembers} for details.
* @param projectId
Expand Down Expand Up @@ -81,6 +143,27 @@ export class ProjectsClient {
);
}

/**
* See {@link ProjectUsersApi.updateProjectTeamMemberRoles} for details
* @param projectId
* @param memberId
* @param roleIds
* @returns
*/
async updateProjectMemberRoles(
projectId: string,
memberId: string,
roleIds: string[]
) {
return this.usersApi.updateProjectTeamMemberRoles(
projectId,
memberId,
this.accessToken,
{ roleIds },
ACCEPT
);
}

/**
*
* @param errorResponse
Expand Down
58 changes: 52 additions & 6 deletions packages/app/src/routers/MembersRouter/Members.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
*
* This code is for demonstration purposes and should not be considered production ready.
*--------------------------------------------------------------------------------------------*/
import { Table, Title } from "@itwin/itwinui-react";
import { SvgEdit } from "@itwin/itwinui-icons-react";
import { IconButton, Table, Title } from "@itwin/itwinui-react";
import { RouteComponentProps } from "@reach/router";
import React from "react";

import { TeamMemberProjectsAPI } from "../../api/projects/generated";
import {
RoleProjectsAPI,
TeamMemberProjectsAPI,
} from "../../api/projects/generated";
import { ProjectsClient } from "../../api/projects/projectsClient";
import { useApiPrefix } from "../../api/useApiPrefix";
import { CreateTypeFromInterface } from "../../utils";
Expand All @@ -17,6 +21,7 @@ import {
skeletonRows,
} from "../SynchronizationRouter/components/SkeletonCell";
import { AddMemberInput } from "./components/AddMemberInput";
import { EditMemberRoleCell } from "./components/EditMemberRoleCell";
import { RemoveMemberCell } from "./components/RemoveMemberCell";
import "./Members.scss";

Expand All @@ -25,7 +30,7 @@ interface MembersProps extends RouteComponentProps {
projectId?: string;
}
type TeamMember = CreateTypeFromInterface<TeamMemberProjectsAPI>;
export const Members = ({ accessToken, projectId }: MembersProps) => {
export const Members = ({ accessToken, projectId, navigate }: MembersProps) => {
const urlPrefix = useApiPrefix();

const [users, setUsers] = React.useState<TeamMember[]>(skeletonRows);
Expand All @@ -39,11 +44,28 @@ export const Members = ({ accessToken, projectId }: MembersProps) => {
const { members } = await client.getProjectUsers(projectId);
setUsers(members ?? []);
} catch (error) {
setError(await client.extractAPIErrorMessage(error));
const errorResponse = error as Response;
setError(await client.extractAPIErrorMessage(errorResponse));
}
}, [accessToken, projectId, urlPrefix]);
React.useEffect(() => void fetchUsers(), [fetchUsers]);

const [roles, setRoles] = React.useState<RoleProjectsAPI[]>(skeletonRows);
const fetchRoles = React.useCallback(async () => {
if (!projectId) {
return;
}
const client = new ProjectsClient(urlPrefix, accessToken);
try {
const { roles } = await client.getProjectRoles(projectId);
setRoles(roles ?? []);
} catch (error) {
const errorResponse = error as Response;
setError(await client.extractAPIErrorMessage(errorResponse));
}
}, [accessToken, projectId, urlPrefix]);
React.useEffect(() => void fetchRoles(), [fetchRoles]);

return (
<div className="idp-scrolling-container members-management">
<div className="idp-content-margins">
Expand Down Expand Up @@ -82,7 +104,31 @@ export const Members = ({ accessToken, projectId }: MembersProps) => {
Cell: SkeletonCell,
Header: "Organization",
},
{ accessor: "roles", Cell: SkeletonCell, Header: "Roles" },
{
accessor: "roles",
Header: (
<div>
Roles{" "}
<IconButton
title={"Manage roles"}
styleType={"borderless"}
onClick={() => navigate?.("roles")}
>
<SvgEdit />
</IconButton>
</div>
),
Cell: (props) => (
<EditMemberRoleCell
{...props}
projectId={projectId ?? ""}
accessToken={accessToken}
onDataUpdated={fetchUsers}
roles={roles}
onError={setError}
/>
),
},
{
id: "actions",
accessor: "userId",
Expand All @@ -102,7 +148,7 @@ export const Members = ({ accessToken, projectId }: MembersProps) => {
],
},
],
[accessToken, fetchUsers, projectId]
[accessToken, fetchUsers, navigate, projectId, roles]
)}
emptyTableContent={
error ||
Expand Down
5 changes: 5 additions & 0 deletions packages/app/src/routers/MembersRouter/MembersRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React from "react";

import { SelectionRouter } from "../SelectionRouter/SelectionRouter";
import { Members } from "./Members";
import { RolesRouter } from "./RolesRouter";

interface MembersRouterProps extends RouteComponentProps {
accessToken: string;
Expand All @@ -19,6 +20,10 @@ export const MembersRouter = ({ accessToken }: MembersRouterProps) => {
<Router className="full-height-container">
<SelectionRouter accessToken={accessToken} path="*" />
<Members accessToken={accessToken} path="/project/:projectId/*" />
<RolesRouter
accessToken={accessToken}
path="/project/:projectId/roles/*"
/>
</Router>
);
};
33 changes: 33 additions & 0 deletions packages/app/src/routers/MembersRouter/RoleBase.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*
* This code is for demonstration purposes and should not be considered production ready.
*--------------------------------------------------------------------------------------------*/
.idp-scrolling-role-base {
display: flex;
flex-direction: column;
overflow: hidden;
max-height: 100%;

> div {
display: flex;
flex-direction: column;
overflow: hidden;

> .idp-role-base {
overflow: hidden;
padding: 0;

> div:first-child {
display: flex;
flex-direction: column;
overflow: hidden;

> div:nth-child(2) {
overflow: scroll;
}
}
}
}
}
Loading

0 comments on commit 81451fa

Please sign in to comment.