diff --git a/apps/console/src/pages/organizations/vendors/VendorView.tsx b/apps/console/src/pages/organizations/vendors/VendorView.tsx
index 3b404f791..3524ec6d9 100644
--- a/apps/console/src/pages/organizations/vendors/VendorView.tsx
+++ b/apps/console/src/pages/organizations/vendors/VendorView.tsx
@@ -94,6 +94,13 @@ const vendorViewQuery = graphql`
organization: node(id: $organizationId) {
...PeopleSelector_organization
}
+ viewer {
+ user {
+ people(organizationId: $organizationId) {
+ id
+ }
+ }
+ }
}
`;
@@ -675,6 +682,13 @@ function RiskAssessmentsTable({
});
};
+ // Check if assessment is expired
+ const isExpired = (expiresAt: string) => {
+ console.log(new Date(expiresAt) < new Date());
+
+ return new Date(expiresAt) < new Date();
+ };
+
// Get severity label based on data sensitivity and business impact
const getSeverityLabel = (dataSensitivity: string, businessImpact: string) => {
// Simple logic to determine severity - can be adjusted based on requirements
@@ -731,35 +745,67 @@ function RiskAssessmentsTable({
{assessments.map((assessment) => {
const severity = getSeverityLabel(assessment.dataSensitivity, assessment.businessImpact);
+ const expired = isExpired(assessment.expiresAt);
return (
-
+
|
-
+
{formatDate(assessment.assessedAt)}
|
-
- {formatDate(assessment.expiresAt)}
-
+
+
+ {formatDate(assessment.expiresAt)}
+
+ {expired && (
+
+ Expired
+
+ )}
+
|
-
+
{assessment.dataSensitivity.charAt(0) + assessment.dataSensitivity.slice(1).toLowerCase()}
|
-
+
{assessment.businessImpact.charAt(0) + assessment.businessImpact.slice(1).toLowerCase()}
|
-
+
{severity.label}
|
-
+
{assessment.assessedBy?.fullName || 'N/A'}
|
@@ -770,9 +816,9 @@ function RiskAssessmentsTable({
onClick={() => setShowDropdown(showDropdown === assessment.id ? null : assessment.id)}
>
{showDropdown === assessment.id && (
@@ -1270,27 +1316,9 @@ function VendorViewContent({
businessImpact: BusinessImpact;
notes: string;
}) => {
- // Close the modal
- setShowRiskAssessmentModal(false);
-
- // Current date for assessedAt, and 1 year later for expiresAt
- const today = new Date();
- const nextYear = new Date(today);
+ const nextYear = new Date();
nextYear.setFullYear(nextYear.getFullYear() + 1);
- // Check if there's a business owner assigned
- const businessOwnerId = data.node.businessOwner?.id;
- if (!businessOwnerId) {
- // This check is now redundant as it's handled in the modal
- // but keeping it as a safeguard
- toast({
- title: "Error",
- description: "Please assign a Business Owner to the vendor before creating a risk assessment",
- variant: "destructive",
- });
- return;
- }
-
createRiskAssessment({
variables: {
connections: [
@@ -1301,12 +1329,11 @@ function VendorViewContent({
],
input: {
vendorId: data.node.id!,
- assessedBy: businessOwnerId,
+ assessedBy: data.viewer.user.people?.id!,
expiresAt: nextYear.toISOString(),
dataSensitivity: formValues.dataSensitivity,
businessImpact: formValues.businessImpact,
notes: formValues.notes || "Risk assessment",
- attachments: []
},
},
onCompleted: () => {
@@ -1315,6 +1342,7 @@ function VendorViewContent({
description: "Risk assessment created successfully",
variant: "default",
});
+ setShowRiskAssessmentModal(false);
loadQuery({
vendorId: data.node.id!,
organizationId: organizationId!,
@@ -1328,7 +1356,7 @@ function VendorViewContent({
});
},
});
- }, [createRiskAssessment, data.node.id, data.node.businessOwner?.id, loadQuery, toast, organizationId]);
+ }, [createRiskAssessment, data.node.id, data.viewer.user.people?.id, loadQuery, toast, organizationId]);
const formatDate = (dateStr: string | null | undefined) => {
if (!dateStr) return "N/A";
diff --git a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewCreateRiskAssessmentMutation.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewCreateRiskAssessmentMutation.graphql.ts
index 0dbc113ad..66d14c5b1 100644
--- a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewCreateRiskAssessmentMutation.graphql.ts
+++ b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewCreateRiskAssessmentMutation.graphql.ts
@@ -1,5 +1,5 @@
/**
- * @generated SignedSource<<8372d50934049d43afb678c894f40dbd>>
+ * @generated SignedSource<<08de2d8758474132b472d63f413b59a5>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -13,7 +13,6 @@ export type BusinessImpact = "CRITICAL" | "HIGH" | "LOW" | "MEDIUM";
export type DataSensitivity = "CRITICAL" | "HIGH" | "LOW" | "MEDIUM" | "NONE";
export type CreateVendorRiskAssessmentInput = {
assessedBy: string;
- attachments?: ReadonlyArray | null | undefined;
businessImpact: BusinessImpact;
dataSensitivity: DataSensitivity;
expiresAt: string;
diff --git a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts
index d9e08ccc1..089ebee3f 100644
--- a/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts
+++ b/apps/console/src/pages/organizations/vendors/__generated__/VendorViewQuery.graphql.ts
@@ -1,5 +1,5 @@
/**
- * @generated SignedSource<>
+ * @generated SignedSource<>
* @lightSyntaxTransform
* @nogrep
*/
@@ -78,6 +78,13 @@ export type VendorViewQuery$data = {
readonly organization: {
readonly " $fragmentSpreads": FragmentRefs<"PeopleSelector_organization">;
};
+ readonly viewer: {
+ readonly user: {
+ readonly people: {
+ readonly id: string;
+ } | null | undefined;
+ };
+ };
};
export type VendorViewQuery = {
response: VendorViewQuery$data;
@@ -444,15 +451,33 @@ v30 = [
}
],
v31 = {
+ "alias": null,
+ "args": [
+ {
+ "kind": "Variable",
+ "name": "organizationId",
+ "variableName": "organizationId"
+ }
+ ],
+ "concreteType": "People",
+ "kind": "LinkedField",
+ "name": "people",
+ "plural": false,
+ "selections": [
+ (v3/*: any*/)
+ ],
+ "storageKey": null
+},
+v32 = {
"kind": "Literal",
"name": "first",
"value": 100
},
-v32 = [
- (v31/*: any*/)
-],
v33 = [
- (v31/*: any*/),
+ (v32/*: any*/)
+],
+v34 = [
+ (v32/*: any*/),
{
"kind": "Literal",
"name": "orderBy",
@@ -545,6 +570,29 @@ return {
}
],
"storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "Viewer",
+ "kind": "LinkedField",
+ "name": "viewer",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "User",
+ "kind": "LinkedField",
+ "name": "user",
+ "plural": false,
+ "selections": [
+ (v31/*: any*/)
+ ],
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
}
],
"type": "Query",
@@ -593,7 +641,7 @@ return {
(v24/*: any*/),
{
"alias": null,
- "args": (v32/*: any*/),
+ "args": (v33/*: any*/),
"concreteType": "VendorComplianceReportConnection",
"kind": "LinkedField",
"name": "complianceReports",
@@ -603,7 +651,7 @@ return {
},
{
"alias": null,
- "args": (v32/*: any*/),
+ "args": (v33/*: any*/),
"filters": null,
"handle": "connection",
"key": "VendorView_complianceReports",
@@ -612,7 +660,7 @@ return {
},
{
"alias": null,
- "args": (v32/*: any*/),
+ "args": (v33/*: any*/),
"concreteType": "VendorRiskAssessmentConnection",
"kind": "LinkedField",
"name": "riskAssessments",
@@ -622,7 +670,7 @@ return {
},
{
"alias": null,
- "args": (v32/*: any*/),
+ "args": (v33/*: any*/),
"filters": null,
"handle": "connection",
"key": "VendorView_riskAssessments",
@@ -651,7 +699,7 @@ return {
"selections": [
{
"alias": null,
- "args": (v33/*: any*/),
+ "args": (v34/*: any*/),
"concreteType": "PeopleConnection",
"kind": "LinkedField",
"name": "peoples",
@@ -696,7 +744,7 @@ return {
},
{
"alias": null,
- "args": (v33/*: any*/),
+ "args": (v34/*: any*/),
"filters": [
"orderBy"
],
@@ -711,11 +759,36 @@ return {
}
],
"storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "Viewer",
+ "kind": "LinkedField",
+ "name": "viewer",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "User",
+ "kind": "LinkedField",
+ "name": "user",
+ "plural": false,
+ "selections": [
+ (v31/*: any*/),
+ (v3/*: any*/)
+ ],
+ "storageKey": null
+ },
+ (v3/*: any*/)
+ ],
+ "storageKey": null
}
]
},
"params": {
- "cacheID": "5df12f781d305bd4bd6b6c0ccb4d947e",
+ "cacheID": "9b2a625a96210e0168dceed7e4b98ed0",
"id": null,
"metadata": {
"connection": [
@@ -741,11 +814,11 @@ return {
},
"name": "VendorViewQuery",
"operationKind": "query",
- "text": "query VendorViewQuery(\n $vendorId: ID!\n $organizationId: ID!\n) {\n node(id: $vendorId) {\n __typename\n ... on Vendor {\n id\n name\n description\n serviceStartAt\n serviceTerminationAt\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n securityPageUrl\n trustPageUrl\n certifications\n headquarterAddress\n legalName\n websiteUrl\n businessOwner {\n id\n fullName\n }\n securityOwner {\n id\n fullName\n }\n createdAt\n updatedAt\n complianceReports(first: 100) {\n edges {\n node {\n id\n reportName\n reportDate\n validUntil\n fileUrl\n fileSize\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n riskAssessments(first: 100) {\n edges {\n node {\n id\n assessedAt\n expiresAt\n dataSensitivity\n businessImpact\n notes\n assessedBy {\n id\n fullName\n }\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n organization: node(id: $organizationId) {\n __typename\n ...PeopleSelector_organization\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
+ "text": "query VendorViewQuery(\n $vendorId: ID!\n $organizationId: ID!\n) {\n node(id: $vendorId) {\n __typename\n ... on Vendor {\n id\n name\n description\n serviceStartAt\n serviceTerminationAt\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n securityPageUrl\n trustPageUrl\n certifications\n headquarterAddress\n legalName\n websiteUrl\n businessOwner {\n id\n fullName\n }\n securityOwner {\n id\n fullName\n }\n createdAt\n updatedAt\n complianceReports(first: 100) {\n edges {\n node {\n id\n reportName\n reportDate\n validUntil\n fileUrl\n fileSize\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n riskAssessments(first: 100) {\n edges {\n node {\n id\n assessedAt\n expiresAt\n dataSensitivity\n businessImpact\n notes\n assessedBy {\n id\n fullName\n }\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n organization: node(id: $organizationId) {\n __typename\n ...PeopleSelector_organization\n id\n }\n viewer {\n user {\n people(organizationId: $organizationId) {\n id\n }\n id\n }\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
}
};
})();
-(node as any).hash = "2b856ea721d1bfb6b853fc281ee1aac6";
+(node as any).hash = "7f9a178bd20cefd76440ea8d71c3b2fa";
export default node;
diff --git a/pkg/coredata/migrations/20250422T033400Z.sql b/pkg/coredata/migrations/20250422T033400Z.sql
new file mode 100644
index 000000000..944e599a4
--- /dev/null
+++ b/pkg/coredata/migrations/20250422T033400Z.sql
@@ -0,0 +1,2 @@
+ALTER TABLE vendor_risk_assessments ALTER COLUMN approved_at DROP NOT NULL;
+ALTER TABLE vendor_risk_assessments ALTER COLUMN approved_by DROP NOT NULL;
diff --git a/pkg/coredata/migrations/20250422T152800Z.sql b/pkg/coredata/migrations/20250422T152800Z.sql
new file mode 100644
index 000000000..8a883baf2
--- /dev/null
+++ b/pkg/coredata/migrations/20250422T152800Z.sql
@@ -0,0 +1,3 @@
+
+ALTER TABLE vendor_risk_assessments DROP COLUMN accessed_at;
+ALTER TABLE vendor_risk_assessments DROP COLUMN accessed_by;
diff --git a/pkg/coredata/people.go b/pkg/coredata/people.go
index 9cd913e55..0c42cc907 100644
--- a/pkg/coredata/people.go
+++ b/pkg/coredata/people.go
@@ -98,6 +98,53 @@ LIMIT 1;
return nil
}
+func (p *People) LoadByUserID(
+ ctx context.Context,
+ conn pg.Conn,
+ scope Scoper,
+ organizationID gid.GID,
+ userID gid.GID,
+) error {
+ q := `
+SELECT
+ id,
+ organization_id,
+ kind,
+ user_id,
+ full_name,
+ primary_email_address,
+ additional_email_addresses,
+ created_at,
+ updated_at
+FROM
+ peoples
+WHERE
+ %s
+ AND organization_id = @organization_id
+ AND user_id = @user_id
+LIMIT 1;
+`
+
+ q = fmt.Sprintf(q, scope.SQLFragment())
+
+ args := pgx.StrictNamedArgs{"organization_id": organizationID, "user_id": userID}
+ maps.Copy(args, scope.SQLArguments())
+
+ rows, err := conn.Query(ctx, q, args)
+ if err != nil {
+ return fmt.Errorf("cannot query people: %w", err)
+ }
+
+ people, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[People])
+ if err != nil {
+ return fmt.Errorf("cannot collect people: %w", err)
+ }
+
+ *p = people
+
+ return nil
+}
+
func (p People) Insert(
ctx context.Context,
conn pg.Conn,
diff --git a/pkg/coredata/vendor.go b/pkg/coredata/vendor.go
index 96ce189df..6349301ef 100644
--- a/pkg/coredata/vendor.go
+++ b/pkg/coredata/vendor.go
@@ -350,3 +350,37 @@ WHERE %s
_, err := conn.Exec(ctx, q, args)
return err
}
+
+func (v Vendor) ExpireNonExpiredRiskAssessments(
+ ctx context.Context,
+ conn pg.Conn,
+ scope Scoper,
+) error {
+ now := time.Now()
+
+ q := `
+ UPDATE vendor_risk_assessments
+ SET
+ expires_at = @now,
+ updated_at = @now
+ WHERE
+ %s
+ AND vendor_id = @vendor_id
+ AND expires_at > @now
+ `
+
+ q = fmt.Sprintf(q, scope.SQLFragment())
+
+ args := pgx.StrictNamedArgs{
+ "vendor_id": v.ID,
+ "now": now,
+ }
+ maps.Copy(args, scope.SQLArguments())
+
+ _, err := conn.Exec(ctx, q, args)
+ if err != nil {
+ return fmt.Errorf("cannot expire existing risk assessments: %w", err)
+ }
+
+ return nil
+}
diff --git a/pkg/coredata/vendor_risk_assessment.go b/pkg/coredata/vendor_risk_assessment.go
index c4319eb9d..3333439f8 100644
--- a/pkg/coredata/vendor_risk_assessment.go
+++ b/pkg/coredata/vendor_risk_assessment.go
@@ -33,9 +33,8 @@ type (
VendorID gid.GID `db:"vendor_id"`
AssessedAt time.Time `db:"assessed_at"`
AssessedBy gid.GID `db:"assessed_by"`
- AccessedAt time.Time `db:"accessed_at"`
- ApprovedBy gid.GID `db:"approved_by"`
- ApprovedAt time.Time `db:"approved_at"`
+ ApprovedBy *gid.GID `db:"approved_by"`
+ ApprovedAt *time.Time `db:"approved_at"`
ExpiresAt time.Time `db:"expires_at"`
DataSensitivity DataSensitivity `db:"data_sensitivity"`
BusinessImpact BusinessImpact `db:"business_impact"`
@@ -96,7 +95,6 @@ INSERT INTO
vendor_id,
assessed_at,
assessed_by,
- accessed_at,
approved_by,
approved_at,
expires_at,
@@ -112,11 +110,12 @@ VALUES (
@vendor_id,
@assessed_at,
@assessed_by,
+ @approved_by,
+ @approved_at,
@expires_at,
@data_sensitivity,
@business_impact,
@notes,
- @attachments,
@created_at,
@updated_at
)
@@ -128,7 +127,6 @@ VALUES (
"vendor_id": r.VendorID,
"assessed_at": r.AssessedAt,
"assessed_by": r.AssessedBy,
- "accessed_at": r.AccessedAt,
"approved_by": r.ApprovedBy,
"approved_at": r.ApprovedAt,
"expires_at": r.ExpiresAt,
@@ -155,7 +153,6 @@ SELECT
vendor_id,
assessed_at,
assessed_by,
- accessed_at,
approved_by,
approved_at,
expires_at,
@@ -206,7 +203,6 @@ SELECT
vendor_id,
assessed_at,
assessed_by,
- accessed_at,
approved_by,
approved_at,
expires_at,
@@ -260,7 +256,6 @@ SELECT
vendor_id,
assessed_at,
assessed_by,
- accessed_at,
approved_by,
approved_at,
expires_at,
diff --git a/pkg/probo/people_service.go b/pkg/probo/people_service.go
index cafb5d767..c9b8ad6fa 100644
--- a/pkg/probo/people_service.go
+++ b/pkg/probo/people_service.go
@@ -69,6 +69,27 @@ func (s PeopleService) Get(
return people, nil
}
+func (s PeopleService) GetByUserID(
+ ctx context.Context,
+ organizationID gid.GID,
+ userID gid.GID,
+) (*coredata.People, error) {
+ people := &coredata.People{}
+
+ err := s.svc.pg.WithConn(
+ ctx,
+ func(conn pg.Conn) error {
+ return people.LoadByUserID(ctx, conn, s.svc.scope, organizationID, userID)
+ },
+ )
+
+ if err != nil {
+ return nil, err
+ }
+
+ return people, nil
+}
+
func (s PeopleService) ListForOrganizationID(
ctx context.Context,
organizationID gid.GID,
diff --git a/pkg/probo/vendor_service.go b/pkg/probo/vendor_service.go
index 73c8f24fe..7e5a132e3 100644
--- a/pkg/probo/vendor_service.go
+++ b/pkg/probo/vendor_service.go
@@ -359,6 +359,7 @@ func (s VendorService) CreateRiskAssessment(
ID: vendorRiskAssessmentID,
VendorID: req.VendorID,
AssessedBy: req.AssessedByID,
+ AssessedAt: now,
ExpiresAt: req.ExpiresAt,
DataSensitivity: req.DataSensitivity,
BusinessImpact: req.BusinessImpact,
@@ -369,11 +370,15 @@ func (s VendorService) CreateRiskAssessment(
err = s.svc.pg.WithTx(
ctx,
- func(conn pg.Conn) error {
- if err := vendorRiskAssessment.Insert(ctx, conn, s.svc.scope); err != nil {
- return fmt.Errorf("cannot insert vendor risk assessment: %w", err)
+ func(tx pg.Conn) error {
+ vendor := coredata.Vendor{ID: req.VendorID}
+ if err := vendor.ExpireNonExpiredRiskAssessments(ctx, tx, s.svc.scope); err != nil {
+ return fmt.Errorf("cannot expire vendor risk assessments: %w", err)
}
+ if err := vendorRiskAssessment.Insert(ctx, tx, s.svc.scope); err != nil {
+ return fmt.Errorf("cannot insert vendor risk assessment: %w", err)
+ }
return nil
},
)
@@ -384,3 +389,23 @@ func (s VendorService) CreateRiskAssessment(
return vendorRiskAssessment, nil
}
+
+func (s VendorService) GetRiskAssessment(
+ ctx context.Context,
+ vendorRiskAssessmentID gid.GID,
+) (*coredata.VendorRiskAssessment, error) {
+ vendorRiskAssessment := &coredata.VendorRiskAssessment{}
+
+ err := s.svc.pg.WithConn(
+ ctx,
+ func(conn pg.Conn) error {
+ return vendorRiskAssessment.LoadByID(ctx, conn, s.svc.scope, vendorRiskAssessmentID)
+ },
+ )
+
+ if err != nil {
+ return nil, err
+ }
+
+ return vendorRiskAssessment, nil
+}
diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql
index d7e00c6c3..2f9c5df69 100644
--- a/pkg/server/api/console/v1/schema.graphql
+++ b/pkg/server/api/console/v1/schema.graphql
@@ -455,6 +455,8 @@ type User implements Node {
email: String!
createdAt: Datetime!
updatedAt: Datetime!
+
+ people(organizationId: ID!): People @goField(forceResolver: true)
}
type Connector implements Node {
@@ -1463,7 +1465,6 @@ type VendorRiskAssessment implements Node {
dataSensitivity: DataSensitivity!
businessImpact: BusinessImpact!
notes: String
- attachments: [String!]!
createdAt: Datetime!
updatedAt: Datetime!
}
@@ -1487,7 +1488,6 @@ input CreateVendorRiskAssessmentInput {
dataSensitivity: DataSensitivity!
businessImpact: BusinessImpact!
notes: String
- attachments: [String!]
}
type CreateVendorRiskAssessmentPayload {
diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go
index 7ed04375a..c8e769ed5 100644
--- a/pkg/server/api/console/v1/schema/schema.go
+++ b/pkg/server/api/console/v1/schema/schema.go
@@ -52,6 +52,7 @@ type ResolverRoot interface {
Query() QueryResolver
Risk() RiskResolver
Task() TaskResolver
+ User() UserResolver
Vendor() VendorResolver
VendorComplianceReport() VendorComplianceReportResolver
VendorRiskAssessment() VendorRiskAssessmentResolver
@@ -538,6 +539,7 @@ type ComplexityRoot struct {
Email func(childComplexity int) int
FullName func(childComplexity int) int
ID func(childComplexity int) int
+ People func(childComplexity int, organizationID gid.GID) int
UpdatedAt func(childComplexity int) int
}
@@ -611,7 +613,6 @@ type ComplexityRoot struct {
VendorRiskAssessment struct {
AssessedAt func(childComplexity int) int
AssessedBy func(childComplexity int) int
- Attachments func(childComplexity int) int
BusinessImpact func(childComplexity int) int
CreatedAt func(childComplexity int) int
DataSensitivity func(childComplexity int) int
@@ -730,6 +731,9 @@ type TaskResolver interface {
AssignedTo(ctx context.Context, obj *types.Task) (*types.People, error)
Evidences(ctx context.Context, obj *types.Task, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.EvidenceOrderBy) (*types.EvidenceConnection, error)
}
+type UserResolver interface {
+ People(ctx context.Context, obj *types.User, organizationID gid.GID) (*types.People, error)
+}
type VendorResolver interface {
ComplianceReports(ctx context.Context, obj *types.Vendor, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorComplianceReportOrderBy) (*types.VendorComplianceReportConnection, error)
RiskAssessments(ctx context.Context, obj *types.Vendor, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorRiskAssessmentOrder) (*types.VendorRiskAssessmentConnection, error)
@@ -2760,6 +2764,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.User.ID(childComplexity), true
+ case "User.people":
+ if e.complexity.User.People == nil {
+ break
+ }
+
+ args, err := ec.field_User_people_args(context.TODO(), rawArgs)
+ if err != nil {
+ return 0, false
+ }
+
+ return e.complexity.User.People(childComplexity, args["organizationId"].(gid.GID)), true
+
case "User.updatedAt":
if e.complexity.User.UpdatedAt == nil {
break
@@ -3092,13 +3108,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.VendorRiskAssessment.AssessedBy(childComplexity), true
- case "VendorRiskAssessment.attachments":
- if e.complexity.VendorRiskAssessment.Attachments == nil {
- break
- }
-
- return e.complexity.VendorRiskAssessment.Attachments(childComplexity), true
-
case "VendorRiskAssessment.businessImpact":
if e.complexity.VendorRiskAssessment.BusinessImpact == nil {
break
@@ -3830,6 +3839,8 @@ type User implements Node {
email: String!
createdAt: Datetime!
updatedAt: Datetime!
+
+ people(organizationId: ID!): People @goField(forceResolver: true)
}
type Connector implements Node {
@@ -4838,7 +4849,6 @@ type VendorRiskAssessment implements Node {
dataSensitivity: DataSensitivity!
businessImpact: BusinessImpact!
notes: String
- attachments: [String!]!
createdAt: Datetime!
updatedAt: Datetime!
}
@@ -4862,7 +4872,6 @@ input CreateVendorRiskAssessmentInput {
dataSensitivity: DataSensitivity!
businessImpact: BusinessImpact!
notes: String
- attachments: [String!]
}
type CreateVendorRiskAssessmentPayload {
@@ -7762,6 +7771,29 @@ func (ec *executionContext) field_Task_evidences_argsOrderBy(
return zeroVal, nil
}
+func (ec *executionContext) field_User_people_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
+ var err error
+ args := map[string]any{}
+ arg0, err := ec.field_User_people_argsOrganizationID(ctx, rawArgs)
+ if err != nil {
+ return nil, err
+ }
+ args["organizationId"] = arg0
+ return args, nil
+}
+func (ec *executionContext) field_User_people_argsOrganizationID(
+ ctx context.Context,
+ rawArgs map[string]any,
+) (gid.GID, error) {
+ ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("organizationId"))
+ if tmp, ok := rawArgs["organizationId"]; ok {
+ return ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, tmp)
+ }
+
+ var zeroVal gid.GID
+ return zeroVal, nil
+}
+
func (ec *executionContext) field_Vendor_complianceReports_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -20538,6 +20570,74 @@ func (ec *executionContext) fieldContext_User_updatedAt(_ context.Context, field
return fc, nil
}
+func (ec *executionContext) _User_people(ctx context.Context, field graphql.CollectedField, obj *types.User) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_User_people(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.User().People(rctx, obj, fc.Args["organizationId"].(gid.GID))
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ return graphql.Null
+ }
+ res := resTmp.(*types.People)
+ fc.Result = res
+ return ec.marshalOPeople2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐPeople(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_User_people(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "User",
+ Field: field,
+ IsMethod: true,
+ IsResolver: true,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ switch field.Name {
+ case "id":
+ return ec.fieldContext_People_id(ctx, field)
+ case "fullName":
+ return ec.fieldContext_People_fullName(ctx, field)
+ case "primaryEmailAddress":
+ return ec.fieldContext_People_primaryEmailAddress(ctx, field)
+ case "additionalEmailAddresses":
+ return ec.fieldContext_People_additionalEmailAddresses(ctx, field)
+ case "kind":
+ return ec.fieldContext_People_kind(ctx, field)
+ case "createdAt":
+ return ec.fieldContext_People_createdAt(ctx, field)
+ case "updatedAt":
+ return ec.fieldContext_People_updatedAt(ctx, field)
+ }
+ return nil, fmt.Errorf("no field named %q was found under type People", field.Name)
+ },
+ }
+ defer func() {
+ if r := recover(); r != nil {
+ err = ec.Recover(ctx, r)
+ ec.Error(ctx, err)
+ }
+ }()
+ ctx = graphql.WithFieldContext(ctx, fc)
+ if fc.Args, err = ec.field_User_people_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
+ ec.Error(ctx, err)
+ return fc, err
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _UserConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.UserConnection) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_UserConnection_edges(ctx, field)
if err != nil {
@@ -20735,6 +20835,8 @@ func (ec *executionContext) fieldContext_UserEdge_node(_ context.Context, field
return ec.fieldContext_User_createdAt(ctx, field)
case "updatedAt":
return ec.fieldContext_User_updatedAt(ctx, field)
+ case "people":
+ return ec.fieldContext_User_people(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ -23034,50 +23136,6 @@ func (ec *executionContext) fieldContext_VendorRiskAssessment_notes(_ context.Co
return fc, nil
}
-func (ec *executionContext) _VendorRiskAssessment_attachments(ctx context.Context, field graphql.CollectedField, obj *types.VendorRiskAssessment) (ret graphql.Marshaler) {
- fc, err := ec.fieldContext_VendorRiskAssessment_attachments(ctx, field)
- if err != nil {
- return graphql.Null
- }
- ctx = graphql.WithFieldContext(ctx, fc)
- defer func() {
- if r := recover(); r != nil {
- ec.Error(ctx, ec.Recover(ctx, r))
- ret = graphql.Null
- }
- }()
- resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
- ctx = rctx // use context from middleware stack in children
- return obj.Attachments, nil
- })
- if err != nil {
- ec.Error(ctx, err)
- return graphql.Null
- }
- if resTmp == nil {
- if !graphql.HasFieldError(ctx, fc) {
- ec.Errorf(ctx, "must not be null")
- }
- return graphql.Null
- }
- res := resTmp.([]string)
- fc.Result = res
- return ec.marshalNString2ᚕstringᚄ(ctx, field.Selections, res)
-}
-
-func (ec *executionContext) fieldContext_VendorRiskAssessment_attachments(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
- fc = &graphql.FieldContext{
- Object: "VendorRiskAssessment",
- Field: field,
- IsMethod: false,
- IsResolver: false,
- Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
- return nil, errors.New("field of type String does not have child fields")
- },
- }
- return fc, nil
-}
-
func (ec *executionContext) _VendorRiskAssessment_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.VendorRiskAssessment) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_VendorRiskAssessment_createdAt(ctx, field)
if err != nil {
@@ -23369,8 +23427,6 @@ func (ec *executionContext) fieldContext_VendorRiskAssessmentEdge_node(_ context
return ec.fieldContext_VendorRiskAssessment_businessImpact(ctx, field)
case "notes":
return ec.fieldContext_VendorRiskAssessment_notes(ctx, field)
- case "attachments":
- return ec.fieldContext_VendorRiskAssessment_attachments(ctx, field)
case "createdAt":
return ec.fieldContext_VendorRiskAssessment_createdAt(ctx, field)
case "updatedAt":
@@ -23475,6 +23531,8 @@ func (ec *executionContext) fieldContext_Viewer_user(_ context.Context, field gr
return ec.fieldContext_User_createdAt(ctx, field)
case "updatedAt":
return ec.fieldContext_User_updatedAt(ctx, field)
+ case "people":
+ return ec.fieldContext_User_people(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type User", field.Name)
},
@@ -26366,7 +26424,7 @@ func (ec *executionContext) unmarshalInputCreateVendorRiskAssessmentInput(ctx co
asMap[k] = v
}
- fieldsInOrder := [...]string{"vendorId", "assessedBy", "expiresAt", "dataSensitivity", "businessImpact", "notes", "attachments"}
+ fieldsInOrder := [...]string{"vendorId", "assessedBy", "expiresAt", "dataSensitivity", "businessImpact", "notes"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -26415,13 +26473,6 @@ func (ec *executionContext) unmarshalInputCreateVendorRiskAssessmentInput(ctx co
return it, err
}
it.Notes = data
- case "attachments":
- ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("attachments"))
- data, err := ec.unmarshalOString2ᚕstringᚄ(ctx, v)
- if err != nil {
- return it, err
- }
- it.Attachments = data
}
}
@@ -32945,28 +32996,61 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
case "id":
out.Values[i] = ec._User_id(ctx, field, obj)
if out.Values[i] == graphql.Null {
- out.Invalids++
+ atomic.AddUint32(&out.Invalids, 1)
}
case "fullName":
out.Values[i] = ec._User_fullName(ctx, field, obj)
if out.Values[i] == graphql.Null {
- out.Invalids++
+ atomic.AddUint32(&out.Invalids, 1)
}
case "email":
out.Values[i] = ec._User_email(ctx, field, obj)
if out.Values[i] == graphql.Null {
- out.Invalids++
+ atomic.AddUint32(&out.Invalids, 1)
}
case "createdAt":
out.Values[i] = ec._User_createdAt(ctx, field, obj)
if out.Values[i] == graphql.Null {
- out.Invalids++
+ atomic.AddUint32(&out.Invalids, 1)
}
case "updatedAt":
out.Values[i] = ec._User_updatedAt(ctx, field, obj)
if out.Values[i] == graphql.Null {
- out.Invalids++
+ atomic.AddUint32(&out.Invalids, 1)
}
+ case "people":
+ field := field
+
+ innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._User_people(ctx, field, obj)
+ return res
+ }
+
+ if field.Deferrable != nil {
+ dfs, ok := deferred[field.Deferrable.Label]
+ di := 0
+ if ok {
+ dfs.AddField(field)
+ di = len(dfs.Values) - 1
+ } else {
+ dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
+ deferred[field.Deferrable.Label] = dfs
+ }
+ dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
+ return innerFunc(ctx, dfs)
+ })
+
+ // don't run the out.Concurrently() call below
+ out.Values[i] = graphql.Null
+ continue
+ }
+
+ out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@@ -33728,11 +33812,6 @@ func (ec *executionContext) _VendorRiskAssessment(ctx context.Context, sel ast.S
}
case "notes":
out.Values[i] = ec._VendorRiskAssessment_notes(ctx, field, obj)
- case "attachments":
- out.Values[i] = ec._VendorRiskAssessment_attachments(ctx, field, obj)
- if out.Values[i] == graphql.Null {
- atomic.AddUint32(&out.Invalids, 1)
- }
case "createdAt":
out.Values[i] = ec._VendorRiskAssessment_createdAt(ctx, field, obj)
if out.Values[i] == graphql.Null {
diff --git a/pkg/server/api/console/v1/types/types.go b/pkg/server/api/console/v1/types/types.go
index 9f93fd0d1..1b4f55dba 100644
--- a/pkg/server/api/console/v1/types/types.go
+++ b/pkg/server/api/console/v1/types/types.go
@@ -252,7 +252,6 @@ type CreateVendorRiskAssessmentInput struct {
DataSensitivity coredata.DataSensitivity `json:"dataSensitivity"`
BusinessImpact coredata.BusinessImpact `json:"businessImpact"`
Notes *string `json:"notes,omitempty"`
- Attachments []string `json:"attachments,omitempty"`
}
type CreateVendorRiskAssessmentPayload struct {
@@ -799,6 +798,7 @@ type User struct {
Email string `json:"email"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
+ People *People `json:"people,omitempty"`
}
func (User) IsNode() {}
@@ -886,7 +886,6 @@ type VendorRiskAssessment struct {
DataSensitivity coredata.DataSensitivity `json:"dataSensitivity"`
BusinessImpact coredata.BusinessImpact `json:"businessImpact"`
Notes *string `json:"notes,omitempty"`
- Attachments []string `json:"attachments"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go
index 566368c80..30809947a 100644
--- a/pkg/server/api/console/v1/v1_resolver.go
+++ b/pkg/server/api/console/v1/v1_resolver.go
@@ -1049,16 +1049,19 @@ func (r *mutationResolver) DeletePolicy(ctx context.Context, input types.DeleteP
// CreateVendorRiskAssessment is the resolver for the createVendorRiskAssessment field.
func (r *mutationResolver) CreateVendorRiskAssessment(ctx context.Context, input types.CreateVendorRiskAssessmentInput) (*types.CreateVendorRiskAssessmentPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.VendorID.TenantID())
- user := UserFromContext(ctx)
- expiresAt := time.Now().Add(time.Hour * 24 * 365)
+ fmt.Println("input.AssessedBy", input.AssessedBy)
+ fmt.Println("input.ExpiresAt", input.ExpiresAt)
+ fmt.Println("input.DataSensitivity", input.DataSensitivity)
+ fmt.Println("input.BusinessImpact", input.BusinessImpact)
+ fmt.Println("input.Notes", input.Notes)
vendorRiskAssessment, err := svc.Vendors.CreateRiskAssessment(
ctx,
probo.CreateVendorRiskAssessmentRequest{
VendorID: input.VendorID,
- AssessedByID: user.ID,
- ExpiresAt: expiresAt,
+ AssessedByID: input.AssessedBy,
+ ExpiresAt: input.ExpiresAt,
DataSensitivity: input.DataSensitivity,
BusinessImpact: input.BusinessImpact,
Notes: input.Notes,
@@ -1558,6 +1561,18 @@ func (r *taskResolver) Evidences(ctx context.Context, obj *types.Task, first *in
return types.NewEvidenceConnection(page), nil
}
+// People is the resolver for the people field.
+func (r *userResolver) People(ctx context.Context, obj *types.User, organizationID gid.GID) (*types.People, error) {
+ svc := GetTenantService(ctx, r.proboSvc, organizationID.TenantID())
+
+ people, err := svc.Peoples.GetByUserID(ctx, organizationID, obj.ID)
+ if err != nil {
+ panic(fmt.Errorf("failed to get people: %w", err))
+ }
+
+ return types.NewPeople(people), nil
+}
+
// ComplianceReports is the resolver for the complianceReports field.
func (r *vendorResolver) ComplianceReports(ctx context.Context, obj *types.Vendor, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorComplianceReportOrderBy) (*types.VendorComplianceReportConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
@@ -1676,12 +1691,31 @@ func (r *vendorComplianceReportResolver) FileURL(ctx context.Context, obj *types
// Vendor is the resolver for the vendor field.
func (r *vendorRiskAssessmentResolver) Vendor(ctx context.Context, obj *types.VendorRiskAssessment) (*types.Vendor, error) {
- panic(fmt.Errorf("not implemented: Vendor - vendor"))
+ svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
+
+ vendor, err := svc.Vendors.Get(ctx, obj.ID)
+ if err != nil {
+ panic(fmt.Errorf("failed to get vendor: %w", err))
+ }
+
+ return types.NewVendor(vendor), nil
}
// AssessedBy is the resolver for the assessedBy field.
func (r *vendorRiskAssessmentResolver) AssessedBy(ctx context.Context, obj *types.VendorRiskAssessment) (*types.People, error) {
- panic(fmt.Errorf("not implemented: AssessedBy - assessedBy"))
+ svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
+
+ vendorRiskAssessment, err := svc.Vendors.GetRiskAssessment(ctx, obj.ID)
+ if err != nil {
+ panic(fmt.Errorf("failed to get vendor risk assessment: %w", err))
+ }
+
+ people, err := svc.Peoples.Get(ctx, vendorRiskAssessment.AssessedBy)
+ if err != nil {
+ panic(fmt.Errorf("failed to get assessed by: %w", err))
+ }
+
+ return types.NewPeople(people), nil
}
// Organizations is the resolver for the organizations field.
@@ -1739,6 +1773,9 @@ func (r *Resolver) Risk() schema.RiskResolver { return &riskResolver{r} }
// Task returns schema.TaskResolver implementation.
func (r *Resolver) Task() schema.TaskResolver { return &taskResolver{r} }
+// User returns schema.UserResolver implementation.
+func (r *Resolver) User() schema.UserResolver { return &userResolver{r} }
+
// Vendor returns schema.VendorResolver implementation.
func (r *Resolver) Vendor() schema.VendorResolver { return &vendorResolver{r} }
@@ -1765,6 +1802,7 @@ type policyResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type riskResolver struct{ *Resolver }
type taskResolver struct{ *Resolver }
+type userResolver struct{ *Resolver }
type vendorResolver struct{ *Resolver }
type vendorComplianceReportResolver struct{ *Resolver }
type vendorRiskAssessmentResolver struct{ *Resolver }