Add risk list and create risk
Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
@@ -22,6 +22,8 @@ import { UpdatePolicyPage } from "./policies/UpdatePolicyPage";
|
|||||||
import { VendorListPage } from "./vendors/VendorListPage";
|
import { VendorListPage } from "./vendors/VendorListPage";
|
||||||
import { VendorPage } from "./vendors/VendorPage";
|
import { VendorPage } from "./vendors/VendorPage";
|
||||||
import { MitigationNewPage } from "./mitigations/MitigationNewPage";
|
import { MitigationNewPage } from "./mitigations/MitigationNewPage";
|
||||||
|
import { RiskListPage } from "./risks/RiskListPage";
|
||||||
|
import { NewRiskPage } from "./risks/NewRiskPage";
|
||||||
|
|
||||||
export function OrganizationsRoutes() {
|
export function OrganizationsRoutes() {
|
||||||
return (
|
return (
|
||||||
@@ -53,6 +55,8 @@ export function OrganizationsRoutes() {
|
|||||||
path="policies/:policyId/update"
|
path="policies/:policyId/update"
|
||||||
element={<UpdatePolicyPage />}
|
element={<UpdatePolicyPage />}
|
||||||
/>
|
/>
|
||||||
|
<Route path="risks" element={<RiskListPage />} />
|
||||||
|
<Route path="risks/new" element={<NewRiskPage />} />
|
||||||
<Route path="settings" element={<SettingsPage />} />
|
<Route path="settings" element={<SettingsPage />} />
|
||||||
<Route path="*" element={<NotFoundPage />} />
|
<Route path="*" element={<NotFoundPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|||||||
29
apps/console/src/pages/organizations/risks/NewRiskPage.tsx
Normal file
29
apps/console/src/pages/organizations/risks/NewRiskPage.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { PageTemplateSkeleton } from "@/components/PageTemplate";
|
||||||
|
import { Suspense } from "react";
|
||||||
|
import { lazy } from "@probo/react-lazy";
|
||||||
|
import { useLocation } from "react-router";
|
||||||
|
import { ErrorBoundaryWithLocation } from "../ErrorBoundary";
|
||||||
|
|
||||||
|
const NewRiskView = lazy(() => import("./NewRiskView"));
|
||||||
|
|
||||||
|
export function NewRiskViewSkeleton() {
|
||||||
|
return (
|
||||||
|
<PageTemplateSkeleton>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="h-96 bg-muted animate-pulse rounded-lg" />
|
||||||
|
</div>
|
||||||
|
</PageTemplateSkeleton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NewRiskPage() {
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Suspense key={location.pathname} fallback={<NewRiskViewSkeleton />}>
|
||||||
|
<ErrorBoundaryWithLocation>
|
||||||
|
<NewRiskView />
|
||||||
|
</ErrorBoundaryWithLocation>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
163
apps/console/src/pages/organizations/risks/NewRiskView.tsx
Normal file
163
apps/console/src/pages/organizations/risks/NewRiskView.tsx
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { useNavigate, useParams } from "react-router";
|
||||||
|
import { ConnectionHandler, graphql, useMutation } from "react-relay";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
import { PageTemplate } from "@/components/PageTemplate";
|
||||||
|
|
||||||
|
const createRiskMutation = graphql`
|
||||||
|
mutation NewRiskViewCreateRiskMutation(
|
||||||
|
$input: CreateRiskInput!
|
||||||
|
$connections: [ID!]!
|
||||||
|
) {
|
||||||
|
createRisk(input: $input) {
|
||||||
|
riskEdge @prependEdge(connections: $connections) {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function NewRiskView() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { organizationId } = useParams<{ organizationId: string }>();
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [description, setDescription] = useState("");
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const [commitMutation, isInFlight] = useMutation(createRiskMutation);
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!name.trim()) {
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "Please enter a name for the risk.",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSubmitting(true);
|
||||||
|
|
||||||
|
const input = {
|
||||||
|
organizationId: organizationId!,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
};
|
||||||
|
|
||||||
|
commitMutation({
|
||||||
|
variables: {
|
||||||
|
input,
|
||||||
|
connections: [
|
||||||
|
ConnectionHandler.getConnectionID(
|
||||||
|
organizationId!,
|
||||||
|
"RiskListView_risks"
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
onCompleted: (response, errors) => {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
if (errors) {
|
||||||
|
console.error("Error creating risk:", errors);
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "Failed to create risk. Please try again.",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Success",
|
||||||
|
description: "Risk created successfully!",
|
||||||
|
});
|
||||||
|
|
||||||
|
navigate(`/organizations/${organizationId}/risks`);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
console.error("Error creating risk:", error);
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "Failed to create risk. Please try again.",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageTemplate
|
||||||
|
title="Create Risk"
|
||||||
|
description="Add a new risk to your organization"
|
||||||
|
>
|
||||||
|
<Card className="max-w-2xl">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Risk Details</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Enter the details of the risk you want to add to your organization.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="name">Name</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
placeholder="Risk name"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="description">Description</Label>
|
||||||
|
<Textarea
|
||||||
|
id="description"
|
||||||
|
placeholder="Describe the risk in detail"
|
||||||
|
value={description}
|
||||||
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
|
rows={5}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-3">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() =>
|
||||||
|
navigate(`/organizations/${organizationId}/risks`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button type="submit" disabled={isInFlight || isSubmitting}>
|
||||||
|
{isInFlight || isSubmitting ? "Creating..." : "Create Risk"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</PageTemplate>
|
||||||
|
);
|
||||||
|
}
|
||||||
31
apps/console/src/pages/organizations/risks/RiskListPage.tsx
Normal file
31
apps/console/src/pages/organizations/risks/RiskListPage.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { PageTemplateSkeleton } from "@/components/PageTemplate";
|
||||||
|
import { Suspense } from "react";
|
||||||
|
import { lazy } from "@probo/react-lazy";
|
||||||
|
import { useLocation } from "react-router";
|
||||||
|
import { ErrorBoundaryWithLocation } from "../ErrorBoundary";
|
||||||
|
|
||||||
|
const RiskListView = lazy(() => import("./RiskListView"));
|
||||||
|
|
||||||
|
export function RiskViewSkeleton() {
|
||||||
|
return (
|
||||||
|
<PageTemplateSkeleton>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{[1, 2].map((i) => (
|
||||||
|
<div key={i} className="h-20 bg-muted animate-pulse rounded-lg" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</PageTemplateSkeleton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RiskListPage() {
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Suspense key={location.pathname} fallback={<RiskViewSkeleton />}>
|
||||||
|
<ErrorBoundaryWithLocation>
|
||||||
|
<RiskListView />
|
||||||
|
</ErrorBoundaryWithLocation>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
404
apps/console/src/pages/organizations/risks/RiskListView.tsx
Normal file
404
apps/console/src/pages/organizations/risks/RiskListView.tsx
Normal file
@@ -0,0 +1,404 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
graphql,
|
||||||
|
PreloadedQuery,
|
||||||
|
usePaginationFragment,
|
||||||
|
usePreloadedQuery,
|
||||||
|
useQueryLoader,
|
||||||
|
useMutation,
|
||||||
|
} from "react-relay";
|
||||||
|
import {
|
||||||
|
Suspense,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useTransition,
|
||||||
|
} from "react";
|
||||||
|
import type { RiskListViewQuery } from "./__generated__/RiskListViewQuery.graphql";
|
||||||
|
import { useParams, useSearchParams } from "react-router";
|
||||||
|
import { PageTemplate } from "@/components/PageTemplate";
|
||||||
|
import { RiskViewSkeleton } from "./RiskListPage";
|
||||||
|
import { RiskListViewPaginationQuery } from "./__generated__/RiskListViewPaginationQuery.graphql";
|
||||||
|
import { RiskListView_risks$key } from "./__generated__/RiskListView_risks.graphql";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Link } from "react-router";
|
||||||
|
import { Plus, Trash2 } from "lucide-react";
|
||||||
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { RiskListViewDeleteMutation } from "./__generated__/RiskListViewDeleteMutation.graphql";
|
||||||
|
|
||||||
|
const defaultPageSize = 25;
|
||||||
|
|
||||||
|
const riskListViewQuery = graphql`
|
||||||
|
query RiskListViewQuery(
|
||||||
|
$organizationId: ID!
|
||||||
|
$first: Int
|
||||||
|
$after: CursorKey
|
||||||
|
$last: Int
|
||||||
|
$before: CursorKey
|
||||||
|
) {
|
||||||
|
organization: node(id: $organizationId) {
|
||||||
|
id
|
||||||
|
|
||||||
|
...RiskListView_risks
|
||||||
|
@arguments(first: $first, after: $after, last: $last, before: $before)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const riskListFragment = graphql`
|
||||||
|
fragment RiskListView_risks on Organization
|
||||||
|
@refetchable(queryName: "RiskListViewPaginationQuery")
|
||||||
|
@argumentDefinitions(
|
||||||
|
first: { type: "Int" }
|
||||||
|
after: { type: "CursorKey" }
|
||||||
|
last: { type: "Int" }
|
||||||
|
before: { type: "CursorKey" }
|
||||||
|
) {
|
||||||
|
risks(first: $first, after: $after, last: $last, before: $before)
|
||||||
|
@connection(key: "RiskListView_risks") {
|
||||||
|
__id
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
hasPreviousPage
|
||||||
|
startCursor
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const deleteRiskMutation = graphql`
|
||||||
|
mutation RiskListViewDeleteMutation(
|
||||||
|
$input: DeleteRiskInput!
|
||||||
|
$connections: [ID!]!
|
||||||
|
) {
|
||||||
|
deleteRisk(input: $input) {
|
||||||
|
deletedRiskId @deleteEdge(connections: $connections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
function LoadAboveButton({
|
||||||
|
isLoading,
|
||||||
|
hasMore,
|
||||||
|
onLoadMore,
|
||||||
|
}: {
|
||||||
|
isLoading: boolean;
|
||||||
|
hasMore: boolean;
|
||||||
|
onLoadMore: () => void;
|
||||||
|
}) {
|
||||||
|
if (!hasMore) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center mb-6">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={onLoadMore}
|
||||||
|
disabled={isLoading}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{isLoading ? "Loading..." : "Load above"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LoadBelowButton({
|
||||||
|
isLoading,
|
||||||
|
hasMore,
|
||||||
|
onLoadMore,
|
||||||
|
}: {
|
||||||
|
isLoading: boolean;
|
||||||
|
hasMore: boolean;
|
||||||
|
onLoadMore: () => void;
|
||||||
|
}) {
|
||||||
|
if (!hasMore) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center mt-6">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={onLoadMore}
|
||||||
|
disabled={isLoading || !hasMore}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{isLoading ? "Loading..." : "Load below"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RiskListViewContent({
|
||||||
|
queryRef,
|
||||||
|
}: {
|
||||||
|
queryRef: PreloadedQuery<RiskListViewQuery>;
|
||||||
|
}) {
|
||||||
|
const data = usePreloadedQuery(riskListViewQuery, queryRef);
|
||||||
|
const [, setSearchParams] = useSearchParams();
|
||||||
|
const [, startTransition] = useTransition();
|
||||||
|
const { organizationId } = useParams<{ organizationId: string }>();
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
// State for delete confirmation dialog
|
||||||
|
const [riskToDelete, setRiskToDelete] = useState<{
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
} | null>(null);
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
|
|
||||||
|
// Setup delete mutation
|
||||||
|
const [commitDeleteMutation] =
|
||||||
|
useMutation<RiskListViewDeleteMutation>(deleteRiskMutation);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: risksConnection,
|
||||||
|
loadNext,
|
||||||
|
loadPrevious,
|
||||||
|
hasNext,
|
||||||
|
hasPrevious,
|
||||||
|
isLoadingNext,
|
||||||
|
isLoadingPrevious,
|
||||||
|
} = usePaginationFragment<
|
||||||
|
RiskListViewPaginationQuery,
|
||||||
|
RiskListView_risks$key
|
||||||
|
>(riskListFragment, data.organization);
|
||||||
|
|
||||||
|
const risks = risksConnection?.risks?.edges?.map((edge) => edge.node) || [];
|
||||||
|
const pageInfo = risksConnection?.risks?.pageInfo;
|
||||||
|
const connectionId = risksConnection?.risks?.__id;
|
||||||
|
|
||||||
|
// Handle delete risk
|
||||||
|
const handleDeleteRisk = useCallback(() => {
|
||||||
|
if (!riskToDelete || !connectionId) return;
|
||||||
|
|
||||||
|
setIsDeleting(true);
|
||||||
|
|
||||||
|
commitDeleteMutation({
|
||||||
|
variables: {
|
||||||
|
input: {
|
||||||
|
riskId: riskToDelete.id,
|
||||||
|
},
|
||||||
|
connections: [connectionId],
|
||||||
|
},
|
||||||
|
onCompleted: (_, errors) => {
|
||||||
|
setIsDeleting(false);
|
||||||
|
setRiskToDelete(null);
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
console.error("Error deleting risk:", errors);
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "Failed to delete risk. Please try again.",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Success",
|
||||||
|
description: "Risk deleted successfully.",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
setIsDeleting(false);
|
||||||
|
setRiskToDelete(null);
|
||||||
|
console.error("Error deleting risk:", error);
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "Failed to delete risk. Please try again.",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, [riskToDelete, connectionId, commitDeleteMutation, toast]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageTemplate
|
||||||
|
title="Risks"
|
||||||
|
actions={
|
||||||
|
<Button asChild>
|
||||||
|
<Link to={`/organizations/${organizationId}/risks/new`}>
|
||||||
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
New Risk
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="space-y-6">
|
||||||
|
<LoadAboveButton
|
||||||
|
isLoading={isLoadingPrevious}
|
||||||
|
hasMore={hasPrevious}
|
||||||
|
onLoadMore={() => {
|
||||||
|
startTransition(() => {
|
||||||
|
setSearchParams((prev) => {
|
||||||
|
prev.set("before", pageInfo?.startCursor || "");
|
||||||
|
prev.delete("after");
|
||||||
|
return prev;
|
||||||
|
});
|
||||||
|
loadPrevious(defaultPageSize);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-0">
|
||||||
|
<div className="w-full overflow-auto">
|
||||||
|
<table className="w-full caption-bottom text-sm">
|
||||||
|
<thead className="[&_tr]:border-b">
|
||||||
|
<tr className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted">
|
||||||
|
<th className="h-12 px-4 text-left align-middle font-medium text-muted-foreground">
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
<th className="h-12 px-4 text-left align-middle font-medium text-muted-foreground">
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
<th className="h-12 px-4 text-left align-middle font-medium text-muted-foreground">
|
||||||
|
Actions
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="[&_tr:last-child]:border-0">
|
||||||
|
{risks.length === 0 ? (
|
||||||
|
<tr className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted">
|
||||||
|
<td
|
||||||
|
colSpan={3}
|
||||||
|
className="text-center p-4 align-middle text-muted-foreground"
|
||||||
|
>
|
||||||
|
No risks found. Create a new risk to get started.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : (
|
||||||
|
risks.map((risk) => (
|
||||||
|
<tr
|
||||||
|
key={risk.id}
|
||||||
|
className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted cursor-pointer"
|
||||||
|
>
|
||||||
|
<td className="p-4 align-middle font-medium">
|
||||||
|
{risk.name}
|
||||||
|
</td>
|
||||||
|
<td className="p-4 align-middle">{risk.description}</td>
|
||||||
|
<td className="p-4 align-middle">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setRiskToDelete({ id: risk.id, name: risk.name });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 text-destructive" />
|
||||||
|
</Button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<LoadBelowButton
|
||||||
|
isLoading={isLoadingNext}
|
||||||
|
hasMore={hasNext}
|
||||||
|
onLoadMore={() => {
|
||||||
|
startTransition(() => {
|
||||||
|
setSearchParams((prev) => {
|
||||||
|
prev.set("after", pageInfo?.endCursor || "");
|
||||||
|
prev.delete("before");
|
||||||
|
return prev;
|
||||||
|
});
|
||||||
|
loadNext(defaultPageSize);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Delete Confirmation Dialog */}
|
||||||
|
<Dialog
|
||||||
|
open={!!riskToDelete}
|
||||||
|
onOpenChange={(open) => !open && setRiskToDelete(null)}
|
||||||
|
>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Are you sure?</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
This will permanently delete the risk "{riskToDelete?.name}
|
||||||
|
". This action cannot be undone.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => setRiskToDelete(null)}
|
||||||
|
disabled={isDeleting}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleDeleteRisk}
|
||||||
|
disabled={isDeleting}
|
||||||
|
variant="destructive"
|
||||||
|
>
|
||||||
|
{isDeleting ? "Deleting..." : "Delete"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</PageTemplate>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RiskListView() {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const [queryRef, loadQuery] =
|
||||||
|
useQueryLoader<RiskListViewQuery>(riskListViewQuery);
|
||||||
|
|
||||||
|
const { organizationId } = useParams();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const after = searchParams.get("after");
|
||||||
|
const before = searchParams.get("before");
|
||||||
|
|
||||||
|
loadQuery({
|
||||||
|
organizationId: organizationId!,
|
||||||
|
first: before ? undefined : defaultPageSize,
|
||||||
|
after: after || undefined,
|
||||||
|
last: before ? defaultPageSize : undefined,
|
||||||
|
before: before || undefined,
|
||||||
|
});
|
||||||
|
}, [loadQuery, organizationId]);
|
||||||
|
|
||||||
|
if (!queryRef) {
|
||||||
|
return <RiskViewSkeleton />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<RiskViewSkeleton />}>
|
||||||
|
<RiskListViewContent queryRef={queryRef} />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
192
apps/console/src/pages/organizations/risks/__generated__/NewRiskViewCreateRiskMutation.graphql.ts
generated
Normal file
192
apps/console/src/pages/organizations/risks/__generated__/NewRiskViewCreateRiskMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
/**
|
||||||
|
* @generated SignedSource<<c3a526641ca53f4ef760eacadc5c3685>>
|
||||||
|
* @lightSyntaxTransform
|
||||||
|
* @nogrep
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ConcreteRequest } from 'relay-runtime';
|
||||||
|
export type CreateRiskInput = {
|
||||||
|
description: string;
|
||||||
|
name: string;
|
||||||
|
organizationId: string;
|
||||||
|
};
|
||||||
|
export type NewRiskViewCreateRiskMutation$variables = {
|
||||||
|
connections: ReadonlyArray<string>;
|
||||||
|
input: CreateRiskInput;
|
||||||
|
};
|
||||||
|
export type NewRiskViewCreateRiskMutation$data = {
|
||||||
|
readonly createRisk: {
|
||||||
|
readonly riskEdge: {
|
||||||
|
readonly node: {
|
||||||
|
readonly createdAt: string;
|
||||||
|
readonly description: string;
|
||||||
|
readonly id: string;
|
||||||
|
readonly name: string;
|
||||||
|
readonly updatedAt: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type NewRiskViewCreateRiskMutation = {
|
||||||
|
response: NewRiskViewCreateRiskMutation$data;
|
||||||
|
variables: NewRiskViewCreateRiskMutation$variables;
|
||||||
|
};
|
||||||
|
|
||||||
|
const node: ConcreteRequest = (function(){
|
||||||
|
var v0 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "connections"
|
||||||
|
},
|
||||||
|
v1 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "input"
|
||||||
|
},
|
||||||
|
v2 = [
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "input",
|
||||||
|
"variableName": "input"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
v3 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "RiskEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "riskEdge",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Risk",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "id",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "name",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "description",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "createdAt",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "updatedAt",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
"fragment": {
|
||||||
|
"argumentDefinitions": [
|
||||||
|
(v0/*: any*/),
|
||||||
|
(v1/*: any*/)
|
||||||
|
],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "NewRiskViewCreateRiskMutation",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": (v2/*: any*/),
|
||||||
|
"concreteType": "CreateRiskPayload",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "createRisk",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v3/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Mutation",
|
||||||
|
"abstractKey": null
|
||||||
|
},
|
||||||
|
"kind": "Request",
|
||||||
|
"operation": {
|
||||||
|
"argumentDefinitions": [
|
||||||
|
(v1/*: any*/),
|
||||||
|
(v0/*: any*/)
|
||||||
|
],
|
||||||
|
"kind": "Operation",
|
||||||
|
"name": "NewRiskViewCreateRiskMutation",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": (v2/*: any*/),
|
||||||
|
"concreteType": "CreateRiskPayload",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "createRisk",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v3/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"filters": null,
|
||||||
|
"handle": "prependEdge",
|
||||||
|
"key": "",
|
||||||
|
"kind": "LinkedHandle",
|
||||||
|
"name": "riskEdge",
|
||||||
|
"handleArgs": [
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "connections",
|
||||||
|
"variableName": "connections"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"cacheID": "ce3ac503734a6f0eb26f88813ed02858",
|
||||||
|
"id": null,
|
||||||
|
"metadata": {},
|
||||||
|
"name": "NewRiskViewCreateRiskMutation",
|
||||||
|
"operationKind": "mutation",
|
||||||
|
"text": "mutation NewRiskViewCreateRiskMutation(\n $input: CreateRiskInput!\n) {\n createRisk(input: $input) {\n riskEdge {\n node {\n id\n name\n description\n createdAt\n updatedAt\n }\n }\n }\n}\n"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
(node as any).hash = "c0d8d1f046ed41c7a429c54b32358d3f";
|
||||||
|
|
||||||
|
export default node;
|
||||||
132
apps/console/src/pages/organizations/risks/__generated__/RiskListViewDeleteMutation.graphql.ts
generated
Normal file
132
apps/console/src/pages/organizations/risks/__generated__/RiskListViewDeleteMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* @generated SignedSource<<349d62a3a4ed3ea1d83c12f0c61437dd>>
|
||||||
|
* @lightSyntaxTransform
|
||||||
|
* @nogrep
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ConcreteRequest } from 'relay-runtime';
|
||||||
|
export type DeleteRiskInput = {
|
||||||
|
riskId: string;
|
||||||
|
};
|
||||||
|
export type RiskListViewDeleteMutation$variables = {
|
||||||
|
connections: ReadonlyArray<string>;
|
||||||
|
input: DeleteRiskInput;
|
||||||
|
};
|
||||||
|
export type RiskListViewDeleteMutation$data = {
|
||||||
|
readonly deleteRisk: {
|
||||||
|
readonly deletedRiskId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type RiskListViewDeleteMutation = {
|
||||||
|
response: RiskListViewDeleteMutation$data;
|
||||||
|
variables: RiskListViewDeleteMutation$variables;
|
||||||
|
};
|
||||||
|
|
||||||
|
const node: ConcreteRequest = (function(){
|
||||||
|
var v0 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "connections"
|
||||||
|
},
|
||||||
|
v1 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "input"
|
||||||
|
},
|
||||||
|
v2 = [
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "input",
|
||||||
|
"variableName": "input"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
v3 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "deletedRiskId",
|
||||||
|
"storageKey": null
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
"fragment": {
|
||||||
|
"argumentDefinitions": [
|
||||||
|
(v0/*: any*/),
|
||||||
|
(v1/*: any*/)
|
||||||
|
],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "RiskListViewDeleteMutation",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": (v2/*: any*/),
|
||||||
|
"concreteType": "DeleteRiskPayload",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "deleteRisk",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v3/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Mutation",
|
||||||
|
"abstractKey": null
|
||||||
|
},
|
||||||
|
"kind": "Request",
|
||||||
|
"operation": {
|
||||||
|
"argumentDefinitions": [
|
||||||
|
(v1/*: any*/),
|
||||||
|
(v0/*: any*/)
|
||||||
|
],
|
||||||
|
"kind": "Operation",
|
||||||
|
"name": "RiskListViewDeleteMutation",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": (v2/*: any*/),
|
||||||
|
"concreteType": "DeleteRiskPayload",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "deleteRisk",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v3/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"filters": null,
|
||||||
|
"handle": "deleteEdge",
|
||||||
|
"key": "",
|
||||||
|
"kind": "ScalarHandle",
|
||||||
|
"name": "deletedRiskId",
|
||||||
|
"handleArgs": [
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "connections",
|
||||||
|
"variableName": "connections"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"cacheID": "525011737d0920883b3942d2603dc31f",
|
||||||
|
"id": null,
|
||||||
|
"metadata": {},
|
||||||
|
"name": "RiskListViewDeleteMutation",
|
||||||
|
"operationKind": "mutation",
|
||||||
|
"text": "mutation RiskListViewDeleteMutation(\n $input: DeleteRiskInput!\n) {\n deleteRisk(input: $input) {\n deletedRiskId\n }\n}\n"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
(node as any).hash = "4aee92e5abfd6733b44588bf01276560";
|
||||||
|
|
||||||
|
export default node;
|
||||||
309
apps/console/src/pages/organizations/risks/__generated__/RiskListViewPaginationQuery.graphql.ts
generated
Normal file
309
apps/console/src/pages/organizations/risks/__generated__/RiskListViewPaginationQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
/**
|
||||||
|
* @generated SignedSource<<56b70bbecd785111cec0e7ff6ec2c226>>
|
||||||
|
* @lightSyntaxTransform
|
||||||
|
* @nogrep
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ConcreteRequest } from 'relay-runtime';
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type RiskListViewPaginationQuery$variables = {
|
||||||
|
after?: string | null | undefined;
|
||||||
|
before?: string | null | undefined;
|
||||||
|
first?: number | null | undefined;
|
||||||
|
id: string;
|
||||||
|
last?: number | null | undefined;
|
||||||
|
};
|
||||||
|
export type RiskListViewPaginationQuery$data = {
|
||||||
|
readonly node: {
|
||||||
|
readonly " $fragmentSpreads": FragmentRefs<"RiskListView_risks">;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type RiskListViewPaginationQuery = {
|
||||||
|
response: RiskListViewPaginationQuery$data;
|
||||||
|
variables: RiskListViewPaginationQuery$variables;
|
||||||
|
};
|
||||||
|
|
||||||
|
const node: ConcreteRequest = (function(){
|
||||||
|
var v0 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "after"
|
||||||
|
},
|
||||||
|
v1 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "before"
|
||||||
|
},
|
||||||
|
v2 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "first"
|
||||||
|
},
|
||||||
|
v3 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "id"
|
||||||
|
},
|
||||||
|
v4 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "last"
|
||||||
|
},
|
||||||
|
v5 = [
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "id",
|
||||||
|
"variableName": "id"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
v6 = [
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "after",
|
||||||
|
"variableName": "after"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "before",
|
||||||
|
"variableName": "before"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "first",
|
||||||
|
"variableName": "first"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "last",
|
||||||
|
"variableName": "last"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
v7 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "__typename",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
v8 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "id",
|
||||||
|
"storageKey": null
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
"fragment": {
|
||||||
|
"argumentDefinitions": [
|
||||||
|
(v0/*: any*/),
|
||||||
|
(v1/*: any*/),
|
||||||
|
(v2/*: any*/),
|
||||||
|
(v3/*: any*/),
|
||||||
|
(v4/*: any*/)
|
||||||
|
],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "RiskListViewPaginationQuery",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": (v5/*: any*/),
|
||||||
|
"concreteType": null,
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"args": (v6/*: any*/),
|
||||||
|
"kind": "FragmentSpread",
|
||||||
|
"name": "RiskListView_risks"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Query",
|
||||||
|
"abstractKey": null
|
||||||
|
},
|
||||||
|
"kind": "Request",
|
||||||
|
"operation": {
|
||||||
|
"argumentDefinitions": [
|
||||||
|
(v0/*: any*/),
|
||||||
|
(v1/*: any*/),
|
||||||
|
(v2/*: any*/),
|
||||||
|
(v4/*: any*/),
|
||||||
|
(v3/*: any*/)
|
||||||
|
],
|
||||||
|
"kind": "Operation",
|
||||||
|
"name": "RiskListViewPaginationQuery",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": (v5/*: any*/),
|
||||||
|
"concreteType": null,
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v7/*: any*/),
|
||||||
|
(v8/*: any*/),
|
||||||
|
{
|
||||||
|
"kind": "InlineFragment",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": (v6/*: any*/),
|
||||||
|
"concreteType": "RiskConnection",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "risks",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "RiskEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "edges",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Risk",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v8/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "name",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "description",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "createdAt",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "updatedAt",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v7/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "cursor",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "PageInfo",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "pageInfo",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "hasNextPage",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "hasPreviousPage",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "startCursor",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "endCursor",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "ClientExtension",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "__id",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": (v6/*: any*/),
|
||||||
|
"filters": null,
|
||||||
|
"handle": "connection",
|
||||||
|
"key": "RiskListView_risks",
|
||||||
|
"kind": "LinkedHandle",
|
||||||
|
"name": "risks"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Organization",
|
||||||
|
"abstractKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"cacheID": "ea3c50c98dea1cce588bbb0e8705cbc4",
|
||||||
|
"id": null,
|
||||||
|
"metadata": {},
|
||||||
|
"name": "RiskListViewPaginationQuery",
|
||||||
|
"operationKind": "query",
|
||||||
|
"text": "query RiskListViewPaginationQuery(\n $after: CursorKey\n $before: CursorKey\n $first: Int\n $last: Int\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...RiskListView_risks_pbnwq\n id\n }\n}\n\nfragment RiskListView_risks_pbnwq on Organization {\n risks(first: $first, after: $after, last: $last, before: $before) {\n edges {\n node {\n id\n name\n description\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
(node as any).hash = "4a665835c97d7b93725e8bbe708d7cfb";
|
||||||
|
|
||||||
|
export default node;
|
||||||
311
apps/console/src/pages/organizations/risks/__generated__/RiskListViewQuery.graphql.ts
generated
Normal file
311
apps/console/src/pages/organizations/risks/__generated__/RiskListViewQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
/**
|
||||||
|
* @generated SignedSource<<2812fb6e9c5db33d4c616daf9a11b012>>
|
||||||
|
* @lightSyntaxTransform
|
||||||
|
* @nogrep
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ConcreteRequest } from 'relay-runtime';
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type RiskListViewQuery$variables = {
|
||||||
|
after?: string | null | undefined;
|
||||||
|
before?: string | null | undefined;
|
||||||
|
first?: number | null | undefined;
|
||||||
|
last?: number | null | undefined;
|
||||||
|
organizationId: string;
|
||||||
|
};
|
||||||
|
export type RiskListViewQuery$data = {
|
||||||
|
readonly organization: {
|
||||||
|
readonly id: string;
|
||||||
|
readonly " $fragmentSpreads": FragmentRefs<"RiskListView_risks">;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type RiskListViewQuery = {
|
||||||
|
response: RiskListViewQuery$data;
|
||||||
|
variables: RiskListViewQuery$variables;
|
||||||
|
};
|
||||||
|
|
||||||
|
const node: ConcreteRequest = (function(){
|
||||||
|
var v0 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "after"
|
||||||
|
},
|
||||||
|
v1 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "before"
|
||||||
|
},
|
||||||
|
v2 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "first"
|
||||||
|
},
|
||||||
|
v3 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "last"
|
||||||
|
},
|
||||||
|
v4 = {
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "organizationId"
|
||||||
|
},
|
||||||
|
v5 = [
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "id",
|
||||||
|
"variableName": "organizationId"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
v6 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "id",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
v7 = [
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "after",
|
||||||
|
"variableName": "after"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "before",
|
||||||
|
"variableName": "before"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "first",
|
||||||
|
"variableName": "first"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Variable",
|
||||||
|
"name": "last",
|
||||||
|
"variableName": "last"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
v8 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "__typename",
|
||||||
|
"storageKey": null
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
"fragment": {
|
||||||
|
"argumentDefinitions": [
|
||||||
|
(v0/*: any*/),
|
||||||
|
(v1/*: any*/),
|
||||||
|
(v2/*: any*/),
|
||||||
|
(v3/*: any*/),
|
||||||
|
(v4/*: any*/)
|
||||||
|
],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "RiskListViewQuery",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": "organization",
|
||||||
|
"args": (v5/*: any*/),
|
||||||
|
"concreteType": null,
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v6/*: any*/),
|
||||||
|
{
|
||||||
|
"args": (v7/*: any*/),
|
||||||
|
"kind": "FragmentSpread",
|
||||||
|
"name": "RiskListView_risks"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Query",
|
||||||
|
"abstractKey": null
|
||||||
|
},
|
||||||
|
"kind": "Request",
|
||||||
|
"operation": {
|
||||||
|
"argumentDefinitions": [
|
||||||
|
(v4/*: any*/),
|
||||||
|
(v2/*: any*/),
|
||||||
|
(v0/*: any*/),
|
||||||
|
(v3/*: any*/),
|
||||||
|
(v1/*: any*/)
|
||||||
|
],
|
||||||
|
"kind": "Operation",
|
||||||
|
"name": "RiskListViewQuery",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": "organization",
|
||||||
|
"args": (v5/*: any*/),
|
||||||
|
"concreteType": null,
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v8/*: any*/),
|
||||||
|
(v6/*: any*/),
|
||||||
|
{
|
||||||
|
"kind": "InlineFragment",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": (v7/*: any*/),
|
||||||
|
"concreteType": "RiskConnection",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "risks",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "RiskEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "edges",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Risk",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v6/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "name",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "description",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "createdAt",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "updatedAt",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v8/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "cursor",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "PageInfo",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "pageInfo",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "hasNextPage",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "hasPreviousPage",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "startCursor",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "endCursor",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "ClientExtension",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "__id",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": (v7/*: any*/),
|
||||||
|
"filters": null,
|
||||||
|
"handle": "connection",
|
||||||
|
"key": "RiskListView_risks",
|
||||||
|
"kind": "LinkedHandle",
|
||||||
|
"name": "risks"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Organization",
|
||||||
|
"abstractKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"cacheID": "7b0cab0d71217df4a49ea2245b2705a8",
|
||||||
|
"id": null,
|
||||||
|
"metadata": {},
|
||||||
|
"name": "RiskListViewQuery",
|
||||||
|
"operationKind": "query",
|
||||||
|
"text": "query RiskListViewQuery(\n $organizationId: ID!\n $first: Int\n $after: CursorKey\n $last: Int\n $before: CursorKey\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...RiskListView_risks_pbnwq\n }\n}\n\nfragment RiskListView_risks_pbnwq on Organization {\n risks(first: $first, after: $after, last: $last, before: $before) {\n edges {\n node {\n id\n name\n description\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
(node as any).hash = "0fff20fd656047e6df52818ccf0a0f3b";
|
||||||
|
|
||||||
|
export default node;
|
||||||
244
apps/console/src/pages/organizations/risks/__generated__/RiskListView_risks.graphql.ts
generated
Normal file
244
apps/console/src/pages/organizations/risks/__generated__/RiskListView_risks.graphql.ts
generated
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
/**
|
||||||
|
* @generated SignedSource<<5cb8bd63e484e9c8d842ddf05618d475>>
|
||||||
|
* @lightSyntaxTransform
|
||||||
|
* @nogrep
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ReaderFragment } from 'relay-runtime';
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type RiskListView_risks$data = {
|
||||||
|
readonly id: string;
|
||||||
|
readonly risks: {
|
||||||
|
readonly __id: string;
|
||||||
|
readonly edges: ReadonlyArray<{
|
||||||
|
readonly node: {
|
||||||
|
readonly createdAt: string;
|
||||||
|
readonly description: string;
|
||||||
|
readonly id: string;
|
||||||
|
readonly name: string;
|
||||||
|
readonly updatedAt: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
readonly pageInfo: {
|
||||||
|
readonly endCursor: string | null | undefined;
|
||||||
|
readonly hasNextPage: boolean;
|
||||||
|
readonly hasPreviousPage: boolean;
|
||||||
|
readonly startCursor: string | null | undefined;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
readonly " $fragmentType": "RiskListView_risks";
|
||||||
|
};
|
||||||
|
export type RiskListView_risks$key = {
|
||||||
|
readonly " $data"?: RiskListView_risks$data;
|
||||||
|
readonly " $fragmentSpreads": FragmentRefs<"RiskListView_risks">;
|
||||||
|
};
|
||||||
|
|
||||||
|
const node: ReaderFragment = (function(){
|
||||||
|
var v0 = [
|
||||||
|
"risks"
|
||||||
|
],
|
||||||
|
v1 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "id",
|
||||||
|
"storageKey": null
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
"argumentDefinitions": [
|
||||||
|
{
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "after"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "before"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "first"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultValue": null,
|
||||||
|
"kind": "LocalArgument",
|
||||||
|
"name": "last"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": {
|
||||||
|
"connection": [
|
||||||
|
{
|
||||||
|
"count": null,
|
||||||
|
"cursor": null,
|
||||||
|
"direction": "bidirectional",
|
||||||
|
"path": (v0/*: any*/)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refetch": {
|
||||||
|
"connection": {
|
||||||
|
"forward": {
|
||||||
|
"count": "first",
|
||||||
|
"cursor": "after"
|
||||||
|
},
|
||||||
|
"backward": {
|
||||||
|
"count": "last",
|
||||||
|
"cursor": "before"
|
||||||
|
},
|
||||||
|
"path": (v0/*: any*/)
|
||||||
|
},
|
||||||
|
"fragmentPathInResult": [
|
||||||
|
"node"
|
||||||
|
],
|
||||||
|
"operation": require('./RiskListViewPaginationQuery.graphql'),
|
||||||
|
"identifierInfo": {
|
||||||
|
"identifierField": "id",
|
||||||
|
"identifierQueryVariableName": "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "RiskListView_risks",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": "risks",
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "RiskConnection",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "__RiskListView_risks_connection",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "RiskEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "edges",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Risk",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v1/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "name",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "description",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "createdAt",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "updatedAt",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "__typename",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "cursor",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "PageInfo",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "pageInfo",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "hasNextPage",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "hasPreviousPage",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "startCursor",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "endCursor",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "ClientExtension",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "__id",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v1/*: any*/)
|
||||||
|
],
|
||||||
|
"type": "Organization",
|
||||||
|
"abstractKey": null
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
(node as any).hash = "4a665835c97d7b93725e8bbe708d7cfb";
|
||||||
|
|
||||||
|
export default node;
|
||||||
@@ -20,14 +20,15 @@ const (
|
|||||||
MitigationEntityType
|
MitigationEntityType
|
||||||
TaskEntityType
|
TaskEntityType
|
||||||
EvidenceEntityType
|
EvidenceEntityType
|
||||||
ControlStateTransitionEntityType
|
_ControlStateTransitionEntityType
|
||||||
TaskStateTransitionEntityType
|
_TaskStateTransitionEntityType
|
||||||
VendorEntityType
|
VendorEntityType
|
||||||
PeopleEntityType
|
PeopleEntityType
|
||||||
EvidenceStateTransitionEntityType
|
_EvidenceStateTransitionEntityType
|
||||||
PolicyEntityType
|
PolicyEntityType
|
||||||
UserEntityType
|
UserEntityType
|
||||||
SessionEntityType
|
SessionEntityType
|
||||||
EmailEntityType
|
EmailEntityType
|
||||||
ControlEntityType
|
ControlEntityType
|
||||||
|
RiskEntityType
|
||||||
)
|
)
|
||||||
|
|||||||
9
pkg/coredata/migrations/20250330T163200Z.sql
Normal file
9
pkg/coredata/migrations/20250330T163200Z.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
CREATE TABLE risks (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
organization_id TEXT NOT NULL REFERENCES organizations(id),
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
created_at TIMESTAMP NOT NULL,
|
||||||
|
updated_at TIMESTAMP NOT NULL
|
||||||
|
);
|
||||||
198
pkg/coredata/risk.go
Normal file
198
pkg/coredata/risk.go
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
package coredata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"maps"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/getprobo/probo/pkg/gid"
|
||||||
|
"github.com/getprobo/probo/pkg/page"
|
||||||
|
"github.com/jackc/pgx/v5"
|
||||||
|
"go.gearno.de/kit/pg"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Risk struct {
|
||||||
|
ID gid.GID
|
||||||
|
OrganizationID gid.GID
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
Risks []*Risk
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *Risk) CursorKey(orderBy RiskOrderField) page.CursorKey {
|
||||||
|
switch orderBy {
|
||||||
|
case RiskOrderFieldCreatedAt:
|
||||||
|
return page.CursorKey{ID: r.ID, Value: r.CreatedAt}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Risks) LoadByOrganizationID(
|
||||||
|
ctx context.Context,
|
||||||
|
conn pg.Conn,
|
||||||
|
scope Scoper,
|
||||||
|
organizationID gid.GID,
|
||||||
|
cursor *page.Cursor[RiskOrderField],
|
||||||
|
) error {
|
||||||
|
q := `
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
organization_id,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
FROM risks
|
||||||
|
WHERE %s
|
||||||
|
AND organization_id = @organization_id
|
||||||
|
AND %s
|
||||||
|
`
|
||||||
|
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
|
||||||
|
|
||||||
|
args := pgx.NamedArgs{"organization_id": organizationID}
|
||||||
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
|
rows, err := conn.Query(ctx, q, args)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot query risks: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
risks, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Risk])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot collect risks: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
*r = risks
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Risk) LoadByID(
|
||||||
|
ctx context.Context,
|
||||||
|
conn pg.Conn,
|
||||||
|
scope Scoper,
|
||||||
|
riskID gid.GID,
|
||||||
|
) error {
|
||||||
|
q := `
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
organization_id,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
FROM risks
|
||||||
|
WHERE %s
|
||||||
|
AND id = @id
|
||||||
|
LIMIT 1;
|
||||||
|
`
|
||||||
|
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||||
|
|
||||||
|
args := pgx.StrictNamedArgs{"id": r.ID}
|
||||||
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
|
rows, err := conn.Query(ctx, q, args)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot query risk: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
risk, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Risk])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot collect risk: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
*r = risk
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Risk) Insert(
|
||||||
|
ctx context.Context,
|
||||||
|
conn pg.Conn,
|
||||||
|
scope Scoper,
|
||||||
|
) error {
|
||||||
|
q := `
|
||||||
|
INSERT INTO risks (id, tenant_id, organization_id, name, description, created_at, updated_at)
|
||||||
|
VALUES (@id, @tenant_id, @organization_id, @name, @description, @created_at, @updated_at)
|
||||||
|
`
|
||||||
|
|
||||||
|
args := pgx.StrictNamedArgs{
|
||||||
|
"id": r.ID,
|
||||||
|
"tenant_id": scope.GetTenantID(),
|
||||||
|
"organization_id": r.OrganizationID,
|
||||||
|
"name": r.Name,
|
||||||
|
"description": r.Description,
|
||||||
|
"created_at": r.CreatedAt,
|
||||||
|
"updated_at": r.UpdatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := conn.Exec(ctx, q, args)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Risk) Update(
|
||||||
|
ctx context.Context,
|
||||||
|
conn pg.Conn,
|
||||||
|
scope Scoper,
|
||||||
|
) error {
|
||||||
|
q := `
|
||||||
|
UPDATE risks
|
||||||
|
SET
|
||||||
|
name = @name,
|
||||||
|
description = @description,
|
||||||
|
updated_at = @updated_at
|
||||||
|
WHERE %s
|
||||||
|
AND tenant_id = @tenant_id
|
||||||
|
`
|
||||||
|
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||||
|
|
||||||
|
args := pgx.StrictNamedArgs{
|
||||||
|
"name": r.Name,
|
||||||
|
"description": r.Description,
|
||||||
|
"updated_at": r.UpdatedAt,
|
||||||
|
}
|
||||||
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
|
_, err := conn.Exec(ctx, q, args)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Risk) Delete(
|
||||||
|
ctx context.Context,
|
||||||
|
conn pg.Conn,
|
||||||
|
scope Scoper,
|
||||||
|
) error {
|
||||||
|
q := `
|
||||||
|
DELETE FROM risks WHERE %s AND id = @id
|
||||||
|
`
|
||||||
|
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||||
|
|
||||||
|
args := pgx.StrictNamedArgs{
|
||||||
|
"id": r.ID,
|
||||||
|
}
|
||||||
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
|
_, err := conn.Exec(ctx, q, args)
|
||||||
|
return err
|
||||||
|
}
|
||||||
40
pkg/coredata/risk_order_field.go
Normal file
40
pkg/coredata/risk_order_field.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
package coredata
|
||||||
|
|
||||||
|
type (
|
||||||
|
RiskOrderField string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RiskOrderFieldCreatedAt RiskOrderField = "CREATED_AT"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p RiskOrderField) Column() string {
|
||||||
|
return string(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p RiskOrderField) String() string {
|
||||||
|
return string(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p RiskOrderField) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(p.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *RiskOrderField) UnmarshalText(text []byte) error {
|
||||||
|
*p = RiskOrderField(text)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
173
pkg/probo/risk_service.go
Normal file
173
pkg/probo/risk_service.go
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
package probo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/getprobo/probo/pkg/coredata"
|
||||||
|
"github.com/getprobo/probo/pkg/gid"
|
||||||
|
"github.com/getprobo/probo/pkg/page"
|
||||||
|
"go.gearno.de/kit/pg"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
RiskService struct {
|
||||||
|
svc *TenantService
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateRiskRequest struct {
|
||||||
|
OrganizationID gid.GID
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateRiskRequest struct {
|
||||||
|
ID gid.GID
|
||||||
|
Name *string
|
||||||
|
Description *string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s RiskService) Create(
|
||||||
|
ctx context.Context,
|
||||||
|
req CreateRiskRequest,
|
||||||
|
) (*coredata.Risk, error) {
|
||||||
|
now := time.Now()
|
||||||
|
riskID, err := gid.NewGID(s.svc.scope.GetTenantID(), coredata.RiskEntityType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot create global id: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
risk := &coredata.Risk{
|
||||||
|
ID: riskID,
|
||||||
|
OrganizationID: req.OrganizationID,
|
||||||
|
Name: req.Name,
|
||||||
|
Description: req.Description,
|
||||||
|
CreatedAt: now,
|
||||||
|
UpdatedAt: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.svc.pg.WithConn(
|
||||||
|
ctx,
|
||||||
|
func(conn pg.Conn) error {
|
||||||
|
return risk.Insert(ctx, conn, s.svc.scope)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot create risk: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return risk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s RiskService) Get(
|
||||||
|
ctx context.Context,
|
||||||
|
riskID gid.GID,
|
||||||
|
) (*coredata.Risk, error) {
|
||||||
|
risk := &coredata.Risk{}
|
||||||
|
|
||||||
|
err := s.svc.pg.WithConn(
|
||||||
|
ctx,
|
||||||
|
func(conn pg.Conn) error {
|
||||||
|
return risk.LoadByID(ctx, conn, s.svc.scope, riskID)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot get risk: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return risk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s RiskService) Update(
|
||||||
|
ctx context.Context,
|
||||||
|
req UpdateRiskRequest,
|
||||||
|
) (*coredata.Risk, error) {
|
||||||
|
risk := &coredata.Risk{ID: req.ID}
|
||||||
|
|
||||||
|
err := s.svc.pg.WithTx(
|
||||||
|
ctx,
|
||||||
|
func(conn pg.Conn) error {
|
||||||
|
if err := risk.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil {
|
||||||
|
return fmt.Errorf("cannot load risk: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Name != nil {
|
||||||
|
risk.Name = *req.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Description != nil {
|
||||||
|
risk.Description = *req.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := risk.Update(ctx, conn, s.svc.scope); err != nil {
|
||||||
|
return fmt.Errorf("cannot update risk: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot update risk: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return risk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s RiskService) Delete(
|
||||||
|
ctx context.Context,
|
||||||
|
riskID gid.GID,
|
||||||
|
) error {
|
||||||
|
risk := &coredata.Risk{ID: riskID}
|
||||||
|
|
||||||
|
return s.svc.pg.WithConn(
|
||||||
|
ctx,
|
||||||
|
func(conn pg.Conn) error {
|
||||||
|
return risk.Delete(ctx, conn, s.svc.scope)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s RiskService) ListForOrganizationID(
|
||||||
|
ctx context.Context,
|
||||||
|
organizationID gid.GID,
|
||||||
|
cursor *page.Cursor[coredata.RiskOrderField],
|
||||||
|
) (*page.Page[*coredata.Risk, coredata.RiskOrderField], error) {
|
||||||
|
var risks coredata.Risks
|
||||||
|
|
||||||
|
err := s.svc.pg.WithConn(
|
||||||
|
ctx,
|
||||||
|
func(conn pg.Conn) error {
|
||||||
|
return risks.LoadByOrganizationID(
|
||||||
|
ctx,
|
||||||
|
conn,
|
||||||
|
s.svc.scope,
|
||||||
|
organizationID,
|
||||||
|
cursor,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot list risks: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return page.NewPage(risks, cursor), nil
|
||||||
|
}
|
||||||
@@ -47,6 +47,7 @@ type (
|
|||||||
Peoples *PeopleService
|
Peoples *PeopleService
|
||||||
Policies *PolicyService
|
Policies *PolicyService
|
||||||
Controls *ControlService
|
Controls *ControlService
|
||||||
|
Risks *RiskService
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,6 +87,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
|
|||||||
tenantService.Policies = &PolicyService{svc: tenantService}
|
tenantService.Policies = &PolicyService{svc: tenantService}
|
||||||
tenantService.Organizations = &OrganizationService{svc: tenantService}
|
tenantService.Organizations = &OrganizationService{svc: tenantService}
|
||||||
tenantService.Controls = &ControlService{svc: tenantService}
|
tenantService.Controls = &ControlService{svc: tenantService}
|
||||||
|
tenantService.Risks = &RiskService{svc: tenantService}
|
||||||
|
|
||||||
return tenantService
|
return tenantService
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,6 +215,14 @@ enum PolicyOrderField
|
|||||||
NAME
|
NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum RiskOrderField
|
||||||
|
@goModel(model: "github.com/getprobo/probo/pkg/coredata.RiskOrderField") {
|
||||||
|
CREATED_AT
|
||||||
|
@goEnum(
|
||||||
|
value: "github.com/getprobo/probo/pkg/coredata.RiskOrderFieldCreatedAt"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
enum EvidenceOrderField
|
enum EvidenceOrderField
|
||||||
@goModel(model: "github.com/getprobo/probo/pkg/coredata.EvidenceOrderField") {
|
@goModel(model: "github.com/getprobo/probo/pkg/coredata.EvidenceOrderField") {
|
||||||
CREATED_AT
|
CREATED_AT
|
||||||
@@ -291,6 +299,14 @@ input PolicyOrder
|
|||||||
field: PolicyOrderField!
|
field: PolicyOrderField!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input RiskOrder
|
||||||
|
@goModel(
|
||||||
|
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.RiskOrderBy"
|
||||||
|
) {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: RiskOrderField!
|
||||||
|
}
|
||||||
|
|
||||||
input EvidenceOrder
|
input EvidenceOrder
|
||||||
@goModel(
|
@goModel(
|
||||||
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.EvidenceOrderBy"
|
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.EvidenceOrderBy"
|
||||||
@@ -358,6 +374,14 @@ type Organization implements Node {
|
|||||||
orderBy: MitigationOrder
|
orderBy: MitigationOrder
|
||||||
): MitigationConnection! @goField(forceResolver: true)
|
): MitigationConnection! @goField(forceResolver: true)
|
||||||
|
|
||||||
|
risks(
|
||||||
|
first: Int
|
||||||
|
after: CursorKey
|
||||||
|
last: Int
|
||||||
|
before: CursorKey
|
||||||
|
orderBy: RiskOrder
|
||||||
|
): RiskConnection! @goField(forceResolver: true)
|
||||||
|
|
||||||
createdAt: Datetime!
|
createdAt: Datetime!
|
||||||
updatedAt: Datetime!
|
updatedAt: Datetime!
|
||||||
}
|
}
|
||||||
@@ -487,6 +511,14 @@ type Policy implements Node {
|
|||||||
updatedAt: Datetime!
|
updatedAt: Datetime!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Risk implements Node {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
description: String!
|
||||||
|
createdAt: Datetime!
|
||||||
|
updatedAt: Datetime!
|
||||||
|
}
|
||||||
|
|
||||||
type Session {
|
type Session {
|
||||||
id: ID!
|
id: ID!
|
||||||
expiresAt: Datetime!
|
expiresAt: Datetime!
|
||||||
@@ -606,6 +638,16 @@ type PolicyEdge {
|
|||||||
node: Policy!
|
node: Policy!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RiskConnection {
|
||||||
|
edges: [RiskEdge!]!
|
||||||
|
pageInfo: PageInfo!
|
||||||
|
}
|
||||||
|
|
||||||
|
type RiskEdge {
|
||||||
|
cursor: CursorKey!
|
||||||
|
node: Risk!
|
||||||
|
}
|
||||||
|
|
||||||
# Root Types
|
# Root Types
|
||||||
type Query {
|
type Query {
|
||||||
node(id: ID!): Node!
|
node(id: ID!): Node!
|
||||||
@@ -657,6 +699,11 @@ type Mutation {
|
|||||||
assignTask(input: AssignTaskInput!): AssignTaskPayload!
|
assignTask(input: AssignTaskInput!): AssignTaskPayload!
|
||||||
unassignTask(input: UnassignTaskInput!): UnassignTaskPayload!
|
unassignTask(input: UnassignTaskInput!): UnassignTaskPayload!
|
||||||
|
|
||||||
|
# Risk mutations
|
||||||
|
createRisk(input: CreateRiskInput!): CreateRiskPayload!
|
||||||
|
updateRisk(input: UpdateRiskInput!): UpdateRiskPayload!
|
||||||
|
deleteRisk(input: DeleteRiskInput!): DeleteRiskPayload!
|
||||||
|
|
||||||
# Evidence mutations
|
# Evidence mutations
|
||||||
uploadEvidence(input: UploadEvidenceInput!): UploadEvidencePayload!
|
uploadEvidence(input: UploadEvidenceInput!): UploadEvidencePayload!
|
||||||
deleteEvidence(input: DeleteEvidenceInput!): DeleteEvidencePayload!
|
deleteEvidence(input: DeleteEvidenceInput!): DeleteEvidencePayload!
|
||||||
@@ -804,6 +851,22 @@ input UnassignTaskInput {
|
|||||||
taskId: ID!
|
taskId: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input CreateRiskInput {
|
||||||
|
organizationId: ID!
|
||||||
|
name: String!
|
||||||
|
description: String!
|
||||||
|
}
|
||||||
|
|
||||||
|
input UpdateRiskInput {
|
||||||
|
id: ID!
|
||||||
|
name: String
|
||||||
|
description: String
|
||||||
|
}
|
||||||
|
|
||||||
|
input DeleteRiskInput {
|
||||||
|
riskId: ID!
|
||||||
|
}
|
||||||
|
|
||||||
input UploadEvidenceInput {
|
input UploadEvidenceInput {
|
||||||
taskId: ID!
|
taskId: ID!
|
||||||
name: String!
|
name: String!
|
||||||
@@ -939,6 +1002,18 @@ type UnassignTaskPayload {
|
|||||||
task: Task!
|
task: Task!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateRiskPayload {
|
||||||
|
riskEdge: RiskEdge!
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateRiskPayload {
|
||||||
|
risk: Risk!
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteRiskPayload {
|
||||||
|
deletedRiskId: ID!
|
||||||
|
}
|
||||||
|
|
||||||
type UploadEvidencePayload {
|
type UploadEvidencePayload {
|
||||||
evidenceEdge: EvidenceEdge!
|
evidenceEdge: EvidenceEdge!
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
54
pkg/server/api/console/v1/types/risk.go
Normal file
54
pkg/server/api/console/v1/types/risk.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/getprobo/probo/pkg/coredata"
|
||||||
|
"github.com/getprobo/probo/pkg/page"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
RiskOrderBy OrderBy[coredata.RiskOrderField]
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRiskConnection(p *page.Page[*coredata.Risk, coredata.RiskOrderField]) *RiskConnection {
|
||||||
|
var edges = make([]*RiskEdge, len(p.Data))
|
||||||
|
|
||||||
|
for i := range edges {
|
||||||
|
edges[i] = NewRiskEdge(p.Data[i], p.Cursor.OrderBy.Field)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &RiskConnection{
|
||||||
|
Edges: edges,
|
||||||
|
PageInfo: NewPageInfo(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRiskEdge(r *coredata.Risk, orderBy coredata.RiskOrderField) *RiskEdge {
|
||||||
|
return &RiskEdge{
|
||||||
|
Cursor: r.CursorKey(orderBy),
|
||||||
|
Node: NewRisk(r),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRisk(r *coredata.Risk) *Risk {
|
||||||
|
return &Risk{
|
||||||
|
ID: r.ID,
|
||||||
|
Name: r.Name,
|
||||||
|
Description: r.Description,
|
||||||
|
CreatedAt: r.CreatedAt,
|
||||||
|
UpdatedAt: r.UpdatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,6 +113,16 @@ type CreatePolicyPayload struct {
|
|||||||
PolicyEdge *PolicyEdge `json:"policyEdge"`
|
PolicyEdge *PolicyEdge `json:"policyEdge"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateRiskInput struct {
|
||||||
|
OrganizationID gid.GID `json:"organizationId"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateRiskPayload struct {
|
||||||
|
RiskEdge *RiskEdge `json:"riskEdge"`
|
||||||
|
}
|
||||||
|
|
||||||
type CreateTaskInput struct {
|
type CreateTaskInput struct {
|
||||||
MitigationID gid.GID `json:"mitigationId"`
|
MitigationID gid.GID `json:"mitigationId"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -182,6 +192,14 @@ type DeletePolicyPayload struct {
|
|||||||
DeletedPolicyID gid.GID `json:"deletedPolicyId"`
|
DeletedPolicyID gid.GID `json:"deletedPolicyId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeleteRiskInput struct {
|
||||||
|
RiskID gid.GID `json:"riskId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteRiskPayload struct {
|
||||||
|
DeletedRiskID gid.GID `json:"deletedRiskId"`
|
||||||
|
}
|
||||||
|
|
||||||
type DeleteTaskInput struct {
|
type DeleteTaskInput struct {
|
||||||
TaskID gid.GID `json:"taskId"`
|
TaskID gid.GID `json:"taskId"`
|
||||||
}
|
}
|
||||||
@@ -313,6 +331,7 @@ type Organization struct {
|
|||||||
Peoples *PeopleConnection `json:"peoples"`
|
Peoples *PeopleConnection `json:"peoples"`
|
||||||
Policies *PolicyConnection `json:"policies"`
|
Policies *PolicyConnection `json:"policies"`
|
||||||
Mitigations *MitigationConnection `json:"mitigations"`
|
Mitigations *MitigationConnection `json:"mitigations"`
|
||||||
|
Risks *RiskConnection `json:"risks"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
}
|
}
|
||||||
@@ -401,6 +420,27 @@ type RemoveUserPayload struct {
|
|||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Risk struct {
|
||||||
|
ID gid.GID `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Risk) IsNode() {}
|
||||||
|
func (this Risk) GetID() gid.GID { return this.ID }
|
||||||
|
|
||||||
|
type RiskConnection struct {
|
||||||
|
Edges []*RiskEdge `json:"edges"`
|
||||||
|
PageInfo *PageInfo `json:"pageInfo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RiskEdge struct {
|
||||||
|
Cursor page.CursorKey `json:"cursor"`
|
||||||
|
Node *Risk `json:"node"`
|
||||||
|
}
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
ID gid.GID `json:"id"`
|
ID gid.GID `json:"id"`
|
||||||
ExpiresAt time.Time `json:"expiresAt"`
|
ExpiresAt time.Time `json:"expiresAt"`
|
||||||
@@ -497,6 +537,16 @@ type UpdatePolicyPayload struct {
|
|||||||
Policy *Policy `json:"policy"`
|
Policy *Policy `json:"policy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateRiskInput struct {
|
||||||
|
ID gid.GID `json:"id"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateRiskPayload struct {
|
||||||
|
Risk *Risk `json:"risk"`
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateTaskInput struct {
|
type UpdateTaskInput struct {
|
||||||
TaskID gid.GID `json:"taskId"`
|
TaskID gid.GID `json:"taskId"`
|
||||||
Name *string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
|
|||||||
@@ -457,7 +457,7 @@ func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTas
|
|||||||
TimeEstimate: input.TimeEstimate,
|
TimeEstimate: input.TimeEstimate,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot create task: %w", err)
|
panic(fmt.Errorf("cannot create task: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.CreateTaskPayload{
|
return &types.CreateTaskPayload{
|
||||||
@@ -477,7 +477,7 @@ func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTas
|
|||||||
TimeEstimate: input.TimeEstimate,
|
TimeEstimate: input.TimeEstimate,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot update task: %w", err)
|
panic(fmt.Errorf("cannot update task: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.UpdateTaskPayload{
|
return &types.UpdateTaskPayload{
|
||||||
@@ -491,7 +491,7 @@ func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTas
|
|||||||
|
|
||||||
err := svc.Tasks.Delete(ctx, input.TaskID)
|
err := svc.Tasks.Delete(ctx, input.TaskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot delete task: %w", err)
|
panic(fmt.Errorf("cannot delete task: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.DeleteTaskPayload{
|
return &types.DeleteTaskPayload{
|
||||||
@@ -505,7 +505,7 @@ func (r *mutationResolver) AssignTask(ctx context.Context, input types.AssignTas
|
|||||||
|
|
||||||
task, err := svc.Tasks.Assign(ctx, input.TaskID, input.AssignedToID)
|
task, err := svc.Tasks.Assign(ctx, input.TaskID, input.AssignedToID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot assign task: %w", err)
|
panic(fmt.Errorf("cannot assign task: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.AssignTaskPayload{
|
return &types.AssignTaskPayload{
|
||||||
@@ -519,7 +519,7 @@ func (r *mutationResolver) UnassignTask(ctx context.Context, input types.Unassig
|
|||||||
|
|
||||||
task, err := svc.Tasks.Unassign(ctx, input.TaskID)
|
task, err := svc.Tasks.Unassign(ctx, input.TaskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot unassign task: %w", err)
|
panic(fmt.Errorf("cannot unassign task: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.UnassignTaskPayload{
|
return &types.UnassignTaskPayload{
|
||||||
@@ -527,6 +527,62 @@ func (r *mutationResolver) UnassignTask(ctx context.Context, input types.Unassig
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateRisk is the resolver for the createRisk field.
|
||||||
|
func (r *mutationResolver) CreateRisk(ctx context.Context, input types.CreateRiskInput) (*types.CreateRiskPayload, error) {
|
||||||
|
svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID())
|
||||||
|
|
||||||
|
risk, err := svc.Risks.Create(
|
||||||
|
ctx,
|
||||||
|
probo.CreateRiskRequest{
|
||||||
|
OrganizationID: input.OrganizationID,
|
||||||
|
Name: input.Name,
|
||||||
|
Description: input.Description,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("cannot create risk: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.CreateRiskPayload{
|
||||||
|
RiskEdge: types.NewRiskEdge(risk, coredata.RiskOrderFieldCreatedAt),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRisk is the resolver for the updateRisk field.
|
||||||
|
func (r *mutationResolver) UpdateRisk(ctx context.Context, input types.UpdateRiskInput) (*types.UpdateRiskPayload, error) {
|
||||||
|
svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID())
|
||||||
|
|
||||||
|
risk, err := svc.Risks.Update(
|
||||||
|
ctx,
|
||||||
|
probo.UpdateRiskRequest{
|
||||||
|
ID: input.ID,
|
||||||
|
Name: input.Name,
|
||||||
|
Description: input.Description,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("cannot update risk: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.UpdateRiskPayload{
|
||||||
|
Risk: types.NewRisk(risk),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRisk is the resolver for the deleteRisk field.
|
||||||
|
func (r *mutationResolver) DeleteRisk(ctx context.Context, input types.DeleteRiskInput) (*types.DeleteRiskPayload, error) {
|
||||||
|
svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID())
|
||||||
|
|
||||||
|
err := svc.Risks.Delete(ctx, input.RiskID)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("cannot delete risk: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.DeleteRiskPayload{
|
||||||
|
DeletedRiskID: input.RiskID,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// UploadEvidence is the resolver for the uploadEvidence field.
|
// UploadEvidence is the resolver for the uploadEvidence field.
|
||||||
func (r *mutationResolver) UploadEvidence(ctx context.Context, input types.UploadEvidenceInput) (*types.UploadEvidencePayload, error) {
|
func (r *mutationResolver) UploadEvidence(ctx context.Context, input types.UploadEvidenceInput) (*types.UploadEvidencePayload, error) {
|
||||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID())
|
svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID())
|
||||||
@@ -790,6 +846,31 @@ func (r *organizationResolver) Mitigations(ctx context.Context, obj *types.Organ
|
|||||||
return types.NewMitigationConnection(page), nil
|
return types.NewMitigationConnection(page), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Risks is the resolver for the risks field.
|
||||||
|
func (r *organizationResolver) Risks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.RiskOrderBy) (*types.RiskConnection, error) {
|
||||||
|
svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID())
|
||||||
|
|
||||||
|
pageOrderBy := page.OrderBy[coredata.RiskOrderField]{
|
||||||
|
Field: coredata.RiskOrderFieldCreatedAt,
|
||||||
|
Direction: page.OrderDirectionDesc,
|
||||||
|
}
|
||||||
|
if orderBy != nil {
|
||||||
|
pageOrderBy = page.OrderBy[coredata.RiskOrderField]{
|
||||||
|
Field: orderBy.Field,
|
||||||
|
Direction: orderBy.Direction,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||||
|
|
||||||
|
page, err := svc.Risks.ListForOrganizationID(ctx, obj.ID, cursor)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("cannot list organization risks: %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.NewRiskConnection(page), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Owner is the resolver for the owner field.
|
// Owner is the resolver for the owner field.
|
||||||
func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.People, error) {
|
func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.People, error) {
|
||||||
svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID())
|
svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID())
|
||||||
@@ -875,6 +956,12 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
return types.NewControl(control), nil
|
return types.NewControl(control), nil
|
||||||
|
case coredata.RiskEntityType:
|
||||||
|
risk, err := svc.Risks.Get(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return types.NewRisk(risk), nil
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user