@@ -1,9 +1,10 @@
|
||||
import { Suspense, useEffect, useState } from "react";
|
||||
import { Suspense, useEffect, useState, useRef } from "react";
|
||||
import {
|
||||
graphql,
|
||||
PreloadedQuery,
|
||||
usePreloadedQuery,
|
||||
useQueryLoader,
|
||||
useMutation,
|
||||
} from "react-relay";
|
||||
import { useParams, useNavigate, Link } from "react-router";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
@@ -20,16 +21,17 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { MitigationListViewQuery as MitigationListViewQueryType } from "./__generated__/MitigationListViewQuery.graphql";
|
||||
import { MitigationListViewSkeleton } from "./MitigationListPage";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { MitigationListViewImportMitigationMutation as MitigationListViewImportMitigationMutationType } from "./__generated__/MitigationListViewImportMitigationMutation.graphql";
|
||||
|
||||
const mitigationListViewQuery = graphql`
|
||||
query MitigationListViewQuery($organizationId: ID!, $first: Int) {
|
||||
organization: node(id: $organizationId) {
|
||||
id
|
||||
... on Organization {
|
||||
mitigations(
|
||||
first: $first
|
||||
orderBy: { direction: ASC, field: CREATED_AT }
|
||||
) @connection(key: "MitigationListView_mitigations") {
|
||||
mitigations(first: $first)
|
||||
@connection(key: "MitigationListView_mitigations") {
|
||||
__id
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
@@ -48,6 +50,28 @@ const mitigationListViewQuery = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
const importMitigationMutation = graphql`
|
||||
mutation MitigationListViewImportMitigationMutation(
|
||||
$input: ImportMitigationInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
importMitigation(input: $input) {
|
||||
mitigationEdges @appendEdge(connections: $connections) {
|
||||
node {
|
||||
id
|
||||
name
|
||||
description
|
||||
category
|
||||
state
|
||||
importance
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Mitigation {
|
||||
id?: string;
|
||||
name?: string;
|
||||
@@ -93,6 +117,14 @@ function MitigationListContent({
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { organizationId } = useParams();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [isImporting, setIsImporting] = useState(false);
|
||||
const { toast } = useToast();
|
||||
|
||||
const [importMitigation] =
|
||||
useMutation<MitigationListViewImportMitigationMutationType>(
|
||||
importMitigationMutation
|
||||
);
|
||||
|
||||
// Monitor URL hash for changes and update state accordingly
|
||||
const [hashValue, setHashValue] = useState(window.location.hash);
|
||||
@@ -249,18 +281,75 @@ function MitigationListContent({
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
setIsImporting(true);
|
||||
|
||||
importMitigation({
|
||||
variables: {
|
||||
connections: [data.organization.mitigations.__id],
|
||||
input: {
|
||||
organizationId: organizationId!,
|
||||
file: null,
|
||||
},
|
||||
},
|
||||
uploadables: {
|
||||
"input.file": file,
|
||||
},
|
||||
onCompleted: () => {
|
||||
setIsImporting(false);
|
||||
toast({
|
||||
title: "Mitigations imported",
|
||||
description: "Mitigations have been imported successfully.",
|
||||
variant: "default",
|
||||
});
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = "";
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
setIsImporting(false);
|
||||
toast({
|
||||
title: "Error importing mitigations",
|
||||
description: error.message,
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<PageTemplate
|
||||
title="Mitigations"
|
||||
description="Mitigations are actions taken to reduce the risk. Add them to track their implementation status."
|
||||
actions={
|
||||
<Button asChild>
|
||||
<Link to={`/organizations/${organizationId}/mitigations/new`}>
|
||||
New Mitigation
|
||||
</Link>
|
||||
</Button>
|
||||
<div className="flex gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={isImporting}
|
||||
>
|
||||
{isImporting ? "Importing..." : "Import Mitigations"}
|
||||
</Button>
|
||||
<Button asChild>
|
||||
<Link to={`/organizations/${organizationId}/mitigations/new`}>
|
||||
New Mitigation
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{/* Hidden file input for mitigation import */}
|
||||
<input
|
||||
type="file"
|
||||
ref={fileInputRef}
|
||||
onChange={handleFileChange}
|
||||
style={{ display: "none" }}
|
||||
accept=".json"
|
||||
/>
|
||||
|
||||
{/* Global Progress Summary */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* @generated SignedSource<<d4590a13a15cf248104340854bc46aa7>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type MitigationImportance = "ADVANCED" | "MANDATORY" | "PREFERRED";
|
||||
export type MitigationState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
|
||||
export type ImportMitigationInput = {
|
||||
file: any;
|
||||
organizationId: string;
|
||||
};
|
||||
export type MitigationListViewImportMitigationMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: ImportMitigationInput;
|
||||
};
|
||||
export type MitigationListViewImportMitigationMutation$data = {
|
||||
readonly importMitigation: {
|
||||
readonly mitigationEdges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly category: string;
|
||||
readonly createdAt: string;
|
||||
readonly description: string;
|
||||
readonly id: string;
|
||||
readonly importance: MitigationImportance;
|
||||
readonly name: string;
|
||||
readonly state: MitigationState;
|
||||
readonly updatedAt: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
};
|
||||
export type MitigationListViewImportMitigationMutation = {
|
||||
response: MitigationListViewImportMitigationMutation$data;
|
||||
variables: MitigationListViewImportMitigationMutation$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": "MitigationEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "mitigationEdges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Mitigation",
|
||||
"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": "category",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "importance",
|
||||
"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": "MitigationListViewImportMitigationMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "ImportMitigationPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "importMitigation",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "MitigationListViewImportMitigationMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "ImportMitigationPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "importMitigation",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "appendEdge",
|
||||
"key": "",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "mitigationEdges",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "84c038c572378999bfaacb87e8172b1e",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "MitigationListViewImportMitigationMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation MitigationListViewImportMitigationMutation(\n $input: ImportMitigationInput!\n) {\n importMitigation(input: $input) {\n mitigationEdges {\n node {\n id\n name\n description\n category\n state\n importance\n createdAt\n updatedAt\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "59e3d4a8e71cee0dcee606b93d706dad";
|
||||
|
||||
export default node;
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<c919ae742bdab52e5e68fb5c7fe92812>>
|
||||
* @generated SignedSource<<a98cd84786da5d6229cb66bc24ad92d0>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -19,6 +19,7 @@ export type MitigationListViewQuery$data = {
|
||||
readonly organization: {
|
||||
readonly id: string;
|
||||
readonly mitigations?: {
|
||||
readonly __id: string;
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly category: string;
|
||||
@@ -65,21 +66,13 @@ v3 = {
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
"kind": "Literal",
|
||||
"name": "orderBy",
|
||||
"value": {
|
||||
"direction": "ASC",
|
||||
"field": "CREATED_AT"
|
||||
}
|
||||
},
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
v6 = [
|
||||
v5 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -146,7 +139,7 @@ v6 = [
|
||||
"name": "updatedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
(v5/*: any*/)
|
||||
(v4/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
@@ -184,15 +177,26 @@ v6 = [
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "ClientExtension",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__id",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
v7 = [
|
||||
v6 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "first",
|
||||
"variableName": "first"
|
||||
},
|
||||
(v4/*: any*/)
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
@@ -218,15 +222,13 @@ return {
|
||||
"selections": [
|
||||
{
|
||||
"alias": "mitigations",
|
||||
"args": [
|
||||
(v4/*: any*/)
|
||||
],
|
||||
"args": null,
|
||||
"concreteType": "MitigationConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "__MitigationListView_mitigations_connection",
|
||||
"plural": false,
|
||||
"selections": (v6/*: any*/),
|
||||
"storageKey": "__MitigationListView_mitigations_connection(orderBy:{\"direction\":\"ASC\",\"field\":\"CREATED_AT\"})"
|
||||
"selections": (v5/*: any*/),
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
@@ -256,27 +258,25 @@ return {
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v5/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v7/*: any*/),
|
||||
"args": (v6/*: any*/),
|
||||
"concreteType": "MitigationConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "mitigations",
|
||||
"plural": false,
|
||||
"selections": (v6/*: any*/),
|
||||
"selections": (v5/*: any*/),
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v7/*: any*/),
|
||||
"filters": [
|
||||
"orderBy"
|
||||
],
|
||||
"args": (v6/*: any*/),
|
||||
"filters": null,
|
||||
"handle": "connection",
|
||||
"key": "MitigationListView_mitigations",
|
||||
"kind": "LinkedHandle",
|
||||
@@ -292,7 +292,7 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "cb49e5e7740ec71ce0b8e1abbdbcf24d",
|
||||
"cacheID": "9951f5e4be50df7a883af63426fe5fde",
|
||||
"id": null,
|
||||
"metadata": {
|
||||
"connection": [
|
||||
@@ -309,11 +309,11 @@ return {
|
||||
},
|
||||
"name": "MitigationListViewQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query MitigationListViewQuery(\n $organizationId: ID!\n $first: Int\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n mitigations(first: $first, orderBy: {direction: ASC, field: CREATED_AT}) {\n edges {\n node {\n id\n name\n description\n category\n state\n importance\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
|
||||
"text": "query MitigationListViewQuery(\n $organizationId: ID!\n $first: Int\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n mitigations(first: $first) {\n edges {\n node {\n id\n name\n description\n category\n state\n importance\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "ee7fdf984d5cb376068ba53d0a56c59b";
|
||||
(node as any).hash = "c2da6b42a986dd18cd18603c5f5809ad";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -46,6 +46,14 @@ type (
|
||||
State *coredata.MitigationState
|
||||
Importance *coredata.MitigationImportance
|
||||
}
|
||||
|
||||
ImportMitigationRequest struct {
|
||||
Mitigations []struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Category string `json:"category"`
|
||||
} `json:"mitigations"`
|
||||
}
|
||||
)
|
||||
|
||||
func (s MitigationService) Get(
|
||||
@@ -68,6 +76,65 @@ func (s MitigationService) Get(
|
||||
return mitigation, nil
|
||||
}
|
||||
|
||||
func (s MitigationService) Import(
|
||||
ctx context.Context,
|
||||
organizationID gid.GID,
|
||||
req ImportMitigationRequest,
|
||||
) (*page.Page[*coredata.Mitigation, coredata.MitigationOrderField], error) {
|
||||
|
||||
importedMitigations := coredata.Mitigations{}
|
||||
for _, mitigation := range req.Mitigations {
|
||||
now := time.Now()
|
||||
|
||||
mitigationID, err := gid.NewGID(organizationID.TenantID(), coredata.MitigationEntityType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create global id: %w", err)
|
||||
}
|
||||
|
||||
importedMitigations = append(importedMitigations, &coredata.Mitigation{
|
||||
ID: mitigationID,
|
||||
OrganizationID: organizationID,
|
||||
Name: mitigation.Name,
|
||||
Description: mitigation.Description,
|
||||
Category: mitigation.Category,
|
||||
State: coredata.MitigationStateNotStarted,
|
||||
Standards: []string{},
|
||||
Importance: coredata.MitigationImportancePreferred,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
})
|
||||
}
|
||||
|
||||
err := s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(tx pg.Conn) error {
|
||||
for _, mitigation := range importedMitigations {
|
||||
if err := mitigation.Insert(ctx, tx, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot insert mitigation: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot import mitigations: %w", err)
|
||||
}
|
||||
|
||||
cursor := page.NewCursor(
|
||||
len(importedMitigations),
|
||||
nil,
|
||||
page.Head,
|
||||
page.OrderBy[coredata.MitigationOrderField]{
|
||||
Field: coredata.MitigationOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionAsc,
|
||||
},
|
||||
)
|
||||
|
||||
return page.NewPage(importedMitigations, cursor), nil
|
||||
}
|
||||
|
||||
func (s MitigationService) Update(
|
||||
ctx context.Context,
|
||||
req UpdateMitigationRequest,
|
||||
|
||||
@@ -916,7 +916,7 @@ type UpdateMitigationPayload {
|
||||
}
|
||||
|
||||
type ImportMitigationPayload {
|
||||
success: Boolean!
|
||||
mitigationEdges: [MitigationEdge!]!
|
||||
}
|
||||
|
||||
type CreateTaskPayload {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -256,6 +256,15 @@ type ImportFrameworkPayload struct {
|
||||
FrameworkEdge *FrameworkEdge `json:"frameworkEdge"`
|
||||
}
|
||||
|
||||
type ImportMitigationInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
File graphql.Upload `json:"file"`
|
||||
}
|
||||
|
||||
type ImportMitigationPayload struct {
|
||||
MitigationEdges []*MitigationEdge `json:"mitigationEdges"`
|
||||
}
|
||||
|
||||
type InviteUserInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
Email string `json:"email"`
|
||||
|
||||
@@ -86,6 +86,170 @@ func (r *mitigationResolver) Tasks(ctx context.Context, obj *types.Mitigation, f
|
||||
return types.NewTaskConnection(page), nil
|
||||
}
|
||||
|
||||
// CreateOrganization is the resolver for the createOrganization field.
|
||||
func (r *mutationResolver) CreateOrganization(ctx context.Context, input types.CreateOrganizationInput) (*types.CreateOrganizationPayload, error) {
|
||||
svc := r.proboSvc.WithTenant(gid.NewTenantID())
|
||||
|
||||
organization, err := svc.Organizations.Create(ctx, probo.CreateOrganizationRequest{
|
||||
Name: input.Name,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create organization: %w", err)
|
||||
}
|
||||
|
||||
err = r.usrmgrSvc.EnrollUserInOrganization(ctx, UserFromContext(ctx).ID, organization.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot add user to organization: %w", err)
|
||||
}
|
||||
|
||||
tenantIDs, _ := ctx.Value(userTenantContextKey).(*[]gid.TenantID)
|
||||
*tenantIDs = append(*tenantIDs, organization.ID.TenantID())
|
||||
|
||||
return &types.CreateOrganizationPayload{
|
||||
OrganizationEdge: types.NewOrganizationEdge(organization, coredata.OrganizationOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateOrganization is the resolver for the updateOrganization field.
|
||||
func (r *mutationResolver) UpdateOrganization(ctx context.Context, input types.UpdateOrganizationInput) (*types.UpdateOrganizationPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID())
|
||||
|
||||
req := probo.UpdateOrganizationRequest{
|
||||
ID: input.OrganizationID,
|
||||
Name: input.Name,
|
||||
}
|
||||
|
||||
if input.Logo != nil {
|
||||
req.File = input.Logo.File
|
||||
}
|
||||
|
||||
organization, err := svc.Organizations.Update(ctx, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot update organization: %w", err)
|
||||
}
|
||||
|
||||
return &types.UpdateOrganizationPayload{
|
||||
Organization: types.NewOrganization(organization),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteOrganization is the resolver for the deleteOrganization field.
|
||||
func (r *mutationResolver) DeleteOrganization(ctx context.Context, input types.DeleteOrganizationInput) (*types.DeleteOrganizationPayload, error) {
|
||||
panic(fmt.Errorf("not implemented: DeleteOrganization - deleteOrganization"))
|
||||
}
|
||||
|
||||
// ConfirmEmail is the resolver for the confirmEmail field.
|
||||
func (r *mutationResolver) ConfirmEmail(ctx context.Context, input types.ConfirmEmailInput) (*types.ConfirmEmailPayload, error) {
|
||||
err := r.usrmgrSvc.ConfirmEmail(ctx, input.Token)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.ConfirmEmailPayload{Success: true}, nil
|
||||
}
|
||||
|
||||
// InviteUser is the resolver for the inviteUser field.
|
||||
func (r *mutationResolver) InviteUser(ctx context.Context, input types.InviteUserInput) (*types.InviteUserPayload, error) {
|
||||
user := UserFromContext(ctx)
|
||||
|
||||
organizations, err := r.usrmgrSvc.ListOrganizationsForUserID(ctx, user.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to list organizations for user: %w", err))
|
||||
}
|
||||
|
||||
for _, organization := range organizations {
|
||||
if organization.ID == input.OrganizationID {
|
||||
err := r.usrmgrSvc.InviteUser(ctx, input.OrganizationID, input.FullName, input.Email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.InviteUserPayload{Success: true}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("organization not found")
|
||||
}
|
||||
|
||||
// RemoveUser is the resolver for the removeUser field.
|
||||
func (r *mutationResolver) RemoveUser(ctx context.Context, input types.RemoveUserInput) (*types.RemoveUserPayload, error) {
|
||||
user := UserFromContext(ctx)
|
||||
|
||||
organizations, err := r.usrmgrSvc.ListOrganizationsForUserID(ctx, user.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to list organizations for user: %w", err))
|
||||
}
|
||||
|
||||
for _, organization := range organizations {
|
||||
if organization.ID == input.OrganizationID {
|
||||
err := r.usrmgrSvc.RemoveUser(ctx, input.OrganizationID, input.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.RemoveUserPayload{Success: true}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("organization not found")
|
||||
}
|
||||
|
||||
// CreatePeople is the resolver for the createPeople field.
|
||||
func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.CreatePeoplePayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID())
|
||||
|
||||
people, err := svc.Peoples.Create(ctx, probo.CreatePeopleRequest{
|
||||
OrganizationID: input.OrganizationID,
|
||||
FullName: input.FullName,
|
||||
PrimaryEmailAddress: input.PrimaryEmailAddress,
|
||||
AdditionalEmailAddresses: []string{},
|
||||
Kind: input.Kind,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create people: %w", err)
|
||||
}
|
||||
|
||||
return &types.CreatePeoplePayload{
|
||||
PeopleEdge: types.NewPeopleEdge(people, coredata.PeopleOrderFieldFullName),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdatePeople is the resolver for the updatePeople field.
|
||||
func (r *mutationResolver) UpdatePeople(ctx context.Context, input types.UpdatePeopleInput) (*types.UpdatePeoplePayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID())
|
||||
|
||||
people, err := svc.Peoples.Update(ctx, probo.UpdatePeopleRequest{
|
||||
ID: input.ID,
|
||||
FullName: input.FullName,
|
||||
PrimaryEmailAddress: input.PrimaryEmailAddress,
|
||||
AdditionalEmailAddresses: &input.AdditionalEmailAddresses,
|
||||
Kind: input.Kind,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot update people: %w", err)
|
||||
}
|
||||
|
||||
return &types.UpdatePeoplePayload{
|
||||
People: types.NewPeople(people),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeletePeople is the resolver for the deletePeople field.
|
||||
func (r *mutationResolver) DeletePeople(ctx context.Context, input types.DeletePeopleInput) (*types.DeletePeoplePayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.PeopleID.TenantID())
|
||||
|
||||
err := svc.Peoples.Delete(ctx, input.PeopleID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot delete people: %w", err)
|
||||
}
|
||||
|
||||
return &types.DeletePeoplePayload{
|
||||
DeletedPeopleID: input.PeopleID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateVendor is the resolver for the createVendor field.
|
||||
func (r *mutationResolver) CreateVendor(ctx context.Context, input types.CreateVendorInput) (*types.CreateVendorPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID())
|
||||
@@ -149,194 +313,6 @@ func (r *mutationResolver) DeleteVendor(ctx context.Context, input types.DeleteV
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreatePeople is the resolver for the createPeople field.
|
||||
func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.CreatePeoplePayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID())
|
||||
|
||||
people, err := svc.Peoples.Create(ctx, probo.CreatePeopleRequest{
|
||||
OrganizationID: input.OrganizationID,
|
||||
FullName: input.FullName,
|
||||
PrimaryEmailAddress: input.PrimaryEmailAddress,
|
||||
AdditionalEmailAddresses: []string{},
|
||||
Kind: input.Kind,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create people: %w", err)
|
||||
}
|
||||
|
||||
return &types.CreatePeoplePayload{
|
||||
PeopleEdge: types.NewPeopleEdge(people, coredata.PeopleOrderFieldFullName),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdatePeople is the resolver for the updatePeople field.
|
||||
func (r *mutationResolver) UpdatePeople(ctx context.Context, input types.UpdatePeopleInput) (*types.UpdatePeoplePayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID())
|
||||
|
||||
people, err := svc.Peoples.Update(ctx, probo.UpdatePeopleRequest{
|
||||
ID: input.ID,
|
||||
FullName: input.FullName,
|
||||
PrimaryEmailAddress: input.PrimaryEmailAddress,
|
||||
AdditionalEmailAddresses: &input.AdditionalEmailAddresses,
|
||||
Kind: input.Kind,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot update people: %w", err)
|
||||
}
|
||||
|
||||
return &types.UpdatePeoplePayload{
|
||||
People: types.NewPeople(people),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeletePeople is the resolver for the deletePeople field.
|
||||
func (r *mutationResolver) DeletePeople(ctx context.Context, input types.DeletePeopleInput) (*types.DeletePeoplePayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.PeopleID.TenantID())
|
||||
|
||||
err := svc.Peoples.Delete(ctx, input.PeopleID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot delete people: %w", err)
|
||||
}
|
||||
|
||||
return &types.DeletePeoplePayload{
|
||||
DeletedPeopleID: input.PeopleID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateOrganization is the resolver for the createOrganization field.
|
||||
func (r *mutationResolver) CreateOrganization(ctx context.Context, input types.CreateOrganizationInput) (*types.CreateOrganizationPayload, error) {
|
||||
svc := r.proboSvc.WithTenant(gid.NewTenantID())
|
||||
|
||||
organization, err := svc.Organizations.Create(ctx, probo.CreateOrganizationRequest{
|
||||
Name: input.Name,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create organization: %w", err)
|
||||
}
|
||||
|
||||
err = r.usrmgrSvc.EnrollUserInOrganization(ctx, UserFromContext(ctx).ID, organization.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot add user to organization: %w", err)
|
||||
}
|
||||
|
||||
tenantIDs, _ := ctx.Value(userTenantContextKey).(*[]gid.TenantID)
|
||||
*tenantIDs = append(*tenantIDs, organization.ID.TenantID())
|
||||
|
||||
return &types.CreateOrganizationPayload{
|
||||
OrganizationEdge: types.NewOrganizationEdge(organization, coredata.OrganizationOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateOrganization is the resolver for the updateOrganization field.
|
||||
func (r *mutationResolver) UpdateOrganization(ctx context.Context, input types.UpdateOrganizationInput) (*types.UpdateOrganizationPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID())
|
||||
|
||||
req := probo.UpdateOrganizationRequest{
|
||||
ID: input.OrganizationID,
|
||||
Name: input.Name,
|
||||
}
|
||||
|
||||
if input.Logo != nil {
|
||||
req.File = input.Logo.File
|
||||
}
|
||||
|
||||
organization, err := svc.Organizations.Update(ctx, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot update organization: %w", err)
|
||||
}
|
||||
|
||||
return &types.UpdateOrganizationPayload{
|
||||
Organization: types.NewOrganization(organization),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteOrganization is the resolver for the deleteOrganization field.
|
||||
func (r *mutationResolver) DeleteOrganization(ctx context.Context, input types.DeleteOrganizationInput) (*types.DeleteOrganizationPayload, error) {
|
||||
panic(fmt.Errorf("not implemented: DeleteOrganization - deleteOrganization"))
|
||||
}
|
||||
|
||||
// CreateTask is the resolver for the createTask field.
|
||||
func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTaskInput) (*types.CreateTaskPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.MitigationID.TenantID())
|
||||
|
||||
task, err := svc.Tasks.Create(ctx, probo.CreateTaskRequest{
|
||||
MitigationID: input.MitigationID,
|
||||
Name: input.Name,
|
||||
Description: input.Description,
|
||||
TimeEstimate: input.TimeEstimate,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create task: %w", err)
|
||||
}
|
||||
|
||||
return &types.CreateTaskPayload{
|
||||
TaskEdge: types.NewTaskEdge(task, coredata.TaskOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateTask is the resolver for the updateTask field.
|
||||
func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTaskInput) (*types.UpdateTaskPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID())
|
||||
|
||||
task, err := svc.Tasks.Update(ctx, probo.UpdateTaskRequest{
|
||||
TaskID: input.TaskID,
|
||||
Name: input.Name,
|
||||
Description: input.Description,
|
||||
State: input.State,
|
||||
TimeEstimate: input.TimeEstimate,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot update task: %w", err)
|
||||
}
|
||||
|
||||
return &types.UpdateTaskPayload{
|
||||
Task: types.NewTask(task),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteTask is the resolver for the deleteTask field.
|
||||
func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTaskInput) (*types.DeleteTaskPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID())
|
||||
|
||||
err := svc.Tasks.Delete(ctx, input.TaskID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot delete task: %w", err)
|
||||
}
|
||||
|
||||
return &types.DeleteTaskPayload{
|
||||
DeletedTaskID: input.TaskID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AssignTask is the resolver for the assignTask field.
|
||||
func (r *mutationResolver) AssignTask(ctx context.Context, input types.AssignTaskInput) (*types.AssignTaskPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID())
|
||||
|
||||
task, err := svc.Tasks.Assign(ctx, input.TaskID, input.AssignedToID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot assign task: %w", err)
|
||||
}
|
||||
|
||||
return &types.AssignTaskPayload{
|
||||
Task: types.NewTask(task),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UnassignTask is the resolver for the unassignTask field.
|
||||
func (r *mutationResolver) UnassignTask(ctx context.Context, input types.UnassignTaskInput) (*types.UnassignTaskPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID())
|
||||
|
||||
task, err := svc.Tasks.Unassign(ctx, input.TaskID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot unassign task: %w", err)
|
||||
}
|
||||
|
||||
return &types.UnassignTaskPayload{
|
||||
Task: types.NewTask(task),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateFramework is the resolver for the createFramework field.
|
||||
func (r *mutationResolver) CreateFramework(ctx context.Context, input types.CreateFrameworkInput) (*types.CreateFrameworkPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID())
|
||||
@@ -446,6 +422,111 @@ func (r *mutationResolver) UpdateMitigation(ctx context.Context, input types.Upd
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ImportMitigation is the resolver for the importMitigation field.
|
||||
func (r *mutationResolver) ImportMitigation(ctx context.Context, input types.ImportMitigationInput) (*types.ImportMitigationPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID())
|
||||
|
||||
var req probo.ImportMitigationRequest
|
||||
if err := json.NewDecoder(input.File.File).Decode(&req.Mitigations); err != nil {
|
||||
return nil, fmt.Errorf("cannot unmarshal mitigation: %w", err)
|
||||
}
|
||||
|
||||
mitigations, err := svc.Mitigations.Import(ctx, input.OrganizationID, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot import mitigation: %w", err)
|
||||
}
|
||||
|
||||
mitigationEdges := make([]*types.MitigationEdge, len(mitigations.Data))
|
||||
for i, mitigation := range mitigations.Data {
|
||||
mitigationEdges[i] = types.NewMitigationEdge(mitigation, coredata.MitigationOrderFieldCreatedAt)
|
||||
}
|
||||
|
||||
return &types.ImportMitigationPayload{
|
||||
MitigationEdges: mitigationEdges,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateTask is the resolver for the createTask field.
|
||||
func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTaskInput) (*types.CreateTaskPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.MitigationID.TenantID())
|
||||
|
||||
task, err := svc.Tasks.Create(ctx, probo.CreateTaskRequest{
|
||||
MitigationID: input.MitigationID,
|
||||
Name: input.Name,
|
||||
Description: input.Description,
|
||||
TimeEstimate: input.TimeEstimate,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create task: %w", err)
|
||||
}
|
||||
|
||||
return &types.CreateTaskPayload{
|
||||
TaskEdge: types.NewTaskEdge(task, coredata.TaskOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UpdateTask is the resolver for the updateTask field.
|
||||
func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTaskInput) (*types.UpdateTaskPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID())
|
||||
|
||||
task, err := svc.Tasks.Update(ctx, probo.UpdateTaskRequest{
|
||||
TaskID: input.TaskID,
|
||||
Name: input.Name,
|
||||
Description: input.Description,
|
||||
State: input.State,
|
||||
TimeEstimate: input.TimeEstimate,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot update task: %w", err)
|
||||
}
|
||||
|
||||
return &types.UpdateTaskPayload{
|
||||
Task: types.NewTask(task),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteTask is the resolver for the deleteTask field.
|
||||
func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTaskInput) (*types.DeleteTaskPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID())
|
||||
|
||||
err := svc.Tasks.Delete(ctx, input.TaskID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot delete task: %w", err)
|
||||
}
|
||||
|
||||
return &types.DeleteTaskPayload{
|
||||
DeletedTaskID: input.TaskID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AssignTask is the resolver for the assignTask field.
|
||||
func (r *mutationResolver) AssignTask(ctx context.Context, input types.AssignTaskInput) (*types.AssignTaskPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID())
|
||||
|
||||
task, err := svc.Tasks.Assign(ctx, input.TaskID, input.AssignedToID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot assign task: %w", err)
|
||||
}
|
||||
|
||||
return &types.AssignTaskPayload{
|
||||
Task: types.NewTask(task),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UnassignTask is the resolver for the unassignTask field.
|
||||
func (r *mutationResolver) UnassignTask(ctx context.Context, input types.UnassignTaskInput) (*types.UnassignTaskPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID())
|
||||
|
||||
task, err := svc.Tasks.Unassign(ctx, input.TaskID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot unassign task: %w", err)
|
||||
}
|
||||
|
||||
return &types.UnassignTaskPayload{
|
||||
Task: types.NewTask(task),
|
||||
}, 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())
|
||||
@@ -554,63 +635,6 @@ func (r *mutationResolver) DeletePolicy(ctx context.Context, input types.DeleteP
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ConfirmEmail is the resolver for the confirmEmail field.
|
||||
func (r *mutationResolver) ConfirmEmail(ctx context.Context, input types.ConfirmEmailInput) (*types.ConfirmEmailPayload, error) {
|
||||
err := r.usrmgrSvc.ConfirmEmail(ctx, input.Token)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.ConfirmEmailPayload{Success: true}, nil
|
||||
}
|
||||
|
||||
// InviteUser is the resolver for the inviteUser field.
|
||||
func (r *mutationResolver) InviteUser(ctx context.Context, input types.InviteUserInput) (*types.InviteUserPayload, error) {
|
||||
user := UserFromContext(ctx)
|
||||
|
||||
organizations, err := r.usrmgrSvc.ListOrganizationsForUserID(ctx, user.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to list organizations for user: %w", err))
|
||||
}
|
||||
|
||||
for _, organization := range organizations {
|
||||
if organization.ID == input.OrganizationID {
|
||||
err := r.usrmgrSvc.InviteUser(ctx, input.OrganizationID, input.FullName, input.Email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.InviteUserPayload{Success: true}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("organization not found")
|
||||
}
|
||||
|
||||
// RemoveUser is the resolver for the removeUser field.
|
||||
func (r *mutationResolver) RemoveUser(ctx context.Context, input types.RemoveUserInput) (*types.RemoveUserPayload, error) {
|
||||
user := UserFromContext(ctx)
|
||||
|
||||
organizations, err := r.usrmgrSvc.ListOrganizationsForUserID(ctx, user.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to list organizations for user: %w", err))
|
||||
}
|
||||
|
||||
for _, organization := range organizations {
|
||||
if organization.ID == input.OrganizationID {
|
||||
err := r.usrmgrSvc.RemoveUser(ctx, input.OrganizationID, input.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.RemoveUserPayload{Success: true}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("organization not found")
|
||||
}
|
||||
|
||||
// LogoURL is the resolver for the logoUrl field.
|
||||
func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID())
|
||||
|
||||
Reference in New Issue
Block a user