Add compliance frameworks

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-03-03 08:56:39 +01:00
parent 4d882e37b0
commit c17c53e80f
36 changed files with 5303 additions and 90 deletions

View File

@@ -0,0 +1,265 @@
/**
* @generated SignedSource<<8626547ee7655aabebf153f2429023d7>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type ComplianceFrameworkVisibility = "NONE" | "PUBLIC";
import { FragmentRefs } from "relay-runtime";
export type CompliancePageFrameworkList_compliancePageFragment$data = {
readonly canUpdate: boolean;
readonly complianceFrameworks: {
readonly __id: string;
readonly edges: ReadonlyArray<{
readonly node: {
readonly framework: {
readonly darkLogoURL: string | null | undefined;
readonly id: string;
readonly lightLogoURL: string | null | undefined;
readonly name: string;
};
readonly id: string;
readonly rank: number;
readonly visibility: ComplianceFrameworkVisibility;
};
}>;
};
readonly id: string;
readonly " $fragmentType": "CompliancePageFrameworkList_compliancePageFragment";
};
export type CompliancePageFrameworkList_compliancePageFragment$key = {
readonly " $data"?: CompliancePageFrameworkList_compliancePageFragment$data;
readonly " $fragmentSpreads": FragmentRefs<"CompliancePageFrameworkList_compliancePageFragment">;
};
import CompliancePageFrameworkList_compliancePageRefetchQuery_graphql from './CompliancePageFrameworkList_compliancePageRefetchQuery.graphql';
const node: ReaderFragment = (function(){
var v0 = [
"complianceFrameworks"
],
v1 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
};
return {
"argumentDefinitions": [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "after"
},
{
"defaultValue": 100,
"kind": "LocalArgument",
"name": "first"
},
{
"defaultValue": {
"direction": "ASC",
"field": "RANK"
},
"kind": "LocalArgument",
"name": "order"
}
],
"kind": "Fragment",
"metadata": {
"connection": [
{
"count": "first",
"cursor": "after",
"direction": "forward",
"path": (v0/*: any*/)
}
],
"refetch": {
"connection": {
"forward": {
"count": "first",
"cursor": "after"
},
"backward": null,
"path": (v0/*: any*/)
},
"fragmentPathInResult": [
"node"
],
"operation": CompliancePageFrameworkList_compliancePageRefetchQuery_graphql,
"identifierInfo": {
"identifierField": "id",
"identifierQueryVariableName": "id"
}
}
},
"name": "CompliancePageFrameworkList_compliancePageFragment",
"selections": [
(v1/*: any*/),
{
"alias": "canUpdate",
"args": [
{
"kind": "Literal",
"name": "action",
"value": "core:trust-center:update"
}
],
"kind": "ScalarField",
"name": "permission",
"storageKey": "permission(action:\"core:trust-center:update\")"
},
{
"alias": "complianceFrameworks",
"args": [
{
"kind": "Variable",
"name": "orderBy",
"variableName": "order"
}
],
"concreteType": "ComplianceFrameworkConnection",
"kind": "LinkedField",
"name": "__CompliancePageFrameworkList_complianceFrameworks_connection",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFrameworkEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFramework",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v1/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "rank",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "visibility",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "Framework",
"kind": "LinkedField",
"name": "framework",
"plural": false,
"selections": [
(v1/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "lightLogoURL",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "darkLogoURL",
"storageKey": null
}
],
"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": "endCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
}
],
"storageKey": null
},
{
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
}
],
"storageKey": null
}
],
"type": "TrustCenter",
"abstractKey": null
};
})();
(node as any).hash = "981aff9d27dc71027541f7c3ed6984a9";
export default node;

View File

@@ -0,0 +1,335 @@
/**
* @generated SignedSource<<baca8afb1ea09de1d4e9cc721fa3318e>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type ComplianceFrameworkOrderField = "CREATED_AT" | "RANK";
export type OrderDirection = "ASC" | "DESC";
export type ComplianceFrameworkOrder = {
direction: OrderDirection;
field: ComplianceFrameworkOrderField;
};
export type CompliancePageFrameworkList_compliancePageRefetchQuery$variables = {
after?: string | null | undefined;
first?: number | null | undefined;
id: string;
order?: ComplianceFrameworkOrder | null | undefined;
};
export type CompliancePageFrameworkList_compliancePageRefetchQuery$data = {
readonly node: {
readonly " $fragmentSpreads": FragmentRefs<"CompliancePageFrameworkList_compliancePageFragment">;
};
};
export type CompliancePageFrameworkList_compliancePageRefetchQuery = {
response: CompliancePageFrameworkList_compliancePageRefetchQuery$data;
variables: CompliancePageFrameworkList_compliancePageRefetchQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "after"
},
v1 = {
"defaultValue": 100,
"kind": "LocalArgument",
"name": "first"
},
v2 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "id"
},
v3 = {
"defaultValue": {
"direction": "ASC",
"field": "RANK"
},
"kind": "LocalArgument",
"name": "order"
},
v4 = [
{
"kind": "Variable",
"name": "id",
"variableName": "id"
}
],
v5 = {
"kind": "Variable",
"name": "after",
"variableName": "after"
},
v6 = {
"kind": "Variable",
"name": "first",
"variableName": "first"
},
v7 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v8 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v9 = [
(v5/*: any*/),
(v6/*: any*/),
{
"kind": "Variable",
"name": "orderBy",
"variableName": "order"
}
];
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/),
(v2/*: any*/),
(v3/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "CompliancePageFrameworkList_compliancePageRefetchQuery",
"selections": [
{
"alias": null,
"args": (v4/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"args": [
(v5/*: any*/),
(v6/*: any*/),
{
"kind": "Variable",
"name": "order",
"variableName": "order"
}
],
"kind": "FragmentSpread",
"name": "CompliancePageFrameworkList_compliancePageFragment"
}
],
"storageKey": null
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/),
(v3/*: any*/),
(v2/*: any*/)
],
"kind": "Operation",
"name": "CompliancePageFrameworkList_compliancePageRefetchQuery",
"selections": [
{
"alias": null,
"args": (v4/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v7/*: any*/),
(v8/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": "canUpdate",
"args": [
{
"kind": "Literal",
"name": "action",
"value": "core:trust-center:update"
}
],
"kind": "ScalarField",
"name": "permission",
"storageKey": "permission(action:\"core:trust-center:update\")"
},
{
"alias": null,
"args": (v9/*: any*/),
"concreteType": "ComplianceFrameworkConnection",
"kind": "LinkedField",
"name": "complianceFrameworks",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFrameworkEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFramework",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v8/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "rank",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "visibility",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "Framework",
"kind": "LinkedField",
"name": "framework",
"plural": false,
"selections": [
(v8/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "lightLogoURL",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "darkLogoURL",
"storageKey": null
}
],
"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": "endCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
}
],
"storageKey": null
},
{
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
}
],
"storageKey": null
},
{
"alias": null,
"args": (v9/*: any*/),
"filters": [
"orderBy"
],
"handle": "connection",
"key": "CompliancePageFrameworkList_complianceFrameworks",
"kind": "LinkedHandle",
"name": "complianceFrameworks"
}
],
"type": "TrustCenter",
"abstractKey": null
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "c3c1d708940371ebc18c2c5272f0b26d",
"id": null,
"metadata": {},
"name": "CompliancePageFrameworkList_compliancePageRefetchQuery",
"operationKind": "query",
"text": "query CompliancePageFrameworkList_compliancePageRefetchQuery(\n $after: CursorKey = null\n $first: Int = 100\n $order: ComplianceFrameworkOrder = {field: RANK, direction: ASC}\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...CompliancePageFrameworkList_compliancePageFragment_gOVF5\n id\n }\n}\n\nfragment CompliancePageFrameworkList_compliancePageFragment_gOVF5 on TrustCenter {\n id\n canUpdate: permission(action: \"core:trust-center:update\")\n complianceFrameworks(first: $first, after: $after, orderBy: $order) {\n edges {\n node {\n id\n rank\n visibility\n framework {\n id\n name\n lightLogoURL\n darkLogoURL\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
}
};
})();
(node as any).hash = "981aff9d27dc71027541f7c3ed6984a9";
export default node;

View File

@@ -0,0 +1,119 @@
/**
* @generated SignedSource<<2980fa2d2a72851686e88a7b5402d720>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type CreateComplianceFrameworkInput = {
frameworkId: string;
trustCenterId: string;
};
export type CompliancePageFrameworkList_createMutation$variables = {
input: CreateComplianceFrameworkInput;
};
export type CompliancePageFrameworkList_createMutation$data = {
readonly createComplianceFramework: {
readonly complianceFrameworkEdge: {
readonly node: {
readonly id: string;
};
};
};
};
export type CompliancePageFrameworkList_createMutation = {
response: CompliancePageFrameworkList_createMutation$data;
variables: CompliancePageFrameworkList_createMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
}
],
v1 = [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
"concreteType": "CreateComplianceFrameworkPayload",
"kind": "LinkedField",
"name": "createComplianceFramework",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFrameworkEdge",
"kind": "LinkedField",
"name": "complianceFrameworkEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFramework",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "CompliancePageFrameworkList_createMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "CompliancePageFrameworkList_createMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "9caebfdeda321fb743a8d789c7e25e82",
"id": null,
"metadata": {},
"name": "CompliancePageFrameworkList_createMutation",
"operationKind": "mutation",
"text": "mutation CompliancePageFrameworkList_createMutation(\n $input: CreateComplianceFrameworkInput!\n) {\n createComplianceFramework(input: $input) {\n complianceFrameworkEdge {\n node {\n id\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "41815aaef15ed1754b4d81c1d982a2c6";
export default node;

View File

@@ -0,0 +1,92 @@
/**
* @generated SignedSource<<0f29d441e95842685f0311839d3b45ef>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DeleteComplianceFrameworkInput = {
id: string;
};
export type CompliancePageFrameworkList_deleteMutation$variables = {
input: DeleteComplianceFrameworkInput;
};
export type CompliancePageFrameworkList_deleteMutation$data = {
readonly deleteComplianceFramework: {
readonly deletedComplianceFrameworkId: string;
};
};
export type CompliancePageFrameworkList_deleteMutation = {
response: CompliancePageFrameworkList_deleteMutation$data;
variables: CompliancePageFrameworkList_deleteMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
}
],
v1 = [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
"concreteType": "DeleteComplianceFrameworkPayload",
"kind": "LinkedField",
"name": "deleteComplianceFramework",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "deletedComplianceFrameworkId",
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "CompliancePageFrameworkList_deleteMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "CompliancePageFrameworkList_deleteMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "5792168e44352169568a1378b621df16",
"id": null,
"metadata": {},
"name": "CompliancePageFrameworkList_deleteMutation",
"operationKind": "mutation",
"text": "mutation CompliancePageFrameworkList_deleteMutation(\n $input: DeleteComplianceFrameworkInput!\n) {\n deleteComplianceFramework(input: $input) {\n deletedComplianceFrameworkId\n }\n}\n"
}
};
})();
(node as any).hash = "fed45c59adf7d88aec4bbb7eb61840c3";
export default node;

View File

@@ -0,0 +1,114 @@
/**
* @generated SignedSource<<3c6052dff7f71548026fb23353e215b2>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type UpdateComplianceFrameworkInput = {
id: string;
rank: number;
};
export type CompliancePageFrameworkList_updateRankMutation$variables = {
input: UpdateComplianceFrameworkInput;
};
export type CompliancePageFrameworkList_updateRankMutation$data = {
readonly updateComplianceFramework: {
readonly complianceFramework: {
readonly id: string;
readonly rank: number;
};
};
};
export type CompliancePageFrameworkList_updateRankMutation = {
response: CompliancePageFrameworkList_updateRankMutation$data;
variables: CompliancePageFrameworkList_updateRankMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
}
],
v1 = [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
"concreteType": "UpdateComplianceFrameworkPayload",
"kind": "LinkedField",
"name": "updateComplianceFramework",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFramework",
"kind": "LinkedField",
"name": "complianceFramework",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "rank",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "CompliancePageFrameworkList_updateRankMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "CompliancePageFrameworkList_updateRankMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "429989dd7550f3c5466a8098f5167592",
"id": null,
"metadata": {},
"name": "CompliancePageFrameworkList_updateRankMutation",
"operationKind": "mutation",
"text": "mutation CompliancePageFrameworkList_updateRankMutation(\n $input: UpdateComplianceFrameworkInput!\n) {\n updateComplianceFramework(input: $input) {\n complianceFramework {\n id\n rank\n }\n }\n}\n"
}
};
})();
(node as any).hash = "a60d166f55bbe09a53349b400355ffc0";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<18bb7ea1b507c374c33d9f4f7a56db2d>>
* @generated SignedSource<<cf316aa4cda5fc4c9aebae8f0f10c365>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -17,6 +17,7 @@ export type CompliancePageOverviewPageQuery$data = {
readonly organization: {
readonly compliancePage?: {
readonly canGetNDA: boolean;
readonly " $fragmentSpreads": FragmentRefs<"CompliancePageFrameworkList_compliancePageFragment">;
} | null | undefined;
readonly " $fragmentSpreads": FragmentRefs<"CompliancePageNDASectionFragment" | "CompliancePageSlackSectionFragment" | "CompliancePageStatusSectionFragment">;
};
@@ -55,12 +56,35 @@ v2 = {
"storageKey": "permission(action:\"core:trust-center:get-nda\")"
},
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v4 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
};
},
v5 = {
"kind": "Literal",
"name": "first",
"value": 100
},
v6 = [
(v5/*: any*/),
{
"kind": "Literal",
"name": "orderBy",
"value": {
"direction": "ASC",
"field": "RANK"
}
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
@@ -87,7 +111,12 @@ return {
"name": "trustCenter",
"plural": false,
"selections": [
(v2/*: any*/)
(v2/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
"name": "CompliancePageFrameworkList_compliancePageFragment"
}
],
"storageKey": null
}
@@ -131,13 +160,7 @@ return {
"name": "node",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
(v3/*: any*/),
{
"kind": "InlineFragment",
"selections": [
@@ -150,14 +173,7 @@ return {
"plural": false,
"selections": [
(v2/*: any*/),
(v3/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "active",
"storageKey": null
},
(v4/*: any*/),
{
"alias": "canUpdate",
"args": [
@@ -171,6 +187,150 @@ return {
"name": "permission",
"storageKey": "permission(action:\"core:trust-center:update\")"
},
{
"alias": null,
"args": (v6/*: any*/),
"concreteType": "ComplianceFrameworkConnection",
"kind": "LinkedField",
"name": "complianceFrameworks",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFrameworkEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFramework",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v4/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "rank",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "visibility",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "Framework",
"kind": "LinkedField",
"name": "framework",
"plural": false,
"selections": [
(v4/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "lightLogoURL",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "darkLogoURL",
"storageKey": null
}
],
"storageKey": null
},
(v3/*: 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": "endCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
}
],
"storageKey": null
},
{
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
}
],
"storageKey": "complianceFrameworks(first:100,orderBy:{\"direction\":\"ASC\",\"field\":\"RANK\"})"
},
{
"alias": null,
"args": (v6/*: any*/),
"filters": [
"orderBy"
],
"handle": "connection",
"key": "CompliancePageFrameworkList_complianceFrameworks",
"kind": "LinkedHandle",
"name": "complianceFrameworks"
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "active",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -229,18 +389,14 @@ return {
"name": "domain",
"storageKey": null
},
(v3/*: any*/)
(v4/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": [
{
"kind": "Literal",
"name": "first",
"value": 100
}
(v5/*: any*/)
],
"concreteType": "SlackConnectionConnection",
"kind": "LinkedField",
@@ -263,7 +419,7 @@ return {
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/),
(v4/*: any*/),
{
"alias": null,
"args": null,
@@ -291,23 +447,23 @@ return {
"type": "Organization",
"abstractKey": null
},
(v3/*: any*/)
(v4/*: any*/)
],
"storageKey": null
}
]
},
"params": {
"cacheID": "0041132d3ac46a3193d9acbd6f27bdf5",
"cacheID": "e6cc68fe4df4a8dffc558f6db82ead9c",
"id": null,
"metadata": {},
"name": "CompliancePageOverviewPageQuery",
"operationKind": "query",
"text": "query CompliancePageOverviewPageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n compliancePage: trustCenter {\n canGetNDA: permission(action: \"core:trust-center:get-nda\")\n id\n }\n }\n ...CompliancePageStatusSectionFragment\n ...CompliancePageNDASectionFragment\n ...CompliancePageSlackSectionFragment\n id\n }\n}\n\nfragment CompliancePageNDASectionFragment on Organization {\n compliancePage: trustCenter {\n id\n ndaFileName\n ndaFileUrl\n canUpdate: permission(action: \"core:trust-center:update\")\n canUploadNDA: permission(action: \"core:trust-center:upload-nda\")\n canDeleteNDA: permission(action: \"core:trust-center:delete-nda\")\n }\n}\n\nfragment CompliancePageSlackSectionFragment on Organization {\n compliancePage: trustCenter {\n canUpdate: permission(action: \"core:trust-center:update\")\n id\n }\n slackConnections(first: 100) {\n edges {\n node {\n id\n channel\n createdAt\n }\n }\n }\n}\n\nfragment CompliancePageStatusSectionFragment on Organization {\n customDomain {\n domain\n id\n }\n compliancePage: trustCenter {\n id\n active\n canUpdate: permission(action: \"core:trust-center:update\")\n }\n}\n"
"text": "query CompliancePageOverviewPageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n compliancePage: trustCenter {\n canGetNDA: permission(action: \"core:trust-center:get-nda\")\n ...CompliancePageFrameworkList_compliancePageFragment\n id\n }\n }\n ...CompliancePageStatusSectionFragment\n ...CompliancePageNDASectionFragment\n ...CompliancePageSlackSectionFragment\n id\n }\n}\n\nfragment CompliancePageFrameworkList_compliancePageFragment on TrustCenter {\n id\n canUpdate: permission(action: \"core:trust-center:update\")\n complianceFrameworks(first: 100, orderBy: {field: RANK, direction: ASC}) {\n edges {\n node {\n id\n rank\n visibility\n framework {\n id\n name\n lightLogoURL\n darkLogoURL\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment CompliancePageNDASectionFragment on Organization {\n compliancePage: trustCenter {\n id\n ndaFileName\n ndaFileUrl\n canUpdate: permission(action: \"core:trust-center:update\")\n canUploadNDA: permission(action: \"core:trust-center:upload-nda\")\n canDeleteNDA: permission(action: \"core:trust-center:delete-nda\")\n }\n}\n\nfragment CompliancePageSlackSectionFragment on Organization {\n compliancePage: trustCenter {\n canUpdate: permission(action: \"core:trust-center:update\")\n id\n }\n slackConnections(first: 100) {\n edges {\n node {\n id\n channel\n createdAt\n }\n }\n }\n}\n\nfragment CompliancePageStatusSectionFragment on Organization {\n customDomain {\n domain\n id\n }\n compliancePage: trustCenter {\n id\n active\n canUpdate: permission(action: \"core:trust-center:update\")\n }\n}\n"
}
};
})();
(node as any).hash = "689133b7f1c380266e6f15a60c527c91";
(node as any).hash = "be0dff83941c21755ea2ff77a865805b";
export default node;

View File

@@ -1,8 +1,10 @@
import { useTranslate } from "@probo/i18n";
import { type PreloadedQuery, usePreloadedQuery } from "react-relay";
import { graphql } from "relay-runtime";
import type { CompliancePageOverviewPageQuery } from "#/__generated__/core/CompliancePageOverviewPageQuery.graphql";
import { CompliancePageFrameworkList } from "./_components/CompliancePageFrameworkList";
import { CompliancePageNDASection } from "./_components/CompliancePageNDASection";
import { CompliancePageSlackSection } from "./_components/CompliancePageSlackSection";
import { CompliancePageStatusSection } from "./_components/CompliancePageStatusSection";
@@ -13,6 +15,7 @@ export const compliancePageOverviewPageQuery = graphql`
... on Organization {
compliancePage: trustCenter {
canGetNDA: permission(action: "core:trust-center:get-nda")
...CompliancePageFrameworkList_compliancePageFragment
}
}
...CompliancePageStatusSectionFragment
@@ -25,6 +28,8 @@ export const compliancePageOverviewPageQuery = graphql`
export function CompliancePageOverviewPage(props: { queryRef: PreloadedQuery<CompliancePageOverviewPageQuery> }) {
const { queryRef } = props;
const { __ } = useTranslate();
const { organization } = usePreloadedQuery<CompliancePageOverviewPageQuery>(
compliancePageOverviewPageQuery,
queryRef,
@@ -39,6 +44,18 @@ export function CompliancePageOverviewPage(props: { queryRef: PreloadedQuery<Com
)}
<CompliancePageSlackSection fragmentRef={organization} />
{organization.compliancePage && (
<div className="space-y-4">
<div>
<h3 className="text-base font-medium">{__("Frameworks")}</h3>
<p className="text-sm text-txt-tertiary">
{__("Select which frameworks to display as badges on your compliance page")}
</p>
</div>
<CompliancePageFrameworkList compliancePageRef={organization.compliancePage} />
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,358 @@
import { useTranslate } from "@probo/i18n";
import { Badge, Field, FrameworkLogo, Option, Table, Tbody, Td, Th, Thead, Tr } from "@probo/ui";
import { useCallback, useState, useTransition } from "react";
import { useRefetchableFragment } from "react-relay";
import { ConnectionHandler, graphql } from "relay-runtime";
import type { CompliancePageFrameworkList_compliancePageFragment$key } from "#/__generated__/core/CompliancePageFrameworkList_compliancePageFragment.graphql";
import type { CompliancePageFrameworkList_compliancePageFragment$data } from "#/__generated__/core/CompliancePageFrameworkList_compliancePageFragment.graphql";
import type { CompliancePageFrameworkList_compliancePageRefetchQuery } from "#/__generated__/core/CompliancePageFrameworkList_compliancePageRefetchQuery.graphql";
import type { CompliancePageFrameworkList_createMutation } from "#/__generated__/core/CompliancePageFrameworkList_createMutation.graphql";
import type { CompliancePageFrameworkList_deleteMutation } from "#/__generated__/core/CompliancePageFrameworkList_deleteMutation.graphql";
import type { CompliancePageFrameworkList_updateRankMutation } from "#/__generated__/core/CompliancePageFrameworkList_updateRankMutation.graphql";
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
const compliancePageFragment = graphql`
fragment CompliancePageFrameworkList_compliancePageFragment on TrustCenter
@refetchable(queryName: "CompliancePageFrameworkList_compliancePageRefetchQuery")
@argumentDefinitions(
first: { type: Int, defaultValue: 100 }
after: { type: CursorKey, defaultValue: null }
order: { type: ComplianceFrameworkOrder, defaultValue: { field: RANK, direction: ASC } }
) {
id
canUpdate: permission(action: "core:trust-center:update")
complianceFrameworks(first: $first, after: $after, orderBy: $order)
@connection(key: "CompliancePageFrameworkList_complianceFrameworks", filters: ["orderBy"]) {
__id
edges {
node {
id
rank
visibility
framework {
id
name
lightLogoURL
darkLogoURL
}
}
}
}
}
`;
const createMutation = graphql`
mutation CompliancePageFrameworkList_createMutation($input: CreateComplianceFrameworkInput!) {
createComplianceFramework(input: $input) {
complianceFrameworkEdge {
node {
id
}
}
}
}
`;
const updateRankMutation = graphql`
mutation CompliancePageFrameworkList_updateRankMutation($input: UpdateComplianceFrameworkInput!) {
updateComplianceFramework(input: $input) {
complianceFramework {
id
rank
}
}
}
`;
const deleteMutation = graphql`
mutation CompliancePageFrameworkList_deleteMutation($input: DeleteComplianceFrameworkInput!) {
deleteComplianceFramework(input: $input) {
deletedComplianceFrameworkId
}
}
`;
type Edge = CompliancePageFrameworkList_compliancePageFragment$data["complianceFrameworks"]["edges"][number];
function CompliancePageFrameworkListItem(props: {
edge: Edge;
compliancePage: CompliancePageFrameworkList_compliancePageFragment$data;
draggedCfId: string | null;
dragOverCfId: string | null;
onDragStart: (cfId: string) => void;
onDragEnd: () => void;
onDragOver: (e: React.DragEvent, cfId: string) => void;
onDrop: (cfId: string) => void;
onRefetch: () => void;
}) {
const { edge, draggedCfId, dragOverCfId, onDragStart, onDragEnd, onDragOver, onDrop, onRefetch } = props;
const { __ } = useTranslate();
const [isMouseDown, setIsMouseDown] = useState(false);
const compliancePage = props.compliancePage;
const { id, visibility, framework } = edge.node;
const isPublic = visibility === "PUBLIC";
const canDrag = isPublic && compliancePage.canUpdate;
const isDragging = draggedCfId === id;
const isDropTarget = dragOverCfId === id && draggedCfId !== id;
const [createComplianceFramework, isCreating] = useMutationWithToasts<CompliancePageFrameworkList_createMutation>(
createMutation,
{
successMessage: __("Framework visibility updated successfully."),
errorMessage: __("Failed to update framework visibility"),
},
);
const [deleteComplianceFramework, isDeleting] = useMutationWithToasts<CompliancePageFrameworkList_deleteMutation>(
deleteMutation,
{
successMessage: __("Framework visibility updated successfully."),
errorMessage: __("Failed to update framework visibility"),
},
);
const isLoading = isCreating || isDeleting;
const handleVisibilityChange = useCallback(
async (value: string) => {
if (value === "PUBLIC" && !isPublic) {
await createComplianceFramework({
variables: {
input: {
trustCenterId: compliancePage.id,
frameworkId: framework.id,
},
},
onCompleted: (_, errors) => {
if (!errors?.length) {
onRefetch();
}
},
});
} else if (value === "NONE" && isPublic) {
await deleteComplianceFramework({
variables: {
input: { id },
},
onCompleted: (_, errors) => {
if (!errors?.length) {
onRefetch();
}
},
});
}
},
[compliancePage.id, framework.id, id, isPublic, onRefetch, createComplianceFramework, deleteComplianceFramework],
);
const visibilityOptions = [
{ value: "PUBLIC", label: __("Public"), variant: "success" as const },
{ value: "NONE", label: __("None"), variant: "neutral" as const },
];
const rowClassName = [
canDrag && isDragging && "opacity-50 cursor-grabbing",
canDrag && !isDragging && !isMouseDown && "cursor-grab",
canDrag && !isDragging && isMouseDown && "cursor-grabbing",
isDropTarget && "!bg-primary-50 border-y-2 border-primary-500",
]
.filter(Boolean)
.join(" ");
return (
<Tr
draggable={canDrag}
onDragStart={canDrag ? () => onDragStart(id) : undefined}
onDragEnd={canDrag ? onDragEnd : undefined}
onDragOver={canDrag ? e => onDragOver(e, id) : undefined}
onDrop={
canDrag
? (e) => {
e.preventDefault();
onDrop(id);
}
: undefined
}
onMouseDown={canDrag ? () => setIsMouseDown(true) : undefined}
onMouseUp={canDrag ? () => setIsMouseDown(false) : undefined}
onMouseLeave={canDrag ? () => setIsMouseDown(false) : undefined}
className={rowClassName}
>
<Td>
<div className="flex items-center gap-3">
<FrameworkLogo
className="size-8"
lightLogoURL={framework.lightLogoURL}
darkLogoURL={framework.darkLogoURL}
name={framework.name}
/>
{framework.name}
</div>
</Td>
<Td noLink width={130} className="pr-0">
<Field
type="select"
value={visibility}
onValueChange={value => void handleVisibilityChange(value)}
disabled={isLoading || !compliancePage.canUpdate}
className="w-[105px]"
>
{visibilityOptions.map(option => (
<Option key={option.value} value={option.value}>
<div className="flex items-center justify-between w-full">
<Badge variant={option.variant}>{option.label}</Badge>
</div>
</Option>
))}
</Field>
</Td>
</Tr>
);
}
export function CompliancePageFrameworkList(props: {
compliancePageRef: CompliancePageFrameworkList_compliancePageFragment$key;
}) {
const { __ } = useTranslate();
const [, startTransition] = useTransition();
const [compliancePage, refetch] = useRefetchableFragment<
CompliancePageFrameworkList_compliancePageRefetchQuery,
CompliancePageFrameworkList_compliancePageFragment$key
>(compliancePageFragment, props.compliancePageRef);
const [updateRank] = useMutationWithToasts<CompliancePageFrameworkList_updateRankMutation>(
updateRankMutation,
{
successMessage: __("Order updated successfully"),
errorMessage: __("Failed to update order"),
},
);
const [draggedCfId, setDraggedCfId] = useState<string | null>(null);
const [dragOverCfId, setDragOverCfId] = useState<string | null>(null);
const allEdges = compliancePage.complianceFrameworks.edges;
const publicEdges = allEdges.filter(e => e.node.visibility === "PUBLIC");
const handleDragStart = (cfId: string) => {
setDraggedCfId(cfId);
};
const handleDragEnd = () => {
setDraggedCfId(null);
setDragOverCfId(null);
};
const handleDragOver = (e: React.DragEvent, cfId: string) => {
e.preventDefault();
if (draggedCfId !== cfId) {
setDragOverCfId(cfId);
}
};
const handleDrop = async (targetCfId: string) => {
if (!draggedCfId || draggedCfId === targetCfId) {
setDraggedCfId(null);
setDragOverCfId(null);
return;
}
const targetEdge = publicEdges.find(e => e.node.id === targetCfId);
if (!targetEdge) {
setDraggedCfId(null);
setDragOverCfId(null);
return;
}
const draggedId = draggedCfId;
await updateRank({
variables: {
input: {
id: draggedId,
rank: targetEdge.node.rank,
},
},
updater: (store) => {
const trustCenterRecord = store.get(compliancePage.id);
if (!trustCenterRecord) return;
const connection = ConnectionHandler.getConnection(
trustCenterRecord,
"CompliancePageFrameworkList_complianceFrameworks",
{ orderBy: { field: "RANK", direction: "ASC" } },
);
if (!connection) return;
const edges = connection.getLinkedRecords("edges");
if (!edges) return;
const fromIdx = edges.findIndex(e => e.getLinkedRecord("node")?.getDataID() === draggedId);
const toIdx = edges.findIndex(e => e.getLinkedRecord("node")?.getDataID() === targetCfId);
if (fromIdx === -1 || toIdx === -1) return;
const reordered = [...edges];
const [moved] = reordered.splice(fromIdx, 1);
reordered.splice(toIdx, 0, moved);
connection.setLinkedRecords(reordered, "edges");
},
onCompleted: (_, errors) => {
startTransition(() => {
refetch({}, { fetchPolicy: errors?.length ? "network-only" : "store-and-network" });
});
},
});
setDraggedCfId(null);
setDragOverCfId(null);
};
const hasMultiplePublic = publicEdges.length > 1;
return (
<>
<Table>
<Thead>
<Tr>
<Th>{__("Framework")}</Th>
<Th>{__("Visibility")}</Th>
</Tr>
</Thead>
<Tbody>
{allEdges.length === 0 && (
<Tr>
<Td colSpan={2} className="text-center text-txt-secondary">
{__("No frameworks available")}
</Td>
</Tr>
)}
{allEdges.map(edge => (
<CompliancePageFrameworkListItem
key={edge.node.id}
edge={edge}
compliancePage={compliancePage}
draggedCfId={draggedCfId}
dragOverCfId={dragOverCfId}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onDragOver={handleDragOver}
onDrop={cfId => void handleDrop(cfId)}
onRefetch={() => startTransition(() => { refetch({}, { fetchPolicy: "store-and-network" }); })}
/>
))}
</Tbody>
</Table>
{hasMultiplePublic && compliancePage.canUpdate && (
<p className="text-sm text-txt-tertiary">
{__("Drag and drop public frameworks to change their displayed order")}
</p>
)}
</>
);
}

View File

@@ -126,7 +126,7 @@ export function CompliancePageReferenceList(props: {
</Tbody>
</Table>
<p className="text-xs text-txt-tertiary">
<p className="text-sm text-txt-tertiary">
{__("Drag and drop references to change their displayed order")}
</p>
</>

View File

@@ -0,0 +1,32 @@
import { FrameworkLogo } from "@probo/ui";
import { useFragment } from "react-relay";
import { graphql } from "relay-runtime";
import type { FrameworkBadgeFragment$key } from "./__generated__/FrameworkBadgeFragment.graphql";
const frameworkFragment = graphql`
fragment FrameworkBadgeFragment on Framework {
id
name
lightLogoURL
darkLogoURL
}
`;
export function FrameworkBadge(props: { framework: FrameworkBadgeFragment$key }) {
const framework = useFragment(frameworkFragment, props.framework);
return (
<div className="flex flex-col gap-2 items-center w-19">
<FrameworkLogo
className="size-19"
lightLogoURL={framework.lightLogoURL}
darkLogoURL={framework.darkLogoURL}
name={framework.name}
/>
<div className="txt-primary text-sm max-w-19 overflow-hidden min-w-0 whitespace-nowrap text-ellipsis">
{framework.name}
</div>
</div>
);
}

View File

@@ -19,7 +19,7 @@ import type { TrustGraphCurrentQuery$data } from "#/queries/__generated__/TrustG
import { getPathPrefix } from "#/utils/pathPrefix";
import type { OrganizationSidebar_requestAllAccessesMutation } from "./__generated__/OrganizationSidebar_requestAllAccessesMutation.graphql";
import { AuditRowAvatar } from "./AuditRow";
import { FrameworkBadge } from "./FrameworkBadge";
const requestAllAccessesMutation = graphql`
mutation OrganizationSidebar_requestAllAccessesMutation {
@@ -154,7 +154,7 @@ export function OrganizationSidebar({
<hr className="my-6 -mx-6 h-px bg-border-low border-none" />
{/* Certifications */}
{trustCenter.audits.edges.length > 0 && (
{trustCenter.complianceFrameworks.edges.length > 0 && (
<>
<div className="space-y-4">
<h2 className="text-xs text-txt-secondary flex gap-1 items-center">
@@ -167,8 +167,8 @@ export function OrganizationSidebar({
gridTemplateColumns: "repeat(auto-fit, 75px",
}}
>
{trustCenter.audits.edges.map(audit => (
<AuditRowAvatar key={audit.node.id} audit={audit.node} />
{trustCenter.complianceFrameworks.edges.map(edge => (
<FrameworkBadge key={edge.node.id} framework={edge.node.framework} />
))}
</div>
</div>

View File

@@ -0,0 +1,66 @@
/**
* @generated SignedSource<<f13ba91513aa9a472ab62c4c6a93b6bb>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type FrameworkBadgeFragment$data = {
readonly darkLogoURL: string | null | undefined;
readonly id: string;
readonly lightLogoURL: string | null | undefined;
readonly name: string;
readonly " $fragmentType": "FrameworkBadgeFragment";
};
export type FrameworkBadgeFragment$key = {
readonly " $data"?: FrameworkBadgeFragment$data;
readonly " $fragmentSpreads": FragmentRefs<"FrameworkBadgeFragment">;
};
const node: ReaderFragment = {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "FrameworkBadgeFragment",
"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": "lightLogoURL",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "darkLogoURL",
"storageKey": null
}
],
"type": "Framework",
"abstractKey": null
};
(node as any).hash = "cdfaf9a4a0e2cb667a5ef8d3e7db1f99";
export default node;

View File

@@ -34,6 +34,16 @@ export const currentTrustGraphQuery = graphql`
}
}
}
complianceFrameworks(first: 50) {
edges {
node {
id
framework {
...FrameworkBadgeFragment
}
}
}
}
}
}
`;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<47a6e80d3532b4c93d18495c2be7676d>>
* @generated SignedSource<<4eef287e3e3152affa1b16189a504c36>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -22,6 +22,16 @@ export type TrustGraphCurrentQuery$data = {
};
}>;
};
readonly complianceFrameworks: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly framework: {
readonly " $fragmentSpreads": FragmentRefs<"FrameworkBadgeFragment">;
};
readonly id: string;
};
}>;
};
readonly darkLogoFileUrl: string | null | undefined;
readonly id: string;
readonly logoFileUrl: string | null | undefined;
@@ -193,6 +203,33 @@ v16 = {
(v6/*: any*/)
],
"storageKey": null
},
v17 = {
"alias": null,
"args": null,
"concreteType": "Framework",
"kind": "LinkedField",
"name": "framework",
"plural": false,
"selections": [
(v0/*: any*/),
(v7/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "lightLogoURL",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "darkLogoURL",
"storageKey": null
}
],
"storageKey": null
};
return {
"fragment": {
@@ -300,6 +337,56 @@ return {
}
],
"storageKey": "audits(first:50)"
},
{
"alias": null,
"args": (v13/*: any*/),
"concreteType": "ComplianceFrameworkConnection",
"kind": "LinkedField",
"name": "complianceFrameworks",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFrameworkEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFramework",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v0/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "Framework",
"kind": "LinkedField",
"name": "framework",
"plural": false,
"selections": [
{
"args": null,
"kind": "FragmentSpread",
"name": "FrameworkBadgeFragment"
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": "complianceFrameworks(first:50)"
}
],
"storageKey": null
@@ -612,33 +699,7 @@ return {
],
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "Framework",
"kind": "LinkedField",
"name": "framework",
"plural": false,
"selections": [
(v0/*: any*/),
(v7/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "lightLogoURL",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "darkLogoURL",
"storageKey": null
}
],
"storageKey": null
}
(v17/*: any*/)
],
"storageKey": null
}
@@ -647,6 +708,41 @@ return {
}
],
"storageKey": "audits(first:50)"
},
{
"alias": null,
"args": (v13/*: any*/),
"concreteType": "ComplianceFrameworkConnection",
"kind": "LinkedField",
"name": "complianceFrameworks",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFrameworkEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "ComplianceFramework",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v0/*: any*/),
(v17/*: any*/)
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": "complianceFrameworks(first:50)"
}
],
"storageKey": null
@@ -654,16 +750,16 @@ return {
]
},
"params": {
"cacheID": "42b477429f092f97b0f444433a78c6d1",
"cacheID": "e64b984bdcf5a5fb2f19e69c6205ba9a",
"id": null,
"metadata": {},
"name": "TrustGraphCurrentQuery",
"operationKind": "query",
"text": "query TrustGraphCurrentQuery {\n currentTrustCenter {\n id\n slug\n logoFileUrl\n darkLogoFileUrl\n nonDisclosureAgreement {\n fileName\n fileUrl\n viewerSignature {\n status\n id\n }\n }\n organization {\n name\n description\n websiteUrl\n email\n headquarterAddress\n id\n }\n ...OverviewPageFragment\n vendorInfo: vendors(first: 0) {\n totalCount\n }\n audits(first: 50) {\n edges {\n node {\n id\n ...AuditRowFragment\n }\n }\n }\n }\n}\n\nfragment AuditRowFragment on Audit {\n name\n report {\n id\n filename\n isUserAuthorized\n access {\n id\n status\n }\n }\n framework {\n id\n name\n lightLogoURL\n darkLogoURL\n }\n}\n\nfragment DocumentRowFragment on Document {\n id\n title\n isUserAuthorized\n access {\n id\n status\n }\n}\n\nfragment OverviewPageFragment on TrustCenter {\n references(first: 14) {\n edges {\n node {\n id\n name\n logoUrl\n websiteUrl\n }\n }\n }\n vendors(first: 3) {\n edges {\n node {\n id\n countries\n ...VendorRowFragment\n }\n }\n }\n documents(first: 5) {\n edges {\n node {\n id\n ...DocumentRowFragment\n documentType\n }\n }\n }\n trustCenterFiles(first: 5) {\n edges {\n node {\n id\n category\n ...TrustCenterFileRowFragment\n }\n }\n }\n}\n\nfragment TrustCenterFileRowFragment on TrustCenterFile {\n id\n name\n isUserAuthorized\n access {\n id\n status\n }\n}\n\nfragment VendorRowFragment on Vendor {\n name\n description\n websiteUrl\n countries\n}\n"
"text": "query TrustGraphCurrentQuery {\n currentTrustCenter {\n id\n slug\n logoFileUrl\n darkLogoFileUrl\n nonDisclosureAgreement {\n fileName\n fileUrl\n viewerSignature {\n status\n id\n }\n }\n organization {\n name\n description\n websiteUrl\n email\n headquarterAddress\n id\n }\n ...OverviewPageFragment\n vendorInfo: vendors(first: 0) {\n totalCount\n }\n audits(first: 50) {\n edges {\n node {\n id\n ...AuditRowFragment\n }\n }\n }\n complianceFrameworks(first: 50) {\n edges {\n node {\n id\n framework {\n ...FrameworkBadgeFragment\n id\n }\n }\n }\n }\n }\n}\n\nfragment AuditRowFragment on Audit {\n name\n report {\n id\n filename\n isUserAuthorized\n access {\n id\n status\n }\n }\n framework {\n id\n name\n lightLogoURL\n darkLogoURL\n }\n}\n\nfragment DocumentRowFragment on Document {\n id\n title\n isUserAuthorized\n access {\n id\n status\n }\n}\n\nfragment FrameworkBadgeFragment on Framework {\n id\n name\n lightLogoURL\n darkLogoURL\n}\n\nfragment OverviewPageFragment on TrustCenter {\n references(first: 14) {\n edges {\n node {\n id\n name\n logoUrl\n websiteUrl\n }\n }\n }\n vendors(first: 3) {\n edges {\n node {\n id\n countries\n ...VendorRowFragment\n }\n }\n }\n documents(first: 5) {\n edges {\n node {\n id\n ...DocumentRowFragment\n documentType\n }\n }\n }\n trustCenterFiles(first: 5) {\n edges {\n node {\n id\n category\n ...TrustCenterFileRowFragment\n }\n }\n }\n}\n\nfragment TrustCenterFileRowFragment on TrustCenterFile {\n id\n name\n isUserAuthorized\n access {\n id\n status\n }\n}\n\nfragment VendorRowFragment on Vendor {\n name\n description\n websiteUrl\n countries\n}\n"
}
};
})();
(node as any).hash = "41cf8e5383cefdd5b3b7b85263eccb1f";
(node as any).hash = "d1ce7e38b42fad48e9f38bf00bb34d56";
export default node;

View File

@@ -0,0 +1,428 @@
// 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"
"errors"
"fmt"
"maps"
"time"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
ComplianceFramework struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
TrustCenterID gid.GID `db:"trust_center_id"`
FrameworkID gid.GID `db:"framework_id"`
Rank int `db:"rank"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
// Visibility is a non-db field used to return all frameworks for a trust center, including hidden ones.
Visibility ComplianceFrameworkVisibility `db:"visibility"`
}
ComplianceFrameworks []*ComplianceFramework
)
func (c ComplianceFramework) CursorKey(orderBy ComplianceFrameworkOrderField) page.CursorKey {
switch orderBy {
case ComplianceFrameworkOrderFieldCreatedAt:
return page.NewCursorKey(c.ID, c.CreatedAt)
case ComplianceFrameworkOrderFieldRank:
return page.NewCursorKey(c.ID, c.Rank)
}
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (c *ComplianceFramework) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
q := `SELECT organization_id FROM compliance_frameworks WHERE id = $1 LIMIT 1;`
var organizationID gid.GID
if err := conn.QueryRow(ctx, q, c.ID).Scan(&organizationID); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrResourceNotFound
}
return nil, fmt.Errorf("cannot query compliance framework authorization attributes: %w", err)
}
return map[string]string{"organization_id": organizationID.String()}, nil
}
func (c *ComplianceFramework) LoadByID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
complianceFrameworkID gid.GID,
) error {
q := `
SELECT
id,
organization_id,
trust_center_id,
framework_id,
rank,
'PUBLIC' AS visibility,
created_at,
updated_at
FROM
compliance_frameworks
WHERE
%s
AND id = @compliance_framework_id
LIMIT 1;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"compliance_framework_id": complianceFrameworkID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query compliance_frameworks: %w", err)
}
cf, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ComplianceFramework])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrResourceNotFound
}
return fmt.Errorf("cannot collect compliance framework: %w", err)
}
*c = cf
return nil
}
func (c *ComplianceFramework) LoadByTrustCenterIDAndFrameworkID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
trustCenterID gid.GID,
frameworkID gid.GID,
) error {
q := `
SELECT
id,
organization_id,
trust_center_id,
framework_id,
rank,
'PUBLIC' AS visibility,
created_at,
updated_at
FROM
compliance_frameworks
WHERE
%s
AND trust_center_id = @trust_center_id
AND framework_id = @framework_id
LIMIT 1;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"trust_center_id": trustCenterID,
"framework_id": frameworkID,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query compliance_frameworks: %w", err)
}
cf, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[ComplianceFramework])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrResourceNotFound
}
return fmt.Errorf("cannot collect compliance framework: %w", err)
}
*c = cf
return nil
}
func (c *ComplianceFramework) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO
compliance_frameworks (
id,
tenant_id,
organization_id,
trust_center_id,
framework_id,
rank,
created_at,
updated_at
)
VALUES (
@id,
@tenant_id,
@organization_id,
@trust_center_id,
@framework_id,
(SELECT COALESCE(MAX(rank), 0) + 1 FROM compliance_frameworks WHERE trust_center_id = @trust_center_id),
@created_at,
@updated_at
)
RETURNING rank;
`
args := pgx.StrictNamedArgs{
"id": c.ID,
"tenant_id": scope.GetTenantID(),
"organization_id": c.OrganizationID,
"trust_center_id": c.TrustCenterID,
"framework_id": c.FrameworkID,
"created_at": c.CreatedAt,
"updated_at": c.UpdatedAt,
}
err := conn.QueryRow(ctx, q, args).Scan(&c.Rank)
if err != nil {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) {
if pgErr.Code == "23505" {
return ErrResourceAlreadyExists
}
}
return fmt.Errorf("cannot insert compliance framework: %w", err)
}
return nil
}
func (c *ComplianceFramework) UpdateRank(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
WITH old AS (
SELECT
rank AS old_rank
FROM compliance_frameworks
WHERE %s AND id = @id AND trust_center_id = @trust_center_id
)
UPDATE compliance_frameworks
SET
rank = CASE
WHEN id = @id THEN @new_rank
ELSE rank + CASE
WHEN @new_rank < old.old_rank THEN 1
WHEN @new_rank > old.old_rank THEN -1
END
END,
updated_at = @updated_at
FROM old
WHERE %s
AND (
id = @id
OR (rank BETWEEN LEAST(old.old_rank, @new_rank) AND GREATEST(old.old_rank, @new_rank))
);
`
scopeFragment := scope.SQLFragment()
q = fmt.Sprintf(q, scopeFragment, scopeFragment)
args := pgx.StrictNamedArgs{
"id": c.ID,
"new_rank": c.Rank,
"trust_center_id": c.TrustCenterID,
"updated_at": c.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot update compliance framework rank: %w", err)
}
return nil
}
func (c *ComplianceFramework) Delete(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
DELETE FROM
compliance_frameworks
WHERE
%s
AND id = @id;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"id": c.ID}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot delete compliance framework: %w", err)
}
return nil
}
func (c *ComplianceFrameworks) LoadByTrustCenterID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
trustCenterID gid.GID,
cursor *page.Cursor[ComplianceFrameworkOrderField],
) error {
q := `
SELECT
id,
organization_id,
trust_center_id,
framework_id,
rank,
'PUBLIC' AS visibility,
created_at,
updated_at
FROM
compliance_frameworks
WHERE
%s
AND trust_center_id = @trust_center_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.NamedArgs{"trust_center_id": trustCenterID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query compliance_frameworks: %w", err)
}
results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ComplianceFramework])
if err != nil {
return fmt.Errorf("cannot collect compliance frameworks: %w", err)
}
*c = results
return nil
}
func (c *ComplianceFrameworks) LoadWithHiddenByTrustCenterID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
trustCenterID gid.GID,
cursor *page.Cursor[ComplianceFrameworkOrderField],
) error {
q := `
WITH combined AS (
SELECT
COALESCE(cf.id, f.id) AS id,
COALESCE(cf.organization_id, tc.organization_id) AS organization_id,
COALESCE(cf.trust_center_id, tc.id) AS trust_center_id,
f.id AS framework_id,
CASE
WHEN cf.id IS NOT NULL THEN cf.rank
ELSE COALESCE(MAX(cf.rank) OVER (), 0) + ROW_NUMBER() OVER (PARTITION BY (cf.id IS NULL) ORDER BY f.created_at)
END AS rank,
CASE WHEN cf.id IS NULL THEN 'NONE' ELSE 'PUBLIC' END AS visibility,
COALESCE(cf.created_at, f.created_at) AS created_at,
COALESCE(cf.updated_at, f.updated_at) AS updated_at
FROM trust_centers tc
JOIN frameworks f
ON f.organization_id = tc.organization_id
AND f.tenant_id = @tenant_id
LEFT JOIN compliance_frameworks cf
ON cf.framework_id = f.id
AND cf.trust_center_id = tc.id
AND cf.tenant_id = @tenant_id
WHERE tc.id = @trust_center_id
AND tc.tenant_id = @tenant_id
)
SELECT id, organization_id, trust_center_id, framework_id, rank, visibility, created_at, updated_at
FROM combined
WHERE %s
`
q = fmt.Sprintf(q, cursor.SQLFragment())
args := pgx.NamedArgs{"trust_center_id": trustCenterID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query compliance frameworks with hidden: %w", err)
}
results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ComplianceFramework])
if err != nil {
return fmt.Errorf("cannot collect compliance frameworks with hidden: %w", err)
}
*c = results
return nil
}
func (c *ComplianceFrameworks) CountByTrustCenterID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
trustCenterID gid.GID,
) (int, error) {
q := `
SELECT
COUNT(*)
FROM
compliance_frameworks
WHERE
%s
AND trust_center_id = @trust_center_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"trust_center_id": trustCenterID}
maps.Copy(args, scope.SQLArguments())
var count int
err := conn.QueryRow(ctx, q, args).Scan(&count)
if err != nil {
return 0, fmt.Errorf("cannot count compliance frameworks: %w", err)
}
return count, nil
}

View File

@@ -0,0 +1,48 @@
// 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 (
ComplianceFrameworkOrderField string
)
const (
ComplianceFrameworkOrderFieldCreatedAt ComplianceFrameworkOrderField = "CREATED_AT"
ComplianceFrameworkOrderFieldRank ComplianceFrameworkOrderField = "RANK"
)
func (p ComplianceFrameworkOrderField) Column() string {
switch p {
case ComplianceFrameworkOrderFieldCreatedAt:
return "created_at"
case ComplianceFrameworkOrderFieldRank:
return "rank"
default:
return string(p)
}
}
func (p ComplianceFrameworkOrderField) String() string {
return string(p)
}
func (p ComplianceFrameworkOrderField) MarshalText() ([]byte, error) {
return []byte(p.String()), nil
}
func (p *ComplianceFrameworkOrderField) UnmarshalText(text []byte) error {
*p = ComplianceFrameworkOrderField(text)
return nil
}

View File

@@ -0,0 +1,26 @@
// 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 ComplianceFrameworkVisibility string
const (
ComplianceFrameworkVisibilityNone ComplianceFrameworkVisibility = "NONE"
ComplianceFrameworkVisibilityPublic ComplianceFrameworkVisibility = "PUBLIC"
)
func (v ComplianceFrameworkVisibility) String() string {
return string(v)
}

View File

@@ -85,6 +85,7 @@ const (
ElectronicSignatureEntityType uint16 = 59
ElectronicSignatureEventEntityType uint16 = 60
EmailAttachmentEntityType uint16 = 61
ComplianceFrameworkEntityType uint16 = 62
)
func NewEntityFromID(id gid.GID) (any, bool) {
@@ -209,6 +210,8 @@ func NewEntityFromID(id gid.GID) (any, bool) {
return &ElectronicSignatureEvent{ID: id}, true
case EmailAttachmentEntityType:
return &EmailAttachment{ID: id}, true
case ComplianceFrameworkEntityType:
return &ComplianceFramework{ID: id}, true
default:
return nil, false
}

View File

@@ -47,7 +47,7 @@ type (
func (f *Framework) CursorKey(orderBy FrameworkOrderField) page.CursorKey {
switch orderBy {
case FrameworkOrderFieldCreatedAt:
return page.CursorKey{ID: f.ID, Value: f.CreatedAt}
return page.NewCursorKey(f.ID, f.CreatedAt)
}
panic(fmt.Sprintf("unsupported order by: %s", orderBy))

View File

@@ -0,0 +1,54 @@
CREATE TABLE compliance_frameworks (
id TEXT NOT NULL PRIMARY KEY,
tenant_id TEXT NOT NULL,
organization_id TEXT NOT NULL REFERENCES organizations(id) ON UPDATE CASCADE ON DELETE CASCADE,
trust_center_id TEXT NOT NULL REFERENCES trust_centers(id) ON UPDATE CASCADE ON DELETE CASCADE,
framework_id TEXT NOT NULL REFERENCES frameworks(id) ON UPDATE CASCADE ON DELETE CASCADE,
rank INTEGER NOT NULL,
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
UNIQUE (trust_center_id, framework_id)
);
ALTER TABLE compliance_frameworks
ADD CONSTRAINT compliance_frameworks_trust_center_id_rank_key
UNIQUE (trust_center_id, rank)
DEFERRABLE INITIALLY DEFERRED;
INSERT INTO compliance_frameworks (
id,
tenant_id,
organization_id,
trust_center_id,
framework_id,
rank,
created_at,
updated_at
)
WITH distinct_frameworks AS (
SELECT DISTINCT
a.tenant_id,
a.organization_id,
a.framework_id
FROM audits a
WHERE a.trust_center_visibility IN ('PRIVATE', 'PUBLIC')
AND EXISTS (
SELECT 1 FROM trust_centers tc
WHERE tc.organization_id = a.organization_id
)
)
SELECT
generate_gid(decode_base64_unpadded(df.tenant_id), 62),
df.tenant_id,
df.organization_id,
tc.id AS trust_center_id,
df.framework_id,
ROW_NUMBER() OVER (
PARTITION BY df.organization_id
ORDER BY f.created_at ASC, df.framework_id ASC
) AS rank,
NOW(),
NOW()
FROM distinct_frameworks df
JOIN trust_centers tc ON tc.organization_id = df.organization_id
JOIN frameworks f ON f.id = df.framework_id;

View File

@@ -47,6 +47,12 @@ const (
ActionTrustCenterReferenceUpdate = "core:trust-center-reference:update"
ActionTrustCenterReferenceDelete = "core:trust-center-reference:delete"
// ComplianceFramework actions
ActionComplianceFrameworkList = "core:compliance-framework:list"
ActionComplianceFrameworkCreate = "core:compliance-framework:create"
ActionComplianceFrameworkDelete = "core:compliance-framework:delete"
ActionComplianceFrameworkUpdateRank = "core:compliance-framework:update-rank"
// TrustCenterFile actions
ActionTrustCenterFileGet = "core:trust-center-file:get"
ActionTrustCenterFileList = "core:trust-center-file:list"

View File

@@ -0,0 +1,204 @@
// 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"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/validator"
)
type (
ComplianceFrameworkService struct {
svc *TenantService
}
CreateComplianceFrameworkRequest struct {
TrustCenterID gid.GID
FrameworkID gid.GID
}
UpdateComplianceFrameworkRequest struct {
ID gid.GID
Rank int
}
DeleteComplianceFrameworkRequest struct {
ID gid.GID
}
)
func (r *CreateComplianceFrameworkRequest) Validate() error {
v := validator.New()
v.Check(r.TrustCenterID, "trust_center_id", validator.Required(), validator.GID(coredata.TrustCenterEntityType))
v.Check(r.FrameworkID, "framework_id", validator.Required(), validator.GID(coredata.FrameworkEntityType))
return v.Error()
}
func (r *UpdateComplianceFrameworkRequest) Validate() error {
v := validator.New()
v.Check(r.ID, "id", validator.Required(), validator.GID(coredata.ComplianceFrameworkEntityType))
return v.Error()
}
func (r *DeleteComplianceFrameworkRequest) Validate() error {
v := validator.New()
v.Check(r.ID, "id", validator.Required(), validator.GID(coredata.ComplianceFrameworkEntityType))
return v.Error()
}
func (s ComplianceFrameworkService) ListWithHiddenForTrustCenterID(
ctx context.Context,
trustCenterID gid.GID,
cursor *page.Cursor[coredata.ComplianceFrameworkOrderField],
) (*page.Page[*coredata.ComplianceFramework, coredata.ComplianceFrameworkOrderField], error) {
var cfs coredata.ComplianceFrameworks
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
if err := cfs.LoadWithHiddenByTrustCenterID(ctx, conn, s.svc.scope, trustCenterID, cursor); err != nil {
return fmt.Errorf("cannot load compliance frameworks with hidden: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return page.NewPage(cfs, cursor), nil
}
func (s ComplianceFrameworkService) Create(
ctx context.Context,
req *CreateComplianceFrameworkRequest,
) (*coredata.ComplianceFramework, error) {
if err := req.Validate(); err != nil {
return nil, err
}
now := time.Now()
cfID := gid.New(s.svc.scope.GetTenantID(), coredata.ComplianceFrameworkEntityType)
var cf *coredata.ComplianceFramework
err := s.svc.pg.WithTx(
ctx,
func(tx pg.Conn) error {
trustCenter := &coredata.TrustCenter{}
if err := trustCenter.LoadByID(ctx, tx, s.svc.scope, req.TrustCenterID); err != nil {
return fmt.Errorf("cannot load trust center: %w", err)
}
cf = &coredata.ComplianceFramework{
ID: cfID,
OrganizationID: trustCenter.OrganizationID,
TrustCenterID: req.TrustCenterID,
FrameworkID: req.FrameworkID,
CreatedAt: now,
UpdatedAt: now,
}
if err := cf.Insert(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert compliance framework: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return cf, nil
}
func (s ComplianceFrameworkService) Update(
ctx context.Context,
req *UpdateComplianceFrameworkRequest,
) (*coredata.ComplianceFramework, error) {
if err := req.Validate(); err != nil {
return nil, err
}
var cf *coredata.ComplianceFramework
err := s.svc.pg.WithTx(
ctx,
func(tx pg.Conn) error {
cf = &coredata.ComplianceFramework{}
if err := cf.LoadByID(ctx, tx, s.svc.scope, req.ID); err != nil {
return fmt.Errorf("cannot load compliance framework: %w", err)
}
cf.Rank = req.Rank
cf.UpdatedAt = time.Now()
if err := cf.UpdateRank(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot update compliance framework rank: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return cf, nil
}
func (s ComplianceFrameworkService) Delete(
ctx context.Context,
req *DeleteComplianceFrameworkRequest,
) error {
if err := req.Validate(); err != nil {
return err
}
return s.svc.pg.WithTx(
ctx,
func(tx pg.Conn) error {
cf := &coredata.ComplianceFramework{}
if err := cf.LoadByID(ctx, tx, s.svc.scope, req.ID); err != nil {
return fmt.Errorf("cannot load compliance framework: %w", err)
}
if err := cf.Delete(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot delete compliance framework: %w", err)
}
return nil
},
)
}

View File

@@ -88,6 +88,7 @@ var ViewerPolicy = policy.NewPolicy(
ActionTrustCenterDocumentAccessList,
ActionTrustCenterFileGet, ActionTrustCenterFileList, ActionTrustCenterFileGetFileUrl,
ActionTrustCenterReferenceList, ActionTrustCenterReferenceGetLogoUrl,
ActionComplianceFrameworkList,
).WithSID("trust-center-read-access").When(organizationCondition),
policy.Allow(ActionCustomDomainGet).WithSID("custom-domain-read").When(organizationCondition),

View File

@@ -101,6 +101,7 @@ type (
TrustCenterAccesses *TrustCenterAccessService
TrustCenterReferences *TrustCenterReferenceService
TrustCenterFiles *TrustCenterFileService
ComplianceFrameworks *ComplianceFrameworkService
Nonconformities *NonconformityService
Obligations *ObligationService
Snapshots *SnapshotService
@@ -225,6 +226,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
tenantService.TrustCenters = &TrustCenterService{svc: tenantService}
tenantService.TrustCenterAccesses = &TrustCenterAccessService{svc: tenantService}
tenantService.TrustCenterReferences = &TrustCenterReferenceService{svc: tenantService}
tenantService.ComplianceFrameworks = &ComplianceFrameworkService{svc: tenantService}
tenantService.TrustCenterFiles = &TrustCenterFileService{
svc: tenantService,
fileValidator: filevalidation.NewValidator(

View File

@@ -1227,6 +1227,34 @@ enum TrustCenterReferenceOrderField
)
}
enum ComplianceFrameworkOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkOrderFieldCreatedAt"
)
RANK
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkOrderFieldRank"
)
}
enum ComplianceFrameworkVisibility
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkVisibility"
) {
NONE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkVisibilityNone"
)
PUBLIC
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkVisibilityPublic"
)
}
enum TrustCenterFileOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.TrustCenterFileOrderField"
@@ -1463,6 +1491,14 @@ input TrustCenterFileOrder
field: TrustCenterFileOrderField!
}
input ComplianceFrameworkOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ComplianceFrameworkOrderBy"
) {
direction: OrderDirection!
field: ComplianceFrameworkOrderField!
}
input EvidenceOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EvidenceOrderBy"
@@ -1614,6 +1650,14 @@ type TrustCenter implements Node {
orderBy: TrustCenterReferenceOrder
): TrustCenterReferenceConnection! @goField(forceResolver: true)
complianceFrameworks(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ComplianceFrameworkOrder
): ComplianceFrameworkConnection! @goField(forceResolver: true)
permission(action: String!): Boolean! @goField(forceResolver: true)
}
@@ -2806,6 +2850,31 @@ type TrustCenterReferenceEdge {
node: TrustCenterReference!
}
type ComplianceFramework implements Node
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ComplianceFramework"
) {
id: ID!
framework: Framework! @goField(forceResolver: true)
rank: Int!
visibility: ComplianceFrameworkVisibility!
createdAt: Datetime!
updatedAt: Datetime!
}
type ComplianceFrameworkConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ComplianceFrameworkConnection"
) {
edges: [ComplianceFrameworkEdge!]!
pageInfo: PageInfo!
}
type ComplianceFrameworkEdge {
cursor: CursorKey!
node: ComplianceFramework!
}
type TrustCenterFile implements Node {
id: ID!
name: String!
@@ -3247,6 +3316,16 @@ type Mutation {
deleteTrustCenterReference(
input: DeleteTrustCenterReferenceInput!
): DeleteTrustCenterReferencePayload!
# Compliance Framework mutations
createComplianceFramework(
input: CreateComplianceFrameworkInput!
): CreateComplianceFrameworkPayload!
updateComplianceFramework(
input: UpdateComplianceFrameworkInput!
): UpdateComplianceFrameworkPayload!
deleteComplianceFramework(
input: DeleteComplianceFrameworkInput!
): DeleteComplianceFrameworkPayload!
# Trust Center File mutations
createTrustCenterFile(
input: CreateTrustCenterFileInput!
@@ -3647,6 +3726,20 @@ input DeleteTrustCenterReferenceInput {
id: ID!
}
input CreateComplianceFrameworkInput {
trustCenterId: ID!
frameworkId: ID!
}
input UpdateComplianceFrameworkInput {
id: ID!
rank: Int!
}
input DeleteComplianceFrameworkInput {
id: ID!
}
input CreateTrustCenterFileInput {
organizationId: ID!
name: String!
@@ -4462,6 +4555,18 @@ type DeleteTrustCenterReferencePayload {
deletedTrustCenterReferenceId: ID!
}
type CreateComplianceFrameworkPayload {
complianceFrameworkEdge: ComplianceFrameworkEdge!
}
type UpdateComplianceFrameworkPayload {
complianceFramework: ComplianceFramework!
}
type DeleteComplianceFrameworkPayload {
deletedComplianceFrameworkId: ID!
}
type CreateTrustCenterFilePayload {
trustCenterFileEdge: TrustCenterFileEdge!
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
// 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 (
"time"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type ComplianceFrameworkOrderBy = OrderBy[coredata.ComplianceFrameworkOrderField]
type ComplianceFramework struct {
ID gid.GID `json:"id"`
Framework *Framework `json:"framework"`
Rank int `json:"rank"`
Visibility coredata.ComplianceFrameworkVisibility `json:"visibility"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
FrameworkID gid.GID `json:"-"`
}
func (ComplianceFramework) IsNode() {}
func (c ComplianceFramework) GetID() gid.GID { return c.ID }
type ComplianceFrameworkConnection struct {
Edges []*ComplianceFrameworkEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
func NewComplianceFramework(cf *coredata.ComplianceFramework) *ComplianceFramework {
return &ComplianceFramework{
ID: cf.ID,
FrameworkID: cf.FrameworkID,
Rank: cf.Rank,
Visibility: cf.Visibility,
CreatedAt: cf.CreatedAt,
UpdatedAt: cf.UpdatedAt,
}
}
func NewComplianceFrameworkConnection(
p *page.Page[*coredata.ComplianceFramework, coredata.ComplianceFrameworkOrderField],
) *ComplianceFrameworkConnection {
edges := make([]*ComplianceFrameworkEdge, len(p.Data))
for i := range edges {
edges[i] = NewComplianceFrameworkEdge(p.Data[i], p.Cursor.OrderBy.Field)
}
return &ComplianceFrameworkConnection{
Edges: edges,
PageInfo: NewPageInfo(p),
}
}
func NewComplianceFrameworkEdge(cf *coredata.ComplianceFramework, orderBy coredata.ComplianceFrameworkOrderField) *ComplianceFrameworkEdge {
return &ComplianceFrameworkEdge{
Cursor: cf.CursorKey(orderBy),
Node: NewComplianceFramework(cf),
}
}

View File

@@ -149,6 +149,11 @@ type CancelSignatureRequestPayload struct {
DeletedDocumentVersionSignatureID gid.GID `json:"deletedDocumentVersionSignatureId"`
}
type ComplianceFrameworkEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *ComplianceFramework `json:"node"`
}
type ContinualImprovement struct {
ID gid.GID `json:"id"`
SnapshotID *gid.GID `json:"snapshotId,omitempty"`
@@ -250,6 +255,15 @@ type CreateAuditPayload struct {
AuditEdge *AuditEdge `json:"auditEdge"`
}
type CreateComplianceFrameworkInput struct {
TrustCenterID gid.GID `json:"trustCenterId"`
FrameworkID gid.GID `json:"frameworkId"`
}
type CreateComplianceFrameworkPayload struct {
ComplianceFrameworkEdge *ComplianceFrameworkEdge `json:"complianceFrameworkEdge"`
}
type CreateContinualImprovementInput struct {
OrganizationID gid.GID `json:"organizationId"`
ReferenceID string `json:"referenceId"`
@@ -789,6 +803,14 @@ type DeleteAuditReportPayload struct {
Audit *Audit `json:"audit"`
}
type DeleteComplianceFrameworkInput struct {
ID gid.GID `json:"id"`
}
type DeleteComplianceFrameworkPayload struct {
DeletedComplianceFrameworkID gid.GID `json:"deletedComplianceFrameworkId"`
}
type DeleteContinualImprovementInput struct {
ContinualImprovementID gid.GID `json:"continualImprovementId"`
}
@@ -1851,18 +1873,19 @@ type TransferImpactAssessmentFilter struct {
}
type TrustCenter struct {
ID gid.GID `json:"id"`
Active bool `json:"active"`
LogoFileURL *string `json:"logoFileUrl,omitempty"`
DarkLogoFileURL *string `json:"darkLogoFileUrl,omitempty"`
NdaFileName *string `json:"ndaFileName,omitempty"`
NdaFileURL *string `json:"ndaFileUrl,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Organization *Organization `json:"organization"`
Accesses *TrustCenterAccessConnection `json:"accesses"`
References *TrustCenterReferenceConnection `json:"references"`
Permission bool `json:"permission"`
ID gid.GID `json:"id"`
Active bool `json:"active"`
LogoFileURL *string `json:"logoFileUrl,omitempty"`
DarkLogoFileURL *string `json:"darkLogoFileUrl,omitempty"`
NdaFileName *string `json:"ndaFileName,omitempty"`
NdaFileURL *string `json:"ndaFileUrl,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Organization *Organization `json:"organization"`
Accesses *TrustCenterAccessConnection `json:"accesses"`
References *TrustCenterReferenceConnection `json:"references"`
ComplianceFrameworks *ComplianceFrameworkConnection `json:"complianceFrameworks"`
Permission bool `json:"permission"`
}
func (TrustCenter) IsNode() {}
@@ -1994,6 +2017,15 @@ type UpdateAuditPayload struct {
Audit *Audit `json:"audit"`
}
type UpdateComplianceFrameworkInput struct {
ID gid.GID `json:"id"`
Rank int `json:"rank"`
}
type UpdateComplianceFrameworkPayload struct {
ComplianceFramework *ComplianceFramework `json:"complianceFramework"`
}
type UpdateContinualImprovementInput struct {
ID gid.GID `json:"id"`
ReferenceID *string `json:"referenceId,omitempty"`

View File

@@ -324,6 +324,23 @@ func (r *auditConnectionResolver) TotalCount(ctx context.Context, obj *types.Aud
return count, nil
}
// Framework is the resolver for the framework field.
func (r *complianceFrameworkResolver) Framework(ctx context.Context, obj *types.ComplianceFramework) (*types.Framework, error) {
if err := r.authorize(ctx, obj.FrameworkID, probo.ActionFrameworkGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.FrameworkID.TenantID())
framework, err := prb.Frameworks.Get(ctx, obj.FrameworkID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFramework(framework), nil
}
// Organization is the resolver for the organization field.
func (r *continualImprovementResolver) Organization(ctx context.Context, obj *types.ContinualImprovement) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
@@ -2098,6 +2115,77 @@ func (r *mutationResolver) DeleteTrustCenterReference(ctx context.Context, input
}, nil
}
// CreateComplianceFramework is the resolver for the createComplianceFramework field.
func (r *mutationResolver) CreateComplianceFramework(ctx context.Context, input types.CreateComplianceFrameworkInput) (*types.CreateComplianceFrameworkPayload, error) {
if err := r.authorize(ctx, input.TrustCenterID, probo.ActionComplianceFrameworkCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.TrustCenterID.TenantID())
cf, err := prb.ComplianceFrameworks.Create(
ctx,
&probo.CreateComplianceFrameworkRequest{
TrustCenterID: input.TrustCenterID,
FrameworkID: input.FrameworkID,
},
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create compliance framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateComplianceFrameworkPayload{
ComplianceFrameworkEdge: types.NewComplianceFrameworkEdge(cf, coredata.ComplianceFrameworkOrderFieldRank),
}, nil
}
// UpdateComplianceFramework is the resolver for the updateComplianceFramework field.
func (r *mutationResolver) UpdateComplianceFramework(ctx context.Context, input types.UpdateComplianceFrameworkInput) (*types.UpdateComplianceFrameworkPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionComplianceFrameworkUpdateRank); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
cf, err := prb.ComplianceFrameworks.Update(ctx, &probo.UpdateComplianceFrameworkRequest{
ID: input.ID,
Rank: input.Rank,
})
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update compliance framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateComplianceFrameworkPayload{
ComplianceFramework: types.NewComplianceFramework(cf),
}, nil
}
// DeleteComplianceFramework is the resolver for the deleteComplianceFramework field.
func (r *mutationResolver) DeleteComplianceFramework(ctx context.Context, input types.DeleteComplianceFrameworkInput) (*types.DeleteComplianceFrameworkPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionComplianceFrameworkDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
err := prb.ComplianceFrameworks.Delete(
ctx,
&probo.DeleteComplianceFrameworkRequest{
ID: input.ID,
},
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete compliance framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteComplianceFrameworkPayload{
DeletedComplianceFrameworkID: input.ID,
}, nil
}
// CreateTrustCenterFile is the resolver for the createTrustCenterFile field.
func (r *mutationResolver) CreateTrustCenterFile(ctx context.Context, input types.CreateTrustCenterFileInput) (*types.CreateTrustCenterFilePayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionTrustCenterFileCreate); err != nil {
@@ -7814,6 +7902,36 @@ func (r *trustCenterResolver) References(ctx context.Context, obj *types.TrustCe
return types.NewTrustCenterReferenceConnection(result, obj.ID), nil
}
// ComplianceFrameworks is the resolver for the complianceFrameworks field.
func (r *trustCenterResolver) ComplianceFrameworks(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.OrderBy[coredata.ComplianceFrameworkOrderField]) (*types.ComplianceFrameworkConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionComplianceFrameworkList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ComplianceFrameworkOrderField]{
Field: coredata.ComplianceFrameworkOrderFieldRank,
Direction: page.OrderDirectionAsc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.ComplianceFrameworkOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
result, err := prb.ComplianceFrameworks.ListWithHiddenForTrustCenterID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list compliance frameworks", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewComplianceFrameworkConnection(result), nil
}
// Permission is the resolver for the permission field.
func (r *trustCenterResolver) Permission(ctx context.Context, obj *types.TrustCenter, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
@@ -8776,6 +8894,11 @@ func (r *Resolver) AuditConnection() schema.AuditConnectionResolver {
return &auditConnectionResolver{r}
}
// ComplianceFramework returns schema.ComplianceFrameworkResolver implementation.
func (r *Resolver) ComplianceFramework() schema.ComplianceFrameworkResolver {
return &complianceFrameworkResolver{r}
}
// ContinualImprovement returns schema.ContinualImprovementResolver implementation.
func (r *Resolver) ContinualImprovement() schema.ContinualImprovementResolver {
return &continualImprovementResolver{r}
@@ -9078,6 +9201,7 @@ type assetResolver struct{ *Resolver }
type assetConnectionResolver struct{ *Resolver }
type auditResolver struct{ *Resolver }
type auditConnectionResolver struct{ *Resolver }
type complianceFrameworkResolver struct{ *Resolver }
type continualImprovementResolver struct{ *Resolver }
type continualImprovementConnectionResolver struct{ *Resolver }
type controlResolver struct{ *Resolver }

View File

@@ -107,6 +107,21 @@ type AuditEdge @nda {
node: Audit!
}
type ComplianceFramework implements Node {
id: ID!
framework: Framework! @goField(forceResolver: true)
}
type ComplianceFrameworkConnection {
edges: [ComplianceFrameworkEdge!]!
pageInfo: PageInfo!
}
type ComplianceFrameworkEdge {
cursor: CursorKey!
node: ComplianceFramework!
}
enum CountryCode
@goModel(model: "go.probo.inc/probo/pkg/coredata.CountryCode") {
AD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAD")
@@ -542,6 +557,13 @@ type TrustCenter implements Node {
last: Int
before: CursorKey
): TrustCenterFileConnection! @goField(forceResolver: true)
complianceFrameworks(
first: Int
after: CursorKey
last: Int
before: CursorKey
): ComplianceFrameworkConnection! @goField(forceResolver: true)
}
type TrustCenterAccess implements Node {

View File

@@ -37,6 +37,7 @@ type Config = graphql.Config[ResolverRoot, DirectiveRoot, ComplexityRoot]
type ResolverRoot interface {
Audit() AuditResolver
ComplianceFramework() ComplianceFrameworkResolver
Document() DocumentResolver
Framework() FrameworkResolver
Mutation() MutationResolver
@@ -77,6 +78,21 @@ type ComplexityRoot struct {
Node func(childComplexity int) int
}
ComplianceFramework struct {
Framework func(childComplexity int) int
ID func(childComplexity int) int
}
ComplianceFrameworkConnection struct {
Edges func(childComplexity int) int
PageInfo func(childComplexity int) int
}
ComplianceFrameworkEdge struct {
Cursor func(childComplexity int) int
Node func(childComplexity int) int
}
Document struct {
Access func(childComplexity int) int
DocumentType func(childComplexity int) int
@@ -216,6 +232,7 @@ type ComplexityRoot struct {
TrustCenter struct {
Active func(childComplexity int) int
Audits func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int
ComplianceFrameworks func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int
DarkLogoFileURL func(childComplexity int) int
Documents func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int
ID func(childComplexity int) int
@@ -302,6 +319,9 @@ type AuditResolver interface {
Framework(ctx context.Context, obj *types.Audit) (*types.Framework, error)
Report(ctx context.Context, obj *types.Audit) (*types.Report, error)
}
type ComplianceFrameworkResolver interface {
Framework(ctx context.Context, obj *types.ComplianceFramework) (*types.Framework, error)
}
type DocumentResolver interface {
IsUserAuthorized(ctx context.Context, obj *types.Document) (bool, error)
Access(ctx context.Context, obj *types.Document) (*types.DocumentAccess, error)
@@ -349,6 +369,7 @@ type TrustCenterResolver interface {
Vendors(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.VendorConnection, error)
References(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.TrustCenterReferenceConnection, error)
TrustCenterFiles(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.TrustCenterFileConnection, error)
ComplianceFrameworks(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.ComplianceFrameworkConnection, error)
}
type TrustCenterFileResolver interface {
IsUserAuthorized(ctx context.Context, obj *types.TrustCenterFile) (bool, error)
@@ -433,6 +454,45 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.ComplexityRoot.AuditEdge.Node(childComplexity), true
case "ComplianceFramework.framework":
if e.ComplexityRoot.ComplianceFramework.Framework == nil {
break
}
return e.ComplexityRoot.ComplianceFramework.Framework(childComplexity), true
case "ComplianceFramework.id":
if e.ComplexityRoot.ComplianceFramework.ID == nil {
break
}
return e.ComplexityRoot.ComplianceFramework.ID(childComplexity), true
case "ComplianceFrameworkConnection.edges":
if e.ComplexityRoot.ComplianceFrameworkConnection.Edges == nil {
break
}
return e.ComplexityRoot.ComplianceFrameworkConnection.Edges(childComplexity), true
case "ComplianceFrameworkConnection.pageInfo":
if e.ComplexityRoot.ComplianceFrameworkConnection.PageInfo == nil {
break
}
return e.ComplexityRoot.ComplianceFrameworkConnection.PageInfo(childComplexity), true
case "ComplianceFrameworkEdge.cursor":
if e.ComplexityRoot.ComplianceFrameworkEdge.Cursor == nil {
break
}
return e.ComplexityRoot.ComplianceFrameworkEdge.Cursor(childComplexity), true
case "ComplianceFrameworkEdge.node":
if e.ComplexityRoot.ComplianceFrameworkEdge.Node == nil {
break
}
return e.ComplexityRoot.ComplianceFrameworkEdge.Node(childComplexity), true
case "Document.access":
if e.ComplexityRoot.Document.Access == nil {
break
@@ -948,6 +1008,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
}
return e.ComplexityRoot.TrustCenter.Audits(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey)), true
case "TrustCenter.complianceFrameworks":
if e.ComplexityRoot.TrustCenter.ComplianceFrameworks == nil {
break
}
args, err := ec.field_TrustCenter_complianceFrameworks_args(ctx, rawArgs)
if err != nil {
return 0, false
}
return e.ComplexityRoot.TrustCenter.ComplianceFrameworks(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey)), true
case "TrustCenter.darkLogoFileUrl":
if e.ComplexityRoot.TrustCenter.DarkLogoFileURL == nil {
break
@@ -1458,6 +1529,21 @@ type AuditEdge @nda {
node: Audit!
}
type ComplianceFramework implements Node {
id: ID!
framework: Framework! @goField(forceResolver: true)
}
type ComplianceFrameworkConnection {
edges: [ComplianceFrameworkEdge!]!
pageInfo: PageInfo!
}
type ComplianceFrameworkEdge {
cursor: CursorKey!
node: ComplianceFramework!
}
enum CountryCode
@goModel(model: "go.probo.inc/probo/pkg/coredata.CountryCode") {
AD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAD")
@@ -1893,6 +1979,13 @@ type TrustCenter implements Node {
last: Int
before: CursorKey
): TrustCenterFileConnection! @goField(forceResolver: true)
complianceFrameworks(
first: Int
after: CursorKey
last: Int
before: CursorKey
): ComplianceFrameworkConnection! @goField(forceResolver: true)
}
type TrustCenterAccess implements Node {
@@ -2396,6 +2489,32 @@ func (ec *executionContext) field_TrustCenter_audits_args(ctx context.Context, r
return args, nil
}
func (ec *executionContext) field_TrustCenter_complianceFrameworks_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
arg0, err := graphql.ProcessArgField(ctx, rawArgs, "first", ec.unmarshalOInt2ᚖint)
if err != nil {
return nil, err
}
args["first"] = arg0
arg1, err := graphql.ProcessArgField(ctx, rawArgs, "after", ec.unmarshalOCursorKey2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋpageᚐCursorKey)
if err != nil {
return nil, err
}
args["after"] = arg1
arg2, err := graphql.ProcessArgField(ctx, rawArgs, "last", ec.unmarshalOInt2ᚖint)
if err != nil {
return nil, err
}
args["last"] = arg2
arg3, err := graphql.ProcessArgField(ctx, rawArgs, "before", ec.unmarshalOCursorKey2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋpageᚐCursorKey)
if err != nil {
return nil, err
}
args["before"] = arg3
return args, nil
}
func (ec *executionContext) field_TrustCenter_documents_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -2929,6 +3048,212 @@ func (ec *executionContext) fieldContext_AuditEdge_node(_ context.Context, field
return fc, nil
}
func (ec *executionContext) _ComplianceFramework_id(ctx context.Context, field graphql.CollectedField, obj *types.ComplianceFramework) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_ComplianceFramework_id,
func(ctx context.Context) (any, error) {
return obj.ID, nil
},
nil,
ec.marshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID,
true,
true,
)
}
func (ec *executionContext) fieldContext_ComplianceFramework_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ComplianceFramework",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type ID does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _ComplianceFramework_framework(ctx context.Context, field graphql.CollectedField, obj *types.ComplianceFramework) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_ComplianceFramework_framework,
func(ctx context.Context) (any, error) {
return ec.Resolvers.ComplianceFramework().Framework(ctx, obj)
},
nil,
ec.marshalNFramework2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐFramework,
true,
true,
)
}
func (ec *executionContext) fieldContext_ComplianceFramework_framework(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ComplianceFramework",
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_Framework_id(ctx, field)
case "name":
return ec.fieldContext_Framework_name(ctx, field)
case "lightLogoURL":
return ec.fieldContext_Framework_lightLogoURL(ctx, field)
case "darkLogoURL":
return ec.fieldContext_Framework_darkLogoURL(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type Framework", field.Name)
},
}
return fc, nil
}
func (ec *executionContext) _ComplianceFrameworkConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.ComplianceFrameworkConnection) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_ComplianceFrameworkConnection_edges,
func(ctx context.Context) (any, error) {
return obj.Edges, nil
},
nil,
ec.marshalNComplianceFrameworkEdge2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐComplianceFrameworkEdgeᚄ,
true,
true,
)
}
func (ec *executionContext) fieldContext_ComplianceFrameworkConnection_edges(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ComplianceFrameworkConnection",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "cursor":
return ec.fieldContext_ComplianceFrameworkEdge_cursor(ctx, field)
case "node":
return ec.fieldContext_ComplianceFrameworkEdge_node(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type ComplianceFrameworkEdge", field.Name)
},
}
return fc, nil
}
func (ec *executionContext) _ComplianceFrameworkConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *types.ComplianceFrameworkConnection) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_ComplianceFrameworkConnection_pageInfo,
func(ctx context.Context) (any, error) {
return obj.PageInfo, nil
},
nil,
ec.marshalNPageInfo2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐPageInfo,
true,
true,
)
}
func (ec *executionContext) fieldContext_ComplianceFrameworkConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ComplianceFrameworkConnection",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "hasNextPage":
return ec.fieldContext_PageInfo_hasNextPage(ctx, field)
case "hasPreviousPage":
return ec.fieldContext_PageInfo_hasPreviousPage(ctx, field)
case "startCursor":
return ec.fieldContext_PageInfo_startCursor(ctx, field)
case "endCursor":
return ec.fieldContext_PageInfo_endCursor(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name)
},
}
return fc, nil
}
func (ec *executionContext) _ComplianceFrameworkEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *types.ComplianceFrameworkEdge) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_ComplianceFrameworkEdge_cursor,
func(ctx context.Context) (any, error) {
return obj.Cursor, nil
},
nil,
ec.marshalNCursorKey2goᚗproboᚗincᚋproboᚋpkgᚋpageᚐCursorKey,
true,
true,
)
}
func (ec *executionContext) fieldContext_ComplianceFrameworkEdge_cursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ComplianceFrameworkEdge",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type CursorKey does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _ComplianceFrameworkEdge_node(ctx context.Context, field graphql.CollectedField, obj *types.ComplianceFrameworkEdge) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_ComplianceFrameworkEdge_node,
func(ctx context.Context) (any, error) {
return obj.Node, nil
},
nil,
ec.marshalNComplianceFramework2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐComplianceFramework,
true,
true,
)
}
func (ec *executionContext) fieldContext_ComplianceFrameworkEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ComplianceFrameworkEdge",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "id":
return ec.fieldContext_ComplianceFramework_id(ctx, field)
case "framework":
return ec.fieldContext_ComplianceFramework_framework(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type ComplianceFramework", field.Name)
},
}
return fc, nil
}
func (ec *executionContext) _Document_id(ctx context.Context, field graphql.CollectedField, obj *types.Document) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -5203,6 +5528,8 @@ func (ec *executionContext) fieldContext_Query_currentTrustCenter(_ context.Cont
return ec.fieldContext_TrustCenter_references(ctx, field)
case "trustCenterFiles":
return ec.fieldContext_TrustCenter_trustCenterFiles(ctx, field)
case "complianceFrameworks":
return ec.fieldContext_TrustCenter_complianceFrameworks(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type TrustCenter", field.Name)
},
@@ -6228,6 +6555,53 @@ func (ec *executionContext) fieldContext_TrustCenter_trustCenterFiles(ctx contex
return fc, nil
}
func (ec *executionContext) _TrustCenter_complianceFrameworks(ctx context.Context, field graphql.CollectedField, obj *types.TrustCenter) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_TrustCenter_complianceFrameworks,
func(ctx context.Context) (any, error) {
fc := graphql.GetFieldContext(ctx)
return ec.Resolvers.TrustCenter().ComplianceFrameworks(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey))
},
nil,
ec.marshalNComplianceFrameworkConnection2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐComplianceFrameworkConnection,
true,
true,
)
}
func (ec *executionContext) fieldContext_TrustCenter_complianceFrameworks(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "TrustCenter",
Field: field,
IsMethod: true,
IsResolver: true,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "edges":
return ec.fieldContext_ComplianceFrameworkConnection_edges(ctx, field)
case "pageInfo":
return ec.fieldContext_ComplianceFrameworkConnection_pageInfo(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type ComplianceFrameworkConnection", 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_TrustCenter_complianceFrameworks_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
ec.Error(ctx, err)
return fc, err
}
return fc, nil
}
func (ec *executionContext) _TrustCenterAccess_id(ctx context.Context, field graphql.CollectedField, obj *types.TrustCenterAccess) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -9263,6 +9637,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj
return graphql.Null
}
return ec._Document(ctx, sel, obj)
case types.ComplianceFramework:
return ec._ComplianceFramework(ctx, sel, &obj)
case *types.ComplianceFramework:
if obj == nil {
return graphql.Null
}
return ec._ComplianceFramework(ctx, sel, obj)
case types.Audit:
return ec._Audit(ctx, sel, &obj)
case *types.Audit:
@@ -9520,6 +9901,169 @@ func (ec *executionContext) _AuditEdge(ctx context.Context, sel ast.SelectionSet
return out
}
var complianceFrameworkImplementors = []string{"ComplianceFramework", "Node"}
func (ec *executionContext) _ComplianceFramework(ctx context.Context, sel ast.SelectionSet, obj *types.ComplianceFramework) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, complianceFrameworkImplementors)
out := graphql.NewFieldSet(fields)
deferred := make(map[string]*graphql.FieldSet)
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("ComplianceFramework")
case "id":
out.Values[i] = ec._ComplianceFramework_id(ctx, field, obj)
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "framework":
field := field
innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._ComplianceFramework_framework(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&fs.Invalids, 1)
}
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))
}
}
out.Dispatch(ctx)
if out.Invalids > 0 {
return graphql.Null
}
atomic.AddInt32(&ec.Deferred, int32(len(deferred)))
for label, dfs := range deferred {
ec.ProcessDeferredGroup(graphql.DeferredGroup{
Label: label,
Path: graphql.GetPath(ctx),
FieldSet: dfs,
Context: ctx,
})
}
return out
}
var complianceFrameworkConnectionImplementors = []string{"ComplianceFrameworkConnection"}
func (ec *executionContext) _ComplianceFrameworkConnection(ctx context.Context, sel ast.SelectionSet, obj *types.ComplianceFrameworkConnection) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, complianceFrameworkConnectionImplementors)
out := graphql.NewFieldSet(fields)
deferred := make(map[string]*graphql.FieldSet)
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("ComplianceFrameworkConnection")
case "edges":
out.Values[i] = ec._ComplianceFrameworkConnection_edges(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "pageInfo":
out.Values[i] = ec._ComplianceFrameworkConnection_pageInfo(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch(ctx)
if out.Invalids > 0 {
return graphql.Null
}
atomic.AddInt32(&ec.Deferred, int32(len(deferred)))
for label, dfs := range deferred {
ec.ProcessDeferredGroup(graphql.DeferredGroup{
Label: label,
Path: graphql.GetPath(ctx),
FieldSet: dfs,
Context: ctx,
})
}
return out
}
var complianceFrameworkEdgeImplementors = []string{"ComplianceFrameworkEdge"}
func (ec *executionContext) _ComplianceFrameworkEdge(ctx context.Context, sel ast.SelectionSet, obj *types.ComplianceFrameworkEdge) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, complianceFrameworkEdgeImplementors)
out := graphql.NewFieldSet(fields)
deferred := make(map[string]*graphql.FieldSet)
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("ComplianceFrameworkEdge")
case "cursor":
out.Values[i] = ec._ComplianceFrameworkEdge_cursor(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "node":
out.Values[i] = ec._ComplianceFrameworkEdge_node(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch(ctx)
if out.Invalids > 0 {
return graphql.Null
}
atomic.AddInt32(&ec.Deferred, int32(len(deferred)))
for label, dfs := range deferred {
ec.ProcessDeferredGroup(graphql.DeferredGroup{
Label: label,
Path: graphql.GetPath(ctx),
FieldSet: dfs,
Context: ctx,
})
}
return out
}
var documentImplementors = []string{"Document", "Node"}
func (ec *executionContext) _Document(ctx context.Context, sel ast.SelectionSet, obj *types.Document) graphql.Marshaler {
@@ -11265,6 +11809,42 @@ func (ec *executionContext) _TrustCenter(ctx context.Context, sel ast.SelectionS
continue
}
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
case "complianceFrameworks":
field := field
innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._TrustCenter_complianceFrameworks(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&fs.Invalids, 1)
}
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))
@@ -12355,6 +12935,56 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se
return res
}
func (ec *executionContext) marshalNComplianceFramework2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐComplianceFramework(ctx context.Context, sel ast.SelectionSet, v *types.ComplianceFramework) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
return ec._ComplianceFramework(ctx, sel, v)
}
func (ec *executionContext) marshalNComplianceFrameworkConnection2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐComplianceFrameworkConnection(ctx context.Context, sel ast.SelectionSet, v types.ComplianceFrameworkConnection) graphql.Marshaler {
return ec._ComplianceFrameworkConnection(ctx, sel, &v)
}
func (ec *executionContext) marshalNComplianceFrameworkConnection2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐComplianceFrameworkConnection(ctx context.Context, sel ast.SelectionSet, v *types.ComplianceFrameworkConnection) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
return ec._ComplianceFrameworkConnection(ctx, sel, v)
}
func (ec *executionContext) marshalNComplianceFrameworkEdge2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐComplianceFrameworkEdgeᚄ(ctx context.Context, sel ast.SelectionSet, v []*types.ComplianceFrameworkEdge) graphql.Marshaler {
ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler {
fc := graphql.GetFieldContext(ctx)
fc.Result = &v[i]
return ec.marshalNComplianceFrameworkEdge2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐComplianceFrameworkEdge(ctx, sel, v[i])
})
for _, e := range ret {
if e == graphql.Null {
return graphql.Null
}
}
return ret
}
func (ec *executionContext) marshalNComplianceFrameworkEdge2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐComplianceFrameworkEdge(ctx context.Context, sel ast.SelectionSet, v *types.ComplianceFrameworkEdge) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
return ec._ComplianceFrameworkEdge(ctx, sel, v)
}
func (ec *executionContext) unmarshalNCountryCode2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐCountryCode(ctx context.Context, v any) (coredata.CountryCode, error) {
tmp, err := graphql.UnmarshalString(v)
res := unmarshalNCountryCode2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐCountryCode[tmp]

View File

@@ -16,6 +16,7 @@ package types
import (
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/page"
)
func NewFramework(f *coredata.Framework) *Framework {
@@ -24,3 +25,31 @@ func NewFramework(f *coredata.Framework) *Framework {
Name: f.Name,
}
}
func NewComplianceFramework(cf *coredata.ComplianceFramework) *ComplianceFramework {
return &ComplianceFramework{
ID: cf.ID,
FrameworkID: cf.FrameworkID,
}
}
func NewComplianceFrameworkEdge(cf *coredata.ComplianceFramework) *ComplianceFrameworkEdge {
return &ComplianceFrameworkEdge{
Cursor: cf.CursorKey(coredata.ComplianceFrameworkOrderFieldRank),
Node: NewComplianceFramework(cf),
}
}
func NewComplianceFrameworkConnection(
p *page.Page[*coredata.ComplianceFramework, coredata.ComplianceFrameworkOrderField],
) *ComplianceFrameworkConnection {
edges := make([]*ComplianceFrameworkEdge, len(p.Data))
for i, cf := range p.Data {
edges[i] = NewComplianceFrameworkEdge(cf)
}
return &ComplianceFrameworkConnection{
Edges: edges,
PageInfo: NewPageInfo(p),
}
}

View File

@@ -45,6 +45,26 @@ type AuditEdge struct {
Node *Audit `json:"node"`
}
type ComplianceFramework struct {
ID gid.GID `json:"id"`
// FrameworkID is a non-schema field used by the Framework resolver to avoid an extra DB lookup.
FrameworkID gid.GID `json:"-"`
Framework *Framework `json:"framework"`
}
func (ComplianceFramework) IsNode() {}
func (this ComplianceFramework) GetID() gid.GID { return this.ID }
type ComplianceFrameworkConnection struct {
Edges []*ComplianceFrameworkEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type ComplianceFrameworkEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *ComplianceFramework `json:"node"`
}
type Document struct {
ID gid.GID `json:"id"`
Title string `json:"title"`
@@ -235,6 +255,7 @@ type TrustCenter struct {
Vendors *VendorConnection `json:"vendors"`
References *TrustCenterReferenceConnection `json:"references"`
TrustCenterFiles *TrustCenterFileConnection `json:"trustCenterFiles"`
ComplianceFrameworks *ComplianceFrameworkConnection `json:"complianceFrameworks"`
}
func (TrustCenter) IsNode() {}

View File

@@ -71,6 +71,19 @@ func (r *auditResolver) Report(ctx context.Context, obj *types.Audit) (*types.Re
return types.NewReport(report), nil
}
// Framework is the resolver for the framework field on ComplianceFramework.
func (r *complianceFrameworkResolver) Framework(ctx context.Context, obj *types.ComplianceFramework) (*types.Framework, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
framework, err := trustService.Frameworks.Get(ctx, obj.FrameworkID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFramework(framework), nil
}
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *documentResolver) IsUserAuthorized(ctx context.Context, obj *types.Document) (bool, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
@@ -1014,6 +1027,25 @@ func (r *trustCenterResolver) TrustCenterFiles(ctx context.Context, obj *types.T
return types.NewTrustCenterFileConnection(trustCenterFilePage), nil
}
// ComplianceFrameworks is the resolver for the complianceFrameworks field.
func (r *trustCenterResolver) ComplianceFrameworks(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.ComplianceFrameworkConnection, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ComplianceFrameworkOrderField]{
Field: coredata.ComplianceFrameworkOrderFieldRank,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
cfPage, err := trustService.ComplianceFrameworks.ListByTrustCenterID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list compliance frameworks", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewComplianceFrameworkConnection(cfPage), nil
}
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *trustCenterFileResolver) IsUserAuthorized(ctx context.Context, obj *types.TrustCenterFile) (bool, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
@@ -1124,6 +1156,11 @@ func (r *vendorConnectionResolver) TotalCount(ctx context.Context, obj *types.Ve
// Audit returns schema.AuditResolver implementation.
func (r *Resolver) Audit() schema.AuditResolver { return &auditResolver{r} }
// ComplianceFramework returns schema.ComplianceFrameworkResolver implementation.
func (r *Resolver) ComplianceFramework() schema.ComplianceFrameworkResolver {
return &complianceFrameworkResolver{r}
}
// Document returns schema.DocumentResolver implementation.
func (r *Resolver) Document() schema.DocumentResolver { return &documentResolver{r} }
@@ -1166,6 +1203,7 @@ func (r *Resolver) VendorConnection() schema.VendorConnectionResolver {
}
type auditResolver struct{ *Resolver }
type complianceFrameworkResolver struct{ *Resolver }
type documentResolver struct{ *Resolver }
type frameworkResolver struct{ *Resolver }
type mutationResolver struct{ *Resolver }

View File

@@ -0,0 +1,55 @@
// 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 trust
import (
"context"
"fmt"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type ComplianceFrameworkService struct {
svc *TenantService
}
func (s ComplianceFrameworkService) ListByTrustCenterID(
ctx context.Context,
trustCenterID gid.GID,
cursor *page.Cursor[coredata.ComplianceFrameworkOrderField],
) (*page.Page[*coredata.ComplianceFramework, coredata.ComplianceFrameworkOrderField], error) {
var complianceFrameworks coredata.ComplianceFrameworks
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
err := complianceFrameworks.LoadByTrustCenterID(ctx, conn, s.svc.scope, trustCenterID, cursor)
if err != nil {
return fmt.Errorf("cannot load compliance frameworks: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return page.NewPage(complianceFrameworks, cursor), nil
}

View File

@@ -70,6 +70,7 @@ type (
Audits *AuditService
Vendors *VendorService
Frameworks *FrameworkService
ComplianceFrameworks *ComplianceFrameworkService
TrustCenterAccesses *TrustCenterAccessService
TrustCenterReferences *TrustCenterReferenceService
TrustCenterFiles *TrustCenterFileService
@@ -130,6 +131,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
tenantService.Audits = &AuditService{svc: tenantService}
tenantService.Vendors = &VendorService{svc: tenantService}
tenantService.Frameworks = &FrameworkService{svc: tenantService}
tenantService.ComplianceFrameworks = &ComplianceFrameworkService{svc: tenantService}
tenantService.TrustCenterAccesses = &TrustCenterAccessService{svc: tenantService, iamSvc: s.iam, logger: s.logger}
tenantService.TrustCenterReferences = &TrustCenterReferenceService{svc: tenantService}
tenantService.TrustCenterFiles = &TrustCenterFileService{svc: tenantService}