-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add permissions page with ability to manage permissions
Fixes #2246 chore: enable hiding resource column fix: make minor adjustments to resource display
- Loading branch information
1 parent
ba4fa25
commit 826bff2
Showing
14 changed files
with
850 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { AVATAR_INFO } from "@flanksource-ui/constants"; | ||
import { IncidentCommander } from "../axios"; | ||
import { resolvePostGrestRequestWithPagination } from "../resolve"; | ||
import { PermissionAPIResponse, PermissionTable } from "../types/permissions"; | ||
|
||
export type FetchPermissionsInput = { | ||
componentId?: string; | ||
personId?: string; | ||
teamId?: string; | ||
configId?: string; | ||
checkId?: string; | ||
canaryId?: string; | ||
playbookId?: string; | ||
connectionId?: string; | ||
}; | ||
|
||
function composeQueryParamForFetchPermissions({ | ||
componentId, | ||
personId, | ||
teamId, | ||
configId, | ||
checkId, | ||
canaryId, | ||
playbookId, | ||
connectionId | ||
}: FetchPermissionsInput) { | ||
if (componentId) { | ||
return `component_id=eq.${componentId}`; | ||
} | ||
if (personId) { | ||
return `person_id=eq.${personId}`; | ||
} | ||
if (teamId) { | ||
return `team_id=eq.${teamId}`; | ||
} | ||
if (configId) { | ||
return `config_id=eq.${configId}`; | ||
} | ||
if (checkId) { | ||
return `check_id=eq.${checkId}`; | ||
} | ||
if (canaryId) { | ||
return `canary_id=eq.${canaryId}`; | ||
} | ||
if (playbookId) { | ||
return `playbook_id=eq.${playbookId}`; | ||
} | ||
if (connectionId) { | ||
return `connection_id=eq.${connectionId}`; | ||
} | ||
return ""; | ||
} | ||
|
||
export function fetchPermissions( | ||
input: FetchPermissionsInput, | ||
pagination: { | ||
pageSize: number; | ||
pageIndex: number; | ||
} | ||
) { | ||
const queryParam = composeQueryParamForFetchPermissions(input); | ||
const selectFields = [ | ||
"*", | ||
// "checks:check_id(id, name, status, type)", | ||
"catalog:config_id(id, name, type, config_class)", | ||
"component:component_id(id, name, icon)", | ||
"canary:canary_id(id, name)", | ||
"playbook:playbook_id(id, title, name, icon)", | ||
"team:team_id(id, name, icon)", | ||
`person:person_id(${AVATAR_INFO})`, | ||
`createdBy:created_by(${AVATAR_INFO})`, | ||
`connection:connection_id(id,name,type)` | ||
]; | ||
|
||
const { pageSize, pageIndex } = pagination; | ||
|
||
const url = `/permissions?${queryParam}&select=${selectFields.join(",")}&limit=${pageSize}&offset=${pageIndex * pageSize}`; | ||
return resolvePostGrestRequestWithPagination( | ||
IncidentCommander.get<PermissionAPIResponse[]>(url) | ||
); | ||
} | ||
|
||
export function addPermission(permission: PermissionTable) { | ||
return IncidentCommander.post<PermissionTable>("/permissions", permission); | ||
} | ||
|
||
export function updatePermission(permission: PermissionTable) { | ||
return IncidentCommander.patch<PermissionTable>( | ||
`/permissions?id=eq.${permission.id}`, | ||
permission | ||
); | ||
} | ||
|
||
export function deletePermission(id: string) { | ||
return IncidentCommander.patch(`/permissions?id=eq.${id}`, { | ||
deleted_at: "now()" | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { Connection } from "@flanksource-ui/components/Connections/ConnectionFormModal"; | ||
import { ConfigItem } from "./configs"; | ||
import { PlaybookSpec } from "./playbooks"; | ||
import { Topology } from "./topology"; | ||
import { Team, User } from "./users"; | ||
|
||
export type PermissionTable = { | ||
id: string; | ||
description: string; | ||
action: string; | ||
deny?: boolean; | ||
component_id?: string; | ||
config_id?: string; | ||
canary_id?: string; | ||
playbook_id?: string; | ||
created_by: string; | ||
connection_id?: string; | ||
person_id?: string; | ||
team_id?: string; | ||
updated_by: string; | ||
created_at: string; | ||
updated_at: string; | ||
until?: string; | ||
source?: string; | ||
}; | ||
|
||
export type PermissionAPIResponse = PermissionTable & { | ||
// checks: Pick<HealthCheck, "id" | "name" | "type" | "status">; | ||
catalog: Pick<ConfigItem, "id" | "name" | "type" | "config_class">; | ||
component: Pick<Topology, "id" | "name" | "icon">; | ||
canary: { | ||
id: string; | ||
name: string; | ||
}; | ||
playbook: Pick<PlaybookSpec, "id" | "name" | "icon" | "title">; | ||
team: Pick<Team, "id" | "name" | "icon">; | ||
connection: Pick<Connection, "id" | "name" | "type">; | ||
person: User; | ||
createdBy: User; | ||
}; |
16 changes: 16 additions & 0 deletions
16
src/components/Permissions/ManagePermissions/Forms/AddPermissionButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { useState } from "react"; | ||
import { AiFillPlusCircle } from "react-icons/ai"; | ||
import PermissionForm from "./PermissionForm"; | ||
|
||
export default function AddPermissionButton() { | ||
const [isOpen, setIsOpen] = useState(false); | ||
|
||
return ( | ||
<> | ||
<button type="button" className="" onClick={() => setIsOpen(true)}> | ||
<AiFillPlusCircle size={32} className="text-blue-600" /> | ||
</button> | ||
<PermissionForm isOpen={isOpen} onClose={() => setIsOpen(false)} /> | ||
</> | ||
); | ||
} |
65 changes: 65 additions & 0 deletions
65
src/components/Permissions/ManagePermissions/Forms/DeletePermission.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { deletePermission } from "@flanksource-ui/api/services/permissions"; | ||
import { | ||
toastError, | ||
toastSuccess | ||
} from "@flanksource-ui/components/Toast/toast"; | ||
import { ConfirmationPromptDialog } from "@flanksource-ui/ui/AlertDialog/ConfirmationPromptDialog"; | ||
import { Button } from "@flanksource-ui/ui/Buttons/Button"; | ||
import { useMutation } from "@tanstack/react-query"; | ||
import { AxiosError } from "axios"; | ||
import { useCallback, useState } from "react"; | ||
import { FaCircleNotch, FaTrash } from "react-icons/fa"; | ||
|
||
export default function DeletePermission({ | ||
permissionId, | ||
onDeleted = () => {} | ||
}: { | ||
permissionId: string; | ||
onDeleted: () => void; | ||
}) { | ||
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false); | ||
|
||
const { mutate: deleteResource, isLoading } = useMutation({ | ||
mutationFn: async (id: string) => { | ||
const res = await deletePermission(id); | ||
return res.data; | ||
}, | ||
onSuccess: (_) => { | ||
toastSuccess("Permission deleted"); | ||
onDeleted(); | ||
}, | ||
onError: (error: AxiosError) => { | ||
toastError(error.message); | ||
} | ||
}); | ||
|
||
const onDeleteResource = useCallback(() => { | ||
setIsConfirmDialogOpen(false); | ||
deleteResource(permissionId); | ||
}, [deleteResource, permissionId]); | ||
|
||
return ( | ||
<> | ||
<Button | ||
text="Delete" | ||
disabled={isLoading} | ||
icon={ | ||
!isLoading ? <FaTrash /> : <FaCircleNotch className="animate-spin" /> | ||
} | ||
className="btn-danger" | ||
onClick={() => setIsConfirmDialogOpen(true)} | ||
/> | ||
|
||
{isConfirmDialogOpen && ( | ||
<ConfirmationPromptDialog | ||
title="Delete Permission" | ||
description="Are you sure you want to permission?" | ||
onConfirm={onDeleteResource} | ||
isOpen={isConfirmDialogOpen} | ||
onClose={() => setIsConfirmDialogOpen(false)} | ||
className="z-[9999]" | ||
/> | ||
)} | ||
</> | ||
); | ||
} |
68 changes: 68 additions & 0 deletions
68
src/components/Permissions/ManagePermissions/Forms/FormikPermissionSelectResourceFields.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import FormikCanaryDropdown from "@flanksource-ui/components/Forms/Formik/FormikCanaryDropdown"; | ||
import FormikConnectionField from "@flanksource-ui/components/Forms/Formik/FormikConnectionField"; | ||
import FormikPlaybooksDropdown from "@flanksource-ui/components/Forms/Formik/FormikPlaybooksDropdown"; | ||
import FormikResourceSelectorDropdown from "@flanksource-ui/components/Forms/Formik/FormikResourceSelectorDropdown"; | ||
import { Switch } from "@flanksource-ui/ui/FormControls/Switch"; | ||
import { useFormikContext } from "formik"; | ||
import { useState } from "react"; | ||
|
||
export default function FormikPermissionSelectResourceFields() { | ||
const { setFieldValue } = useFormikContext<Record<string, any>>(); | ||
|
||
const [switchOption, setSwitchOption] = useState< | ||
"Component" | "Catalog" | "Canary" | "Playbook" | "Connection" | ||
>("Catalog"); | ||
|
||
return ( | ||
<div className="flex flex-col gap-2"> | ||
<label className={`form-label`}>Resource</label> | ||
<div> | ||
<div className="flex w-full flex-row"> | ||
<Switch | ||
options={["Catalog", "Component", "Connection", "Playbook"]} | ||
className="w-auto" | ||
itemsClassName="" | ||
defaultValue="Go Template" | ||
value={switchOption} | ||
onChange={(v) => { | ||
setSwitchOption(v); | ||
setFieldValue("config_id", undefined); | ||
setFieldValue("check_id", undefined); | ||
setFieldValue("canary_id", undefined); | ||
setFieldValue("component_id", undefined); | ||
setFieldValue("playbook_id", undefined); | ||
}} | ||
/> | ||
</div> | ||
|
||
{switchOption === "Catalog" && ( | ||
<FormikResourceSelectorDropdown | ||
required | ||
name="config_id" | ||
configResourceSelector={[{}]} | ||
/> | ||
)} | ||
|
||
{switchOption === "Component" && ( | ||
<FormikResourceSelectorDropdown | ||
required | ||
name="component_id" | ||
componentResourceSelector={[{}]} | ||
/> | ||
)} | ||
|
||
{switchOption === "Playbook" && ( | ||
<FormikPlaybooksDropdown required name="playbook_id" /> | ||
)} | ||
|
||
{switchOption === "Canary" && ( | ||
<FormikCanaryDropdown required name="canary_id" /> | ||
)} | ||
|
||
{switchOption === "Connection" && ( | ||
<FormikConnectionField required name="connection_id" /> | ||
)} | ||
</div> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.