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 { VendorPage } from "./vendors/VendorPage";
|
||||
import { MitigationNewPage } from "./mitigations/MitigationNewPage";
|
||||
import { RiskListPage } from "./risks/RiskListPage";
|
||||
import { NewRiskPage } from "./risks/NewRiskPage";
|
||||
|
||||
export function OrganizationsRoutes() {
|
||||
return (
|
||||
@@ -53,6 +55,8 @@ export function OrganizationsRoutes() {
|
||||
path="policies/:policyId/update"
|
||||
element={<UpdatePolicyPage />}
|
||||
/>
|
||||
<Route path="risks" element={<RiskListPage />} />
|
||||
<Route path="risks/new" element={<NewRiskPage />} />
|
||||
<Route path="settings" element={<SettingsPage />} />
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</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
|
||||
TaskEntityType
|
||||
EvidenceEntityType
|
||||
ControlStateTransitionEntityType
|
||||
TaskStateTransitionEntityType
|
||||
_ControlStateTransitionEntityType
|
||||
_TaskStateTransitionEntityType
|
||||
VendorEntityType
|
||||
PeopleEntityType
|
||||
EvidenceStateTransitionEntityType
|
||||
_EvidenceStateTransitionEntityType
|
||||
PolicyEntityType
|
||||
UserEntityType
|
||||
SessionEntityType
|
||||
EmailEntityType
|
||||
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
|
||||
Policies *PolicyService
|
||||
Controls *ControlService
|
||||
Risks *RiskService
|
||||
}
|
||||
)
|
||||
|
||||
@@ -86,6 +87,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
|
||||
tenantService.Policies = &PolicyService{svc: tenantService}
|
||||
tenantService.Organizations = &OrganizationService{svc: tenantService}
|
||||
tenantService.Controls = &ControlService{svc: tenantService}
|
||||
tenantService.Risks = &RiskService{svc: tenantService}
|
||||
|
||||
return tenantService
|
||||
}
|
||||
|
||||
@@ -215,6 +215,14 @@ enum PolicyOrderField
|
||||
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
|
||||
@goModel(model: "github.com/getprobo/probo/pkg/coredata.EvidenceOrderField") {
|
||||
CREATED_AT
|
||||
@@ -291,6 +299,14 @@ input PolicyOrder
|
||||
field: PolicyOrderField!
|
||||
}
|
||||
|
||||
input RiskOrder
|
||||
@goModel(
|
||||
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.RiskOrderBy"
|
||||
) {
|
||||
direction: OrderDirection!
|
||||
field: RiskOrderField!
|
||||
}
|
||||
|
||||
input EvidenceOrder
|
||||
@goModel(
|
||||
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.EvidenceOrderBy"
|
||||
@@ -358,6 +374,14 @@ type Organization implements Node {
|
||||
orderBy: MitigationOrder
|
||||
): MitigationConnection! @goField(forceResolver: true)
|
||||
|
||||
risks(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: RiskOrder
|
||||
): RiskConnection! @goField(forceResolver: true)
|
||||
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
@@ -487,6 +511,14 @@ type Policy implements Node {
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type Risk implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type Session {
|
||||
id: ID!
|
||||
expiresAt: Datetime!
|
||||
@@ -606,6 +638,16 @@ type PolicyEdge {
|
||||
node: Policy!
|
||||
}
|
||||
|
||||
type RiskConnection {
|
||||
edges: [RiskEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type RiskEdge {
|
||||
cursor: CursorKey!
|
||||
node: Risk!
|
||||
}
|
||||
|
||||
# Root Types
|
||||
type Query {
|
||||
node(id: ID!): Node!
|
||||
@@ -657,6 +699,11 @@ type Mutation {
|
||||
assignTask(input: AssignTaskInput!): AssignTaskPayload!
|
||||
unassignTask(input: UnassignTaskInput!): UnassignTaskPayload!
|
||||
|
||||
# Risk mutations
|
||||
createRisk(input: CreateRiskInput!): CreateRiskPayload!
|
||||
updateRisk(input: UpdateRiskInput!): UpdateRiskPayload!
|
||||
deleteRisk(input: DeleteRiskInput!): DeleteRiskPayload!
|
||||
|
||||
# Evidence mutations
|
||||
uploadEvidence(input: UploadEvidenceInput!): UploadEvidencePayload!
|
||||
deleteEvidence(input: DeleteEvidenceInput!): DeleteEvidencePayload!
|
||||
@@ -804,6 +851,22 @@ input UnassignTaskInput {
|
||||
taskId: ID!
|
||||
}
|
||||
|
||||
input CreateRiskInput {
|
||||
organizationId: ID!
|
||||
name: String!
|
||||
description: String!
|
||||
}
|
||||
|
||||
input UpdateRiskInput {
|
||||
id: ID!
|
||||
name: String
|
||||
description: String
|
||||
}
|
||||
|
||||
input DeleteRiskInput {
|
||||
riskId: ID!
|
||||
}
|
||||
|
||||
input UploadEvidenceInput {
|
||||
taskId: ID!
|
||||
name: String!
|
||||
@@ -939,6 +1002,18 @@ type UnassignTaskPayload {
|
||||
task: Task!
|
||||
}
|
||||
|
||||
type CreateRiskPayload {
|
||||
riskEdge: RiskEdge!
|
||||
}
|
||||
|
||||
type UpdateRiskPayload {
|
||||
risk: Risk!
|
||||
}
|
||||
|
||||
type DeleteRiskPayload {
|
||||
deletedRiskId: ID!
|
||||
}
|
||||
|
||||
type UploadEvidencePayload {
|
||||
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"`
|
||||
}
|
||||
|
||||
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 {
|
||||
MitigationID gid.GID `json:"mitigationId"`
|
||||
Name string `json:"name"`
|
||||
@@ -182,6 +192,14 @@ type DeletePolicyPayload struct {
|
||||
DeletedPolicyID gid.GID `json:"deletedPolicyId"`
|
||||
}
|
||||
|
||||
type DeleteRiskInput struct {
|
||||
RiskID gid.GID `json:"riskId"`
|
||||
}
|
||||
|
||||
type DeleteRiskPayload struct {
|
||||
DeletedRiskID gid.GID `json:"deletedRiskId"`
|
||||
}
|
||||
|
||||
type DeleteTaskInput struct {
|
||||
TaskID gid.GID `json:"taskId"`
|
||||
}
|
||||
@@ -313,6 +331,7 @@ type Organization struct {
|
||||
Peoples *PeopleConnection `json:"peoples"`
|
||||
Policies *PolicyConnection `json:"policies"`
|
||||
Mitigations *MitigationConnection `json:"mitigations"`
|
||||
Risks *RiskConnection `json:"risks"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
@@ -401,6 +420,27 @@ type RemoveUserPayload struct {
|
||||
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 {
|
||||
ID gid.GID `json:"id"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
@@ -497,6 +537,16 @@ type UpdatePolicyPayload struct {
|
||||
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 {
|
||||
TaskID gid.GID `json:"taskId"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
|
||||
@@ -457,7 +457,7 @@ func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTas
|
||||
TimeEstimate: input.TimeEstimate,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create task: %w", err)
|
||||
panic(fmt.Errorf("cannot create task: %w", err))
|
||||
}
|
||||
|
||||
return &types.CreateTaskPayload{
|
||||
@@ -477,7 +477,7 @@ func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTas
|
||||
TimeEstimate: input.TimeEstimate,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot update task: %w", err)
|
||||
panic(fmt.Errorf("cannot update task: %w", err))
|
||||
}
|
||||
|
||||
return &types.UpdateTaskPayload{
|
||||
@@ -491,7 +491,7 @@ func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTas
|
||||
|
||||
err := svc.Tasks.Delete(ctx, input.TaskID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot delete task: %w", err)
|
||||
panic(fmt.Errorf("cannot delete task: %w", err))
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot assign task: %w", err)
|
||||
panic(fmt.Errorf("cannot assign task: %w", err))
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot unassign task: %w", err)
|
||||
panic(fmt.Errorf("cannot unassign task: %w", err))
|
||||
}
|
||||
|
||||
return &types.UnassignTaskPayload{
|
||||
@@ -527,6 +527,62 @@ func (r *mutationResolver) UnassignTask(ctx context.Context, input types.Unassig
|
||||
}, 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.
|
||||
func (r *mutationResolver) UploadEvidence(ctx context.Context, input types.UploadEvidenceInput) (*types.UploadEvidencePayload, error) {
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.People, error) {
|
||||
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
|
||||
case coredata.RiskEntityType:
|
||||
risk, err := svc.Risks.Get(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return types.NewRisk(risk), nil
|
||||
default:
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user