diff --git a/apps/console/src/pages/organizations/SettingsView.tsx b/apps/console/src/pages/organizations/SettingsView.tsx index a4bd16232..ef6acc290 100644 --- a/apps/console/src/pages/organizations/SettingsView.tsx +++ b/apps/console/src/pages/organizations/SettingsView.tsx @@ -23,6 +23,7 @@ import { useMutation, } from "react-relay"; import { useParams } from "react-router"; +import { Link } from "react-router"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useToast } from "@/hooks/use-toast"; @@ -41,6 +42,21 @@ import type { SettingsViewRemoveUserMutation as SettingsViewRemoveUserMutationTy import { PageTemplate } from "@/components/PageTemplate"; import { SettingsViewSkeleton } from "./SettingsPage"; +// Define the connector type until the generated type is available +interface Connector { + id: string; + name: string; + type: string; + createdAt: string; +} + +interface AvailableConnector { + id: string; + name: string; + type: string; + description: string; +} + const settingsViewQuery = graphql` query SettingsViewQuery($organizationID: ID!) { organization: node(id: $organizationID) { @@ -58,6 +74,16 @@ const settingsViewQuery = graphql` } } } + connectors(first: 100) { + edges { + node { + id + name + type + createdAt + } + } + } } } } @@ -101,6 +127,7 @@ function SettingsViewContent({ const data = usePreloadedQuery(settingsViewQuery, queryRef); const organization = data.organization; const users = organization.users?.edges.map((edge) => edge.node) || []; + const connectors = (organization as any).connectors?.edges.map((edge: any) => edge.node) || []; const { toast } = useToast(); const fileInputRef = useRef(null); @@ -115,6 +142,18 @@ function SettingsViewContent({ const [isUploading, setIsUploading] = useState(false); const [isRemoving, setIsRemoving] = useState(false); + // Available connectors + const availableConnectors: AvailableConnector[] = [ + { id: "github", name: "GitHub", type: "oauth2", description: "Connect to GitHub repositories and issues" }, + { id: "slack", name: "Slack", type: "oauth2", description: "Connect to Slack workspace and channels" }, + ]; + + // Filter out connectors that are already connected + const connectedConnectorIds = connectors.map((connector: Connector) => connector.name); + const notConnectedConnectors = availableConnectors.filter( + connector => !connectedConnectorIds.includes(connector.id) + ); + const [updateOrganization] = useMutation( updateOrganizationMutation @@ -129,7 +168,7 @@ function SettingsViewContent({ const { organizationId } = useParams(); const [, loadQuery] = useQueryLoader(settingsViewQuery); - + const handleUpdateName = () => { updateOrganization({ variables: { @@ -444,6 +483,96 @@ function SettingsViewContent({ + + + +
+ Integrations + + Connect to third-party services to enhance your workflow + +
+
+ +
+ {connectors.length > 0 && ( +
+

Connected services

+
+ {connectors.map((connector: Connector) => ( +
+
+
+ +
+
+ + {connector.name} + + + {connector.type} · Connected on {new Date(connector.createdAt).toLocaleDateString()} + +
+
+
+ ))} +
+
+ )} + + {notConnectedConnectors.length > 0 && ( +
+

Available services

+
+ {notConnectedConnectors.map((connector) => ( +
+
+
+ +
+
+ + {connector.name} + + + {connector.description} + +
+
+ +
+ ))} +
+
+ )} + + {connectors.length === 0 && notConnectedConnectors.length === 0 && ( +
+
+ +
+

No integrations available

+

+ There are currently no integrations available for your workspace. +

+
+ )} +
+
+
diff --git a/apps/console/src/pages/organizations/__generated__/SettingsViewQuery.graphql.ts b/apps/console/src/pages/organizations/__generated__/SettingsViewQuery.graphql.ts index 6542a59b1..3c11e5802 100644 --- a/apps/console/src/pages/organizations/__generated__/SettingsViewQuery.graphql.ts +++ b/apps/console/src/pages/organizations/__generated__/SettingsViewQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<302c6e908c1aea64db4820b83e5e2bcd>> * @lightSyntaxTransform * @nogrep */ @@ -14,6 +14,16 @@ export type SettingsViewQuery$variables = { }; export type SettingsViewQuery$data = { readonly organization: { + readonly connectors?: { + readonly edges: ReadonlyArray<{ + readonly node: { + readonly createdAt: string; + readonly id: string; + readonly name: string; + readonly type: string; + }; + }>; + }; readonly id: string; readonly logoUrl?: string | null | undefined; readonly name?: string; @@ -57,15 +67,30 @@ v2 = { "storageKey": null }, v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}, +v4 = [ + { + "kind": "Literal", + "name": "first", + "value": 100 + } +], +v5 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null +}, +v6 = { "kind": "InlineFragment", "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "name", - "storageKey": null - }, + (v3/*: any*/), { "alias": null, "args": null, @@ -75,13 +100,7 @@ v3 = { }, { "alias": null, - "args": [ - { - "kind": "Literal", - "name": "first", - "value": 100 - } - ], + "args": (v4/*: any*/), "concreteType": "UserConnection", "kind": "LinkedField", "name": "users", @@ -118,13 +137,7 @@ v3 = { "name": "email", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "createdAt", - "storageKey": null - } + (v5/*: any*/) ], "storageKey": null } @@ -133,6 +146,49 @@ v3 = { } ], "storageKey": "users(first:100)" + }, + { + "alias": null, + "args": (v4/*: any*/), + "concreteType": "ConnectorConnection", + "kind": "LinkedField", + "name": "connectors", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "ConnectorEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Connector", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + (v3/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "type", + "storageKey": null + }, + (v5/*: any*/) + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "connectors(first:100)" } ], "type": "Organization", @@ -154,7 +210,7 @@ return { "plural": false, "selections": [ (v2/*: any*/), - (v3/*: any*/) + (v6/*: any*/) ], "storageKey": null } @@ -184,23 +240,23 @@ return { "storageKey": null }, (v2/*: any*/), - (v3/*: any*/) + (v6/*: any*/) ], "storageKey": null } ] }, "params": { - "cacheID": "1749c3e17b6efd13be678a0e968b7ac4", + "cacheID": "e243498081f3875e74172abba80a88e8", "id": null, "metadata": {}, "name": "SettingsViewQuery", "operationKind": "query", - "text": "query SettingsViewQuery(\n $organizationID: ID!\n) {\n organization: node(id: $organizationID) {\n __typename\n id\n ... on Organization {\n name\n logoUrl\n users(first: 100) {\n edges {\n node {\n id\n fullName\n email\n createdAt\n }\n }\n }\n }\n }\n}\n" + "text": "query SettingsViewQuery(\n $organizationID: ID!\n) {\n organization: node(id: $organizationID) {\n __typename\n id\n ... on Organization {\n name\n logoUrl\n users(first: 100) {\n edges {\n node {\n id\n fullName\n email\n createdAt\n }\n }\n }\n connectors(first: 100) {\n edges {\n node {\n id\n name\n type\n createdAt\n }\n }\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "147757d21500b3eb42848ebe8a43bd9f"; +(node as any).hash = "52b24e3845bb31016ddad6f815be1cdb"; export default node; diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index 83ac617ea..f759d6a67 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -19,14 +19,16 @@ import ( "encoding/json" "fmt" "net/http" + + "github.com/getprobo/probo/pkg/gid" ) type ( ProtocolType string Connector interface { - Initiate(ctx context.Context, connectorID string, organizationID string, r *http.Request) (string, error) - Complete(ctx context.Context, connectorID string, organizationID string, r *http.Request) (Connection, error) + Initiate(ctx context.Context, connectorID string, organizationID gid.GID, r *http.Request) (string, error) + Complete(ctx context.Context, connectorID string, organizationID gid.GID, r *http.Request) (Connection, error) } Connection interface { diff --git a/pkg/connector/oauth2.go b/pkg/connector/oauth2.go index b8b868685..b93e1fcd5 100644 --- a/pkg/connector/oauth2.go +++ b/pkg/connector/oauth2.go @@ -23,6 +23,7 @@ import ( "strings" "time" + "github.com/getprobo/probo/pkg/gid" "github.com/getprobo/probo/pkg/statelesstoken" ) @@ -65,8 +66,8 @@ var ( OAuth2TokenTTL = 10 * time.Minute ) -func (c *OAuth2Connector) Initiate(ctx context.Context, connectorID string, organizationID string, r *http.Request) (string, error) { - stateData := OAuth2State{OrganizationID: organizationID, ConnectorID: connectorID} +func (c *OAuth2Connector) Initiate(ctx context.Context, connectorID string, organizationID gid.GID, r *http.Request) (string, error) { + stateData := OAuth2State{OrganizationID: organizationID.String(), ConnectorID: connectorID} state, err := statelesstoken.NewToken(c.ClientSecret, OAuth2TokenType, OAuth2TokenTTL, stateData) if err != nil { return "", fmt.Errorf("cannot create state token: %w", err) @@ -78,7 +79,7 @@ func (c *OAuth2Connector) Initiate(ctx context.Context, connectorID string, orga } redirectQuery := url.Values{} - redirectQuery.Set("organization_id", organizationID) + redirectQuery.Set("organization_id", organizationID.String()) redirectQuery.Set("connector_id", connectorID) redirectURI.RawQuery = redirectQuery.Encode() @@ -100,7 +101,7 @@ func (c *OAuth2Connector) Initiate(ctx context.Context, connectorID string, orga return u.String(), nil } -func (c *OAuth2Connector) Complete(ctx context.Context, connectorID string, organizationID string, r *http.Request) (Connection, error) { +func (c *OAuth2Connector) Complete(ctx context.Context, connectorID string, organizationID gid.GID, r *http.Request) (Connection, error) { code := r.URL.Query().Get("code") if code == "" { return nil, fmt.Errorf("no code in request") @@ -116,7 +117,7 @@ func (c *OAuth2Connector) Complete(ctx context.Context, connectorID string, orga return nil, fmt.Errorf("cannot validate state token: %w", err) } - if payload.Data.OrganizationID != organizationID { + if payload.Data.OrganizationID != organizationID.String() { return nil, fmt.Errorf("invalid organization ID") } @@ -130,7 +131,7 @@ func (c *OAuth2Connector) Complete(ctx context.Context, connectorID string, orga } redirectQuery := url.Values{} - redirectQuery.Set("organization_id", organizationID) + redirectQuery.Set("organization_id", organizationID.String()) redirectQuery.Set("connector_id", connectorID) redirectURI.RawQuery = redirectQuery.Encode() diff --git a/pkg/connector/registry.go b/pkg/connector/registry.go index 3f82487b8..692b08cca 100644 --- a/pkg/connector/registry.go +++ b/pkg/connector/registry.go @@ -19,6 +19,8 @@ import ( "fmt" "net/http" "sync" + + "github.com/getprobo/probo/pkg/gid" ) type ( @@ -54,7 +56,7 @@ func (cr *ConnectorRegistry) Get(connectorID string) (Connector, error) { return connector, nil } -func (cr *ConnectorRegistry) Initiate(ctx context.Context, connectorID string, organizationID string, r *http.Request) (string, error) { +func (cr *ConnectorRegistry) Initiate(ctx context.Context, connectorID string, organizationID gid.GID, r *http.Request) (string, error) { connector, err := cr.Get(connectorID) if err != nil { return "", fmt.Errorf("cannot initiate connector: %w", err) @@ -63,7 +65,7 @@ func (cr *ConnectorRegistry) Initiate(ctx context.Context, connectorID string, o return connector.Initiate(ctx, connectorID, organizationID, r) } -func (cr *ConnectorRegistry) Complete(ctx context.Context, connectorID string, organizationID string, r *http.Request) (Connection, error) { +func (cr *ConnectorRegistry) Complete(ctx context.Context, connectorID string, organizationID gid.GID, r *http.Request) (Connection, error) { connector, err := cr.Get(connectorID) if err != nil { return nil, fmt.Errorf("cannot complete connector: %w", err) diff --git a/pkg/coredata/connector.go b/pkg/coredata/connector.go index 421e34b29..5090e0e84 100644 --- a/pkg/coredata/connector.go +++ b/pkg/coredata/connector.go @@ -18,11 +18,13 @@ import ( "context" "encoding/json" "fmt" + "maps" "time" "github.com/getprobo/probo/pkg/connector" "github.com/getprobo/probo/pkg/crypto/cipher" "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" "github.com/jackc/pgx/v5" "go.gearno.de/kit/pg" ) @@ -38,8 +40,65 @@ type ( CreatedAt time.Time `db:"created_at"` UpdatedAt time.Time `db:"updated_at"` } + + Connectors []*Connector ) +func (c *Connectors) LoadWithoutDecryptedConnectionByOrganizationID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + organizationID gid.GID, + cursor *page.Cursor[ConnectorOrderField], + encryptionKey cipher.EncryptionKey, +) error { + q := ` +SELECT + id, + organization_id, + name, + type, + encrypted_connection, + created_at, + updated_at +FROM + connectors +WHERE + %s + AND organization_id = @organization_id + AND %s +` + + q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) + + args := pgx.StrictNamedArgs{"organization_id": organizationID} + 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 connectors: %w", err) + } + + connectors, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Connector]) + if err != nil { + return fmt.Errorf("cannot collect connectors: %w", err) + } + + *c = connectors + + return nil +} + +func (c *Connector) CursorKey(orderBy ConnectorOrderField) page.CursorKey { + switch orderBy { + case ConnectorOrderFieldCreatedAt: + return page.CursorKey{ID: c.ID, Value: c.CreatedAt} + } + + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) +} + func (c *Connector) Upsert( ctx context.Context, conn pg.Conn, diff --git a/pkg/coredata/connector_order_field.go b/pkg/coredata/connector_order_field.go new file mode 100644 index 000000000..7425563ea --- /dev/null +++ b/pkg/coredata/connector_order_field.go @@ -0,0 +1,41 @@ +// Copyright (c) 2025 Probo Inc . +// +// 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 ( + ConnectorOrderField string +) + +const ( + ConnectorOrderFieldCreatedAt ConnectorOrderField = "CREATED_AT" + ConnectorOrderFieldName ConnectorOrderField = "NAME" +) + +func (p ConnectorOrderField) Column() string { + return string(p) +} + +func (p ConnectorOrderField) String() string { + return string(p) +} + +func (p ConnectorOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *ConnectorOrderField) UnmarshalText(text []byte) error { + *p = ConnectorOrderField(text) + return nil +} diff --git a/pkg/probo/connector_service.go b/pkg/probo/connector_service.go index 411d1d611..f1886e5bd 100644 --- a/pkg/probo/connector_service.go +++ b/pkg/probo/connector_service.go @@ -22,6 +22,7 @@ import ( "github.com/getprobo/probo/pkg/connector" "github.com/getprobo/probo/pkg/coredata" "github.com/getprobo/probo/pkg/gid" + "github.com/getprobo/probo/pkg/page" "go.gearno.de/kit/pg" ) @@ -38,7 +39,38 @@ type ( } ) -func (s *ConnectorService) CreateOrUpdate(ctx context.Context, req CreateOrUpdateConnectorRequest) (*coredata.Connector, error) { +func (s *ConnectorService) ListForOrganizationID( + ctx context.Context, + organizationID gid.GID, + cursor *page.Cursor[coredata.ConnectorOrderField], +) (*page.Page[*coredata.Connector, coredata.ConnectorOrderField], error) { + var connectors coredata.Connectors + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return connectors.LoadWithoutDecryptedConnectionByOrganizationID( + ctx, + conn, + s.svc.scope, + organizationID, + cursor, + s.svc.encryptionKey, + ) + }, + ) + + if err != nil { + return nil, fmt.Errorf("cannot list connectors: %w", err) + } + + return page.NewPage(connectors, cursor), nil +} + +func (s *ConnectorService) CreateOrUpdate( + ctx context.Context, + req CreateOrUpdateConnectorRequest, +) (*coredata.Connector, error) { if req.OrganizationID == gid.Nil { return nil, fmt.Errorf("organization ID is required") } diff --git a/pkg/server/api/console/v1/resolver.go b/pkg/server/api/console/v1/resolver.go index afc7b37c3..5c0cab0e2 100644 --- a/pkg/server/api/console/v1/resolver.go +++ b/pkg/server/api/console/v1/resolver.go @@ -85,15 +85,13 @@ func NewMux(proboSvc *probo.Service, usrmgrSvc *usrmgr.Service, authCfg AuthConf r.Post("/auth/reset-password", ResetPasswordHandler(usrmgrSvc, authCfg)) r.Get("/connectors/initiate", WithSession(usrmgrSvc, authCfg, func(w http.ResponseWriter, r *http.Request) { - session := SessionFromContext(r.Context()) - if session == nil { - panic(fmt.Errorf("session not found")) + connectorID := r.URL.Query().Get("connector_id") + organizationID, err := gid.ParseGID(r.URL.Query().Get("organization_id")) + if err != nil { + panic(fmt.Errorf("failed to parse organization id: %w", err)) } - // TODO: check if current user has access to the organization - - connectorID := r.URL.Query().Get("connector_id") - organizationID := r.URL.Query().Get("organization_id") + _ = GetTenantService(r.Context(), proboSvc, organizationID.TenantID()) redirectURL, err := connectorRegistry.Initiate(r.Context(), connectorID, organizationID, r) if err != nil { @@ -104,28 +102,20 @@ func NewMux(proboSvc *probo.Service, usrmgrSvc *usrmgr.Service, authCfg AuthConf })) r.Get("/connectors/complete", WithSession(usrmgrSvc, authCfg, func(w http.ResponseWriter, r *http.Request) { - session := SessionFromContext(r.Context()) - if session == nil { - panic(fmt.Errorf("session not found")) - } - - // TODO: check if current user has access to the organization - connectorID := r.URL.Query().Get("connector_id") - organizationIDString := r.URL.Query().Get("organization_id") - - connection, err := connectorRegistry.Complete(r.Context(), connectorID, organizationIDString, r) - if err != nil { - panic(fmt.Errorf("failed to complete connector: %w", err)) - } - - organizationID, err := gid.ParseGID(organizationIDString) + organizationID, err := gid.ParseGID(r.URL.Query().Get("organization_id")) if err != nil { panic(fmt.Errorf("failed to parse organization id: %w", err)) } - tenantID := session.ID.TenantID() - _, err = proboSvc.WithTenant(tenantID).Connectors.CreateOrUpdate( + connection, err := connectorRegistry.Complete(r.Context(), connectorID, organizationID, r) + if err != nil { + panic(fmt.Errorf("failed to complete connector: %w", err)) + } + + svc := GetTenantService(r.Context(), proboSvc, organizationID.TenantID()) + + _, err = svc.Connectors.CreateOrUpdate( r.Context(), probo.CreateOrUpdateConnectorRequest{ OrganizationID: organizationID, @@ -282,7 +272,7 @@ func WithSession(usrmgrSvc *usrmgr.Service, authCfg AuthConfig, next http.Handle } } -func (r *Resolver) GetTenantServiceIfAuthorized(ctx context.Context, tenantID gid.TenantID) *probo.TenantService { +func GetTenantService(ctx context.Context, svc *probo.Service, tenantID gid.TenantID) *probo.TenantService { tenantIDs, _ := ctx.Value(userTenantContextKey).(*[]gid.TenantID) if tenantIDs == nil { @@ -291,7 +281,7 @@ func (r *Resolver) GetTenantServiceIfAuthorized(ctx context.Context, tenantID gi for _, id := range *tenantIDs { if id == tenantID { - return r.proboSvc.WithTenant(tenantID) + return svc.WithTenant(tenantID) } } diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql index e6ce2dd01..757418102 100644 --- a/pkg/server/api/console/v1/schema.graphql +++ b/pkg/server/api/console/v1/schema.graphql @@ -262,6 +262,20 @@ enum OrganizationOrderField { UPDATED_AT } +enum ConnectorOrderField + @goModel( + model: "github.com/getprobo/probo/pkg/coredata.ConnectorOrderField" + ) { + CREATED_AT + @goEnum( + value: "github.com/getprobo/probo/pkg/coredata.ConnectorOrderFieldCreatedAt" + ) + NAME + @goEnum( + value: "github.com/getprobo/probo/pkg/coredata.ConnectorOrderFieldName" + ) +} + # Order Input Types input UserOrder @goModel( @@ -356,6 +370,11 @@ input OrganizationOrder { field: OrganizationOrderField! } +input ConnectorOrder { + field: ConnectorOrderField! + direction: OrderDirection! +} + # Core Types type Organization implements Node { id: ID! @@ -370,6 +389,14 @@ type Organization implements Node { orderBy: UserOrder ): UserConnection! @goField(forceResolver: true) + connectors( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ConnectorOrder + ): ConnectorConnection! @goField(forceResolver: true) + frameworks( first: Int after: CursorKey @@ -430,6 +457,14 @@ type User implements Node { updatedAt: Datetime! } +type Connector implements Node { + id: ID! + name: String! + type: String! + createdAt: Datetime! + updatedAt: Datetime! +} + type People implements Node { id: ID! fullName: String! @@ -805,6 +840,16 @@ type VendorComplianceReportEdge { node: VendorComplianceReport! } +type ConnectorConnection { + edges: [ConnectorEdge!]! + pageInfo: PageInfo! +} + +type ConnectorEdge { + cursor: CursorKey! + node: Connector! +} + # Root Types type Query { node(id: ID!): Node! @@ -1213,6 +1258,12 @@ input RemoveUserInput { userId: ID! } +input InitiateConnectorInput { + organizationId: ID! + connectorId: String! + continueUrl: String! +} + # Payload Types type CreateOrganizationPayload { organizationEdge: OrganizationEdge! @@ -1389,3 +1440,7 @@ type InviteUserPayload { type RemoveUserPayload { success: Boolean! } + +type InitiateConnectorPayload { + redirectUrl: String! +} diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go index b3f55221c..0e079d2c3 100644 --- a/pkg/server/api/console/v1/schema/schema.go +++ b/pkg/server/api/console/v1/schema/schema.go @@ -69,6 +69,24 @@ type ComplexityRoot struct { Success func(childComplexity int) int } + Connector struct { + CreatedAt func(childComplexity int) int + ID func(childComplexity int) int + Name func(childComplexity int) int + Type func(childComplexity int) int + UpdatedAt func(childComplexity int) int + } + + ConnectorConnection struct { + Edges func(childComplexity int) int + PageInfo func(childComplexity int) int + } + + ConnectorEdge struct { + Cursor func(childComplexity int) int + Node func(childComplexity int) int + } + Control struct { CreatedAt func(childComplexity int) int Description func(childComplexity int) int @@ -249,6 +267,10 @@ type ComplexityRoot struct { MesureEdges func(childComplexity int) int } + InitiateConnectorPayload struct { + RedirectURL func(childComplexity int) int + } + InviteUserPayload struct { Success func(childComplexity int) int } @@ -325,6 +347,7 @@ type ComplexityRoot struct { } Organization struct { + Connectors func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ConnectorOrder) int CreatedAt func(childComplexity int) int Frameworks func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.FrameworkOrderBy) int ID func(childComplexity int) int @@ -655,6 +678,7 @@ type MutationResolver interface { type OrganizationResolver interface { LogoURL(ctx context.Context, obj *types.Organization) (*string, error) Users(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.UserOrderBy) (*types.UserConnection, error) + Connectors(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ConnectorOrder) (*types.ConnectorConnection, error) Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.FrameworkOrderBy) (*types.FrameworkConnection, error) Vendors(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error) Peoples(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PeopleOrderBy) (*types.PeopleConnection, error) @@ -727,6 +751,69 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ConfirmEmailPayload.Success(childComplexity), true + case "Connector.createdAt": + if e.complexity.Connector.CreatedAt == nil { + break + } + + return e.complexity.Connector.CreatedAt(childComplexity), true + + case "Connector.id": + if e.complexity.Connector.ID == nil { + break + } + + return e.complexity.Connector.ID(childComplexity), true + + case "Connector.name": + if e.complexity.Connector.Name == nil { + break + } + + return e.complexity.Connector.Name(childComplexity), true + + case "Connector.type": + if e.complexity.Connector.Type == nil { + break + } + + return e.complexity.Connector.Type(childComplexity), true + + case "Connector.updatedAt": + if e.complexity.Connector.UpdatedAt == nil { + break + } + + return e.complexity.Connector.UpdatedAt(childComplexity), true + + case "ConnectorConnection.edges": + if e.complexity.ConnectorConnection.Edges == nil { + break + } + + return e.complexity.ConnectorConnection.Edges(childComplexity), true + + case "ConnectorConnection.pageInfo": + if e.complexity.ConnectorConnection.PageInfo == nil { + break + } + + return e.complexity.ConnectorConnection.PageInfo(childComplexity), true + + case "ConnectorEdge.cursor": + if e.complexity.ConnectorEdge.Cursor == nil { + break + } + + return e.complexity.ConnectorEdge.Cursor(childComplexity), true + + case "ConnectorEdge.node": + if e.complexity.ConnectorEdge.Node == nil { + break + } + + return e.complexity.ConnectorEdge.Node(childComplexity), true + case "Control.createdAt": if e.complexity.Control.CreatedAt == nil { break @@ -1204,6 +1291,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ImportMesurePayload.MesureEdges(childComplexity), true + case "InitiateConnectorPayload.redirectUrl": + if e.complexity.InitiateConnectorPayload.RedirectURL == nil { + break + } + + return e.complexity.InitiateConnectorPayload.RedirectURL(childComplexity), true + case "InviteUserPayload.success": if e.complexity.InviteUserPayload.Success == nil { break @@ -1859,6 +1953,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.UploadVendorComplianceReport(childComplexity, args["input"].(types.UploadVendorComplianceReportInput)), true + case "Organization.connectors": + if e.complexity.Organization.Connectors == nil { + break + } + + args, err := ec.field_Organization_connectors_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Organization.Connectors(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ConnectorOrder)), true + case "Organization.createdAt": if e.complexity.Organization.CreatedAt == nil { break @@ -2966,6 +3072,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputAssignTaskInput, ec.unmarshalInputConfirmEmailInput, + ec.unmarshalInputConnectorOrder, ec.unmarshalInputControlOrder, ec.unmarshalInputCreateControlMesureMappingInput, ec.unmarshalInputCreateControlPolicyMappingInput, @@ -2998,6 +3105,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputFulfillEvidenceInput, ec.unmarshalInputImportFrameworkInput, ec.unmarshalInputImportMesureInput, + ec.unmarshalInputInitiateConnectorInput, ec.unmarshalInputInviteUserInput, ec.unmarshalInputMesureOrder, ec.unmarshalInputOrganizationOrder, @@ -3381,6 +3489,20 @@ enum OrganizationOrderField { UPDATED_AT } +enum ConnectorOrderField + @goModel( + model: "github.com/getprobo/probo/pkg/coredata.ConnectorOrderField" + ) { + CREATED_AT + @goEnum( + value: "github.com/getprobo/probo/pkg/coredata.ConnectorOrderFieldCreatedAt" + ) + NAME + @goEnum( + value: "github.com/getprobo/probo/pkg/coredata.ConnectorOrderFieldName" + ) +} + # Order Input Types input UserOrder @goModel( @@ -3475,6 +3597,11 @@ input OrganizationOrder { field: OrganizationOrderField! } +input ConnectorOrder { + field: ConnectorOrderField! + direction: OrderDirection! +} + # Core Types type Organization implements Node { id: ID! @@ -3489,6 +3616,14 @@ type Organization implements Node { orderBy: UserOrder ): UserConnection! @goField(forceResolver: true) + connectors( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ConnectorOrder + ): ConnectorConnection! @goField(forceResolver: true) + frameworks( first: Int after: CursorKey @@ -3549,6 +3684,14 @@ type User implements Node { updatedAt: Datetime! } +type Connector implements Node { + id: ID! + name: String! + type: String! + createdAt: Datetime! + updatedAt: Datetime! +} + type People implements Node { id: ID! fullName: String! @@ -3924,6 +4067,16 @@ type VendorComplianceReportEdge { node: VendorComplianceReport! } +type ConnectorConnection { + edges: [ConnectorEdge!]! + pageInfo: PageInfo! +} + +type ConnectorEdge { + cursor: CursorKey! + node: Connector! +} + # Root Types type Query { node(id: ID!): Node! @@ -4332,6 +4485,12 @@ input RemoveUserInput { userId: ID! } +input InitiateConnectorInput { + organizationId: ID! + connectorId: String! + continueUrl: String! +} + # Payload Types type CreateOrganizationPayload { organizationEdge: OrganizationEdge! @@ -4508,6 +4667,10 @@ type InviteUserPayload { type RemoveUserPayload { success: Boolean! } + +type InitiateConnectorPayload { + redirectUrl: String! +} `, BuiltIn: false}, } var parsedSchema = gqlparser.MustLoadSchema(sources...) @@ -6098,6 +6261,101 @@ func (ec *executionContext) field_Mutation_uploadVendorComplianceReport_argsInpu return zeroVal, nil } +func (ec *executionContext) field_Organization_connectors_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Organization_connectors_argsFirst(ctx, rawArgs) + if err != nil { + return nil, err + } + args["first"] = arg0 + arg1, err := ec.field_Organization_connectors_argsAfter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["after"] = arg1 + arg2, err := ec.field_Organization_connectors_argsLast(ctx, rawArgs) + if err != nil { + return nil, err + } + args["last"] = arg2 + arg3, err := ec.field_Organization_connectors_argsBefore(ctx, rawArgs) + if err != nil { + return nil, err + } + args["before"] = arg3 + arg4, err := ec.field_Organization_connectors_argsOrderBy(ctx, rawArgs) + if err != nil { + return nil, err + } + args["orderBy"] = arg4 + return args, nil +} +func (ec *executionContext) field_Organization_connectors_argsFirst( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + if tmp, ok := rawArgs["first"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Organization_connectors_argsAfter( + ctx context.Context, + rawArgs map[string]any, +) (*page.CursorKey, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + if tmp, ok := rawArgs["after"]; ok { + return ec.unmarshalOCursorKey2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋpageᚐCursorKey(ctx, tmp) + } + + var zeroVal *page.CursorKey + return zeroVal, nil +} + +func (ec *executionContext) field_Organization_connectors_argsLast( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) + if tmp, ok := rawArgs["last"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Organization_connectors_argsBefore( + ctx context.Context, + rawArgs map[string]any, +) (*page.CursorKey, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) + if tmp, ok := rawArgs["before"]; ok { + return ec.unmarshalOCursorKey2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋpageᚐCursorKey(ctx, tmp) + } + + var zeroVal *page.CursorKey + return zeroVal, nil +} + +func (ec *executionContext) field_Organization_connectors_argsOrderBy( + ctx context.Context, + rawArgs map[string]any, +) (*types.ConnectorOrder, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("orderBy")) + if tmp, ok := rawArgs["orderBy"]; ok { + return ec.unmarshalOConnectorOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐConnectorOrder(ctx, tmp) + } + + var zeroVal *types.ConnectorOrder + return zeroVal, nil +} + func (ec *executionContext) field_Organization_frameworks_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -7682,6 +7940,430 @@ func (ec *executionContext) fieldContext_ConfirmEmailPayload_success(_ context.C return fc, nil } +func (ec *executionContext) _Connector_id(ctx context.Context, field graphql.CollectedField, obj *types.Connector) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Connector_id(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(gid.GID) + fc.Result = res + return ec.marshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Connector_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Connector", + 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) _Connector_name(ctx context.Context, field graphql.CollectedField, obj *types.Connector) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Connector_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Connector_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Connector", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Connector_type(ctx context.Context, field graphql.CollectedField, obj *types.Connector) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Connector_type(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Connector_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Connector", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Connector_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Connector) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Connector_createdAt(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.CreatedAt, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(time.Time) + fc.Result = res + return ec.marshalNDatetime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Connector_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Connector", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Datetime does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Connector_updatedAt(ctx context.Context, field graphql.CollectedField, obj *types.Connector) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Connector_updatedAt(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.UpdatedAt, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(time.Time) + fc.Result = res + return ec.marshalNDatetime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Connector_updatedAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Connector", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Datetime does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ConnectorConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.ConnectorConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ConnectorConnection_edges(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Edges, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]*types.ConnectorEdge) + fc.Result = res + return ec.marshalNConnectorEdge2ᚕᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐConnectorEdgeᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ConnectorConnection_edges(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ConnectorConnection", + 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_ConnectorEdge_cursor(ctx, field) + case "node": + return ec.fieldContext_ConnectorEdge_node(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ConnectorEdge", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _ConnectorConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *types.ConnectorConnection) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ConnectorConnection_pageInfo(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.PageInfo, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.PageInfo) + fc.Result = res + return ec.marshalNPageInfo2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐPageInfo(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ConnectorConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ConnectorConnection", + 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) _ConnectorEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *types.ConnectorEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ConnectorEdge_cursor(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Cursor, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(page.CursorKey) + fc.Result = res + return ec.marshalNCursorKey2githubᚗcomᚋgetproboᚋproboᚋpkgᚋpageᚐCursorKey(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ConnectorEdge_cursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ConnectorEdge", + 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) _ConnectorEdge_node(ctx context.Context, field graphql.CollectedField, obj *types.ConnectorEdge) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ConnectorEdge_node(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.Node, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.Connector) + fc.Result = res + return ec.marshalNConnector2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐConnector(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ConnectorEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ConnectorEdge", + 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_Connector_id(ctx, field) + case "name": + return ec.fieldContext_Connector_name(ctx, field) + case "type": + return ec.fieldContext_Connector_type(ctx, field) + case "createdAt": + return ec.fieldContext_Connector_createdAt(ctx, field) + case "updatedAt": + return ec.fieldContext_Connector_updatedAt(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Connector", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Control_id(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Control_id(ctx, field) if err != nil { @@ -10807,6 +11489,50 @@ func (ec *executionContext) fieldContext_ImportMesurePayload_mesureEdges(_ conte return fc, nil } +func (ec *executionContext) _InitiateConnectorPayload_redirectUrl(ctx context.Context, field graphql.CollectedField, obj *types.InitiateConnectorPayload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_InitiateConnectorPayload_redirectUrl(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.RedirectURL, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_InitiateConnectorPayload_redirectUrl(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "InitiateConnectorPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _InviteUserPayload_success(ctx context.Context, field graphql.CollectedField, obj *types.InviteUserPayload) (ret graphql.Marshaler) { fc, err := ec.fieldContext_InviteUserPayload_success(ctx, field) if err != nil { @@ -14388,6 +15114,67 @@ func (ec *executionContext) fieldContext_Organization_users(ctx context.Context, return fc, nil } +func (ec *executionContext) _Organization_connectors(ctx context.Context, field graphql.CollectedField, obj *types.Organization) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Organization_connectors(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Organization().Connectors(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ConnectorOrder)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.ConnectorConnection) + fc.Result = res + return ec.marshalNConnectorConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐConnectorConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Organization_connectors(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Organization", + 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_ConnectorConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_ConnectorConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ConnectorConnection", 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_Organization_connectors_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Organization_frameworks(ctx context.Context, field graphql.CollectedField, obj *types.Organization) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Organization_frameworks(ctx, field) if err != nil { @@ -15037,6 +15824,8 @@ func (ec *executionContext) fieldContext_OrganizationEdge_node(_ context.Context return ec.fieldContext_Organization_logoUrl(ctx, field) case "users": return ec.fieldContext_Organization_users(ctx, field) + case "connectors": + return ec.fieldContext_Organization_connectors(ctx, field) case "frameworks": return ec.fieldContext_Organization_frameworks(ctx, field) case "vendors": @@ -18716,6 +19505,8 @@ func (ec *executionContext) fieldContext_UpdateOrganizationPayload_organization( return ec.fieldContext_Organization_logoUrl(ctx, field) case "users": return ec.fieldContext_Organization_users(ctx, field) + case "connectors": + return ec.fieldContext_Organization_connectors(ctx, field) case "frameworks": return ec.fieldContext_Organization_frameworks(ctx, field) case "vendors": @@ -23658,6 +24449,40 @@ func (ec *executionContext) unmarshalInputConfirmEmailInput(ctx context.Context, return it, nil } +func (ec *executionContext) unmarshalInputConnectorOrder(ctx context.Context, obj any) (types.ConnectorOrder, error) { + var it types.ConnectorOrder + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"field", "direction"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "field": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("field")) + data, err := ec.unmarshalNConnectorOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐConnectorOrderField(ctx, v) + if err != nil { + return it, err + } + it.Field = data + case "direction": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("direction")) + data, err := ec.unmarshalNOrderDirection2githubᚗcomᚋgetproboᚋproboᚋpkgᚋpageᚐOrderDirection(ctx, v) + if err != nil { + return it, err + } + it.Direction = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputControlOrder(ctx context.Context, obj any) (types.ControlOrderBy, error) { var it types.ControlOrderBy asMap := map[string]any{} @@ -25005,6 +25830,47 @@ func (ec *executionContext) unmarshalInputImportMesureInput(ctx context.Context, return it, nil } +func (ec *executionContext) unmarshalInputInitiateConnectorInput(ctx context.Context, obj any) (types.InitiateConnectorInput, error) { + var it types.InitiateConnectorInput + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"organizationId", "connectorId", "continueUrl"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "organizationId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("organizationId")) + data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.OrganizationID = data + case "connectorId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("connectorId")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.ConnectorID = data + case "continueUrl": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("continueUrl")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.ContinueURL = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputInviteUserInput(ctx context.Context, obj any) (types.InviteUserInput, error) { var it types.InviteUserInput asMap := map[string]any{} @@ -26181,6 +27047,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._Control(ctx, sel, obj) + case types.Connector: + return ec._Connector(ctx, sel, &obj) + case *types.Connector: + if obj == nil { + return graphql.Null + } + return ec._Connector(ctx, sel, obj) default: panic(fmt.Errorf("unexpected type %T", obj)) } @@ -26268,6 +27141,153 @@ func (ec *executionContext) _ConfirmEmailPayload(ctx context.Context, sel ast.Se return out } +var connectorImplementors = []string{"Connector", "Node"} + +func (ec *executionContext) _Connector(ctx context.Context, sel ast.SelectionSet, obj *types.Connector) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, connectorImplementors) + + 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("Connector") + case "id": + out.Values[i] = ec._Connector_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "name": + out.Values[i] = ec._Connector_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "type": + out.Values[i] = ec._Connector_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "createdAt": + out.Values[i] = ec._Connector_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "updatedAt": + out.Values[i] = ec._Connector_updatedAt(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 connectorConnectionImplementors = []string{"ConnectorConnection"} + +func (ec *executionContext) _ConnectorConnection(ctx context.Context, sel ast.SelectionSet, obj *types.ConnectorConnection) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, connectorConnectionImplementors) + + 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("ConnectorConnection") + case "edges": + out.Values[i] = ec._ConnectorConnection_edges(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pageInfo": + out.Values[i] = ec._ConnectorConnection_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 connectorEdgeImplementors = []string{"ConnectorEdge"} + +func (ec *executionContext) _ConnectorEdge(ctx context.Context, sel ast.SelectionSet, obj *types.ConnectorEdge) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, connectorEdgeImplementors) + + 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("ConnectorEdge") + case "cursor": + out.Values[i] = ec._ConnectorEdge_cursor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "node": + out.Values[i] = ec._ConnectorEdge_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 controlImplementors = []string{"Control", "Node"} func (ec *executionContext) _Control(ctx context.Context, sel ast.SelectionSet, obj *types.Control) graphql.Marshaler { @@ -28008,6 +29028,45 @@ func (ec *executionContext) _ImportMesurePayload(ctx context.Context, sel ast.Se return out } +var initiateConnectorPayloadImplementors = []string{"InitiateConnectorPayload"} + +func (ec *executionContext) _InitiateConnectorPayload(ctx context.Context, sel ast.SelectionSet, obj *types.InitiateConnectorPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, initiateConnectorPayloadImplementors) + + 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("InitiateConnectorPayload") + case "redirectUrl": + out.Values[i] = ec._InitiateConnectorPayload_redirectUrl(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 inviteUserPayloadImplementors = []string{"InviteUserPayload"} func (ec *executionContext) _InviteUserPayload(ctx context.Context, sel ast.SelectionSet, obj *types.InviteUserPayload) graphql.Marshaler { @@ -28756,6 +29815,42 @@ func (ec *executionContext) _Organization(ctx context.Context, sel ast.Selection continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "connectors": + 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._Organization_connectors(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) }) case "frameworks": field := field @@ -31811,6 +32906,111 @@ func (ec *executionContext) marshalNConfirmEmailPayload2ᚖgithubᚗcomᚋgetpro return ec._ConfirmEmailPayload(ctx, sel, v) } +func (ec *executionContext) marshalNConnector2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐConnector(ctx context.Context, sel ast.SelectionSet, v *types.Connector) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Connector(ctx, sel, v) +} + +func (ec *executionContext) marshalNConnectorConnection2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐConnectorConnection(ctx context.Context, sel ast.SelectionSet, v types.ConnectorConnection) graphql.Marshaler { + return ec._ConnectorConnection(ctx, sel, &v) +} + +func (ec *executionContext) marshalNConnectorConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐConnectorConnection(ctx context.Context, sel ast.SelectionSet, v *types.ConnectorConnection) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ConnectorConnection(ctx, sel, v) +} + +func (ec *executionContext) marshalNConnectorEdge2ᚕᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐConnectorEdgeᚄ(ctx context.Context, sel ast.SelectionSet, v []*types.ConnectorEdge) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNConnectorEdge2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐConnectorEdge(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNConnectorEdge2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐConnectorEdge(ctx context.Context, sel ast.SelectionSet, v *types.ConnectorEdge) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._ConnectorEdge(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNConnectorOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐConnectorOrderField(ctx context.Context, v any) (coredata.ConnectorOrderField, error) { + tmp, err := graphql.UnmarshalString(v) + res := unmarshalNConnectorOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐConnectorOrderField[tmp] + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNConnectorOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐConnectorOrderField(ctx context.Context, sel ast.SelectionSet, v coredata.ConnectorOrderField) graphql.Marshaler { + res := graphql.MarshalString(marshalNConnectorOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐConnectorOrderField[v]) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +var ( + unmarshalNConnectorOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐConnectorOrderField = map[string]coredata.ConnectorOrderField{ + "CREATED_AT": coredata.ConnectorOrderFieldCreatedAt, + "NAME": coredata.ConnectorOrderFieldName, + } + marshalNConnectorOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐConnectorOrderField = map[coredata.ConnectorOrderField]string{ + coredata.ConnectorOrderFieldCreatedAt: "CREATED_AT", + coredata.ConnectorOrderFieldName: "NAME", + } +) + func (ec *executionContext) marshalNControl2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControl(ctx context.Context, sel ast.SelectionSet, v *types.Control) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -34550,6 +35750,14 @@ func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast return res } +func (ec *executionContext) unmarshalOConnectorOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐConnectorOrder(ctx context.Context, v any) (*types.ConnectorOrder, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalInputConnectorOrder(ctx, v) + return &res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalOControlOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlOrderBy(ctx context.Context, v any) (*types.ControlOrderBy, error) { if v == nil { return nil, nil diff --git a/pkg/server/api/console/v1/types/connector.go b/pkg/server/api/console/v1/types/connector.go new file mode 100644 index 000000000..f8657ba8b --- /dev/null +++ b/pkg/server/api/console/v1/types/connector.go @@ -0,0 +1,54 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package types + +import ( + "github.com/getprobo/probo/pkg/coredata" + "github.com/getprobo/probo/pkg/page" +) + +type ( + ConnectorOrderBy OrderBy[coredata.ConnectorOrderField] +) + +func NewConnectorConnection(p *page.Page[*coredata.Connector, coredata.ConnectorOrderField]) *ConnectorConnection { + var edges = make([]*ConnectorEdge, len(p.Data)) + + for i := range edges { + edges[i] = NewConnectorEdge(p.Data[i], p.Cursor.OrderBy.Field) + } + + return &ConnectorConnection{ + Edges: edges, + PageInfo: NewPageInfo(p), + } +} + +func NewConnectorEdge(c *coredata.Connector, orderBy coredata.ConnectorOrderField) *ConnectorEdge { + return &ConnectorEdge{ + Cursor: c.CursorKey(orderBy), + Node: NewConnector(c), + } +} + +func NewConnector(c *coredata.Connector) *Connector { + return &Connector{ + ID: c.ID, + Name: c.Name, + Type: string(c.Type), + CreatedAt: c.CreatedAt, + UpdatedAt: c.UpdatedAt, + } +} diff --git a/pkg/server/api/console/v1/types/types.go b/pkg/server/api/console/v1/types/types.go index 54b03efb1..de209363f 100644 --- a/pkg/server/api/console/v1/types/types.go +++ b/pkg/server/api/console/v1/types/types.go @@ -36,6 +36,32 @@ type ConfirmEmailPayload struct { Success bool `json:"success"` } +type Connector struct { + ID gid.GID `json:"id"` + Name string `json:"name"` + Type string `json:"type"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +func (Connector) IsNode() {} +func (this Connector) GetID() gid.GID { return this.ID } + +type ConnectorConnection struct { + Edges []*ConnectorEdge `json:"edges"` + PageInfo *PageInfo `json:"pageInfo"` +} + +type ConnectorEdge struct { + Cursor page.CursorKey `json:"cursor"` + Node *Connector `json:"node"` +} + +type ConnectorOrder struct { + Field coredata.ConnectorOrderField `json:"field"` + Direction page.OrderDirection `json:"direction"` +} + type Control struct { ID gid.GID `json:"id"` ReferenceID string `json:"referenceId"` @@ -407,6 +433,16 @@ type ImportMesurePayload struct { MesureEdges []*MesureEdge `json:"mesureEdges"` } +type InitiateConnectorInput struct { + OrganizationID gid.GID `json:"organizationId"` + ConnectorID string `json:"connectorId"` + ContinueURL string `json:"continueUrl"` +} + +type InitiateConnectorPayload struct { + RedirectURL string `json:"redirectUrl"` +} + type InviteUserInput struct { OrganizationID gid.GID `json:"organizationId"` Email string `json:"email"` @@ -452,6 +488,7 @@ type Organization struct { Name string `json:"name"` LogoURL *string `json:"logoUrl,omitempty"` Users *UserConnection `json:"users"` + Connectors *ConnectorConnection `json:"connectors"` Frameworks *FrameworkConnection `json:"frameworks"` Vendors *VendorConnection `json:"vendors"` Peoples *PeopleConnection `json:"peoples"` diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index 441878986..560fcc376 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -22,7 +22,7 @@ import ( // Mesures is the resolver for the mesures field. func (r *controlResolver) Mesures(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MesureOrderBy) (*types.MesureConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.MesureOrderField]{ Field: coredata.MesureOrderFieldCreatedAt, @@ -47,7 +47,7 @@ func (r *controlResolver) Mesures(ctx context.Context, obj *types.Control, first // Policies is the resolver for the policies field. func (r *controlResolver) Policies(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyOrderBy) (*types.PolicyConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.PolicyOrderField]{ Field: coredata.PolicyOrderFieldCreatedAt, @@ -72,7 +72,7 @@ func (r *controlResolver) Policies(ctx context.Context, obj *types.Control, firs // FileURL is the resolver for the fileUrl field. func (r *evidenceResolver) FileURL(ctx context.Context, obj *types.Evidence) (*string, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) if obj.Type == coredata.EvidenceTypeLink { return obj.URL, nil @@ -89,7 +89,7 @@ func (r *evidenceResolver) FileURL(ctx context.Context, obj *types.Evidence) (*s // Controls is the resolver for the controls field. func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.ControlOrderField]{ Field: coredata.ControlOrderFieldCreatedAt, @@ -114,7 +114,7 @@ func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework, // Tasks is the resolver for the tasks field. func (r *mesureResolver) Tasks(ctx context.Context, obj *types.Mesure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TaskOrderBy) (*types.TaskConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.TaskOrderField]{ Field: coredata.TaskOrderFieldCreatedAt, @@ -139,7 +139,7 @@ func (r *mesureResolver) Tasks(ctx context.Context, obj *types.Mesure, first *in // Risks is the resolver for the risks field. func (r *mesureResolver) Risks(ctx context.Context, obj *types.Mesure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.RiskOrderBy) (*types.RiskConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.RiskOrderField]{ Field: coredata.RiskOrderFieldCreatedAt, @@ -164,7 +164,7 @@ func (r *mesureResolver) Risks(ctx context.Context, obj *types.Mesure, first *in // Controls is the resolver for the controls field. func (r *mesureResolver) Controls(ctx context.Context, obj *types.Mesure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.ControlOrderField]{ Field: coredata.ControlOrderFieldCreatedAt, @@ -213,7 +213,7 @@ func (r *mutationResolver) CreateOrganization(ctx context.Context, input types.C // UpdateOrganization is the resolver for the updateOrganization field. func (r *mutationResolver) UpdateOrganization(ctx context.Context, input types.UpdateOrganizationInput) (*types.UpdateOrganizationPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID()) req := probo.UpdateOrganizationRequest{ ID: input.OrganizationID, @@ -298,7 +298,7 @@ func (r *mutationResolver) RemoveUser(ctx context.Context, input types.RemoveUse // CreatePeople is the resolver for the createPeople field. func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreatePeopleInput) (*types.CreatePeoplePayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID()) people, err := svc.Peoples.Create(ctx, probo.CreatePeopleRequest{ OrganizationID: input.OrganizationID, @@ -319,7 +319,7 @@ func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreateP // UpdatePeople is the resolver for the updatePeople field. func (r *mutationResolver) UpdatePeople(ctx context.Context, input types.UpdatePeopleInput) (*types.UpdatePeoplePayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.ID.TenantID()) people, err := svc.Peoples.Update(ctx, probo.UpdatePeopleRequest{ ID: input.ID, @@ -339,7 +339,7 @@ func (r *mutationResolver) UpdatePeople(ctx context.Context, input types.UpdateP // DeletePeople is the resolver for the deletePeople field. func (r *mutationResolver) DeletePeople(ctx context.Context, input types.DeletePeopleInput) (*types.DeletePeoplePayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.PeopleID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.PeopleID.TenantID()) err := svc.Peoples.Delete(ctx, input.PeopleID) if err != nil { @@ -353,7 +353,7 @@ func (r *mutationResolver) DeletePeople(ctx context.Context, input types.DeleteP // CreateVendor is the resolver for the createVendor field. func (r *mutationResolver) CreateVendor(ctx context.Context, input types.CreateVendorInput) (*types.CreateVendorPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID()) vendor, err := svc.Vendors.Create( ctx, @@ -391,7 +391,7 @@ func (r *mutationResolver) CreateVendor(ctx context.Context, input types.CreateV // UpdateVendor is the resolver for the updateVendor field. func (r *mutationResolver) UpdateVendor(ctx context.Context, input types.UpdateVendorInput) (*types.UpdateVendorPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.ID.TenantID()) vendor, err := svc.Vendors.Update(ctx, probo.UpdateVendorRequest{ ID: input.ID, @@ -427,7 +427,7 @@ func (r *mutationResolver) UpdateVendor(ctx context.Context, input types.UpdateV // DeleteVendor is the resolver for the deleteVendor field. func (r *mutationResolver) DeleteVendor(ctx context.Context, input types.DeleteVendorInput) (*types.DeleteVendorPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.VendorID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.VendorID.TenantID()) err := svc.Vendors.Delete(ctx, input.VendorID) if err != nil { @@ -441,7 +441,7 @@ func (r *mutationResolver) DeleteVendor(ctx context.Context, input types.DeleteV // CreateFramework is the resolver for the createFramework field. func (r *mutationResolver) CreateFramework(ctx context.Context, input types.CreateFrameworkInput) (*types.CreateFrameworkPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID()) framework, err := svc.Frameworks.Create(ctx, probo.CreateFrameworkRequest{ OrganizationID: input.OrganizationID, @@ -458,7 +458,7 @@ func (r *mutationResolver) CreateFramework(ctx context.Context, input types.Crea // UpdateFramework is the resolver for the updateFramework field. func (r *mutationResolver) UpdateFramework(ctx context.Context, input types.UpdateFrameworkInput) (*types.UpdateFrameworkPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.ID.TenantID()) framework, err := svc.Frameworks.Update(ctx, probo.UpdateFrameworkRequest{ ID: input.ID, @@ -476,7 +476,7 @@ func (r *mutationResolver) UpdateFramework(ctx context.Context, input types.Upda // ImportFramework is the resolver for the importFramework field. func (r *mutationResolver) ImportFramework(ctx context.Context, input types.ImportFrameworkInput) (*types.ImportFrameworkPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID()) req := probo.ImportFrameworkRequest{} if err := json.NewDecoder(input.File.File).Decode(&req.Framework); err != nil { @@ -506,7 +506,7 @@ func (r *mutationResolver) ImportFramework(ctx context.Context, input types.Impo // DeleteFramework is the resolver for the deleteFramework field. func (r *mutationResolver) DeleteFramework(ctx context.Context, input types.DeleteFrameworkInput) (*types.DeleteFrameworkPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.FrameworkID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.FrameworkID.TenantID()) err := svc.Frameworks.Delete(ctx, input.FrameworkID) if err != nil { @@ -520,7 +520,7 @@ func (r *mutationResolver) DeleteFramework(ctx context.Context, input types.Dele // // CreateMesure is the resolver for the createMesure field. func (r *mutationResolver) CreateMesure(ctx context.Context, input types.CreateMesureInput) (*types.CreateMesurePayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID()) mesure, err := svc.Mesures.Create(ctx, probo.CreateMesureRequest{ OrganizationID: input.OrganizationID, @@ -540,7 +540,7 @@ func (r *mutationResolver) CreateMesure(ctx context.Context, input types.CreateM // UpdateMesure is the resolver for the updateMesure field. func (r *mutationResolver) UpdateMesure(ctx context.Context, input types.UpdateMesureInput) (*types.UpdateMesurePayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.ID.TenantID()) mesure, err := svc.Mesures.Update(ctx, probo.UpdateMesureRequest{ ID: input.ID, @@ -561,7 +561,7 @@ func (r *mutationResolver) UpdateMesure(ctx context.Context, input types.UpdateM // ImportMesure is the resolver for the importMesure field. func (r *mutationResolver) ImportMesure(ctx context.Context, input types.ImportMesureInput) (*types.ImportMesurePayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID()) var req probo.ImportMesureRequest if err := json.NewDecoder(input.File.File).Decode(&req.Mesures); err != nil { @@ -585,7 +585,7 @@ func (r *mutationResolver) ImportMesure(ctx context.Context, input types.ImportM // CreateControlMesureMapping is the resolver for the createControlMesureMapping field. func (r *mutationResolver) CreateControlMesureMapping(ctx context.Context, input types.CreateControlMesureMappingInput) (*types.CreateControlMesureMappingPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.MesureID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.MesureID.TenantID()) err := svc.Controls.CreateMesureMapping(ctx, input.ControlID, input.MesureID) if err != nil { @@ -599,7 +599,7 @@ func (r *mutationResolver) CreateControlMesureMapping(ctx context.Context, input // CreateControlPolicyMapping is the resolver for the createControlPolicyMapping field. func (r *mutationResolver) CreateControlPolicyMapping(ctx context.Context, input types.CreateControlPolicyMappingInput) (*types.CreateControlPolicyMappingPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.PolicyID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.PolicyID.TenantID()) err := svc.Controls.CreatePolicyMapping(ctx, input.ControlID, input.PolicyID) if err != nil { @@ -613,7 +613,7 @@ func (r *mutationResolver) CreateControlPolicyMapping(ctx context.Context, input // DeleteControlMesureMapping is the resolver for the deleteControlMesureMapping field. func (r *mutationResolver) DeleteControlMesureMapping(ctx context.Context, input types.DeleteControlMesureMappingInput) (*types.DeleteControlMesureMappingPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.MesureID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.MesureID.TenantID()) err := svc.Controls.DeleteMesureMapping(ctx, input.ControlID, input.MesureID) if err != nil { @@ -627,7 +627,7 @@ func (r *mutationResolver) DeleteControlMesureMapping(ctx context.Context, input // DeleteControlPolicyMapping is the resolver for the deleteControlPolicyMapping field. func (r *mutationResolver) DeleteControlPolicyMapping(ctx context.Context, input types.DeleteControlPolicyMappingInput) (*types.DeleteControlPolicyMappingPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.PolicyID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.PolicyID.TenantID()) err := svc.Controls.DeletePolicyMapping(ctx, input.ControlID, input.PolicyID) if err != nil { @@ -641,7 +641,7 @@ func (r *mutationResolver) DeleteControlPolicyMapping(ctx context.Context, input // CreateTask is the resolver for the createTask field. func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTaskInput) (*types.CreateTaskPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.MesureID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.MesureID.TenantID()) task, err := svc.Tasks.Create(ctx, probo.CreateTaskRequest{ MesureID: input.MesureID, @@ -660,7 +660,7 @@ func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTas // UpdateTask is the resolver for the updateTask field. func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTaskInput) (*types.UpdateTaskPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.TaskID.TenantID()) task, err := svc.Tasks.Update(ctx, probo.UpdateTaskRequest{ TaskID: input.TaskID, @@ -680,7 +680,7 @@ func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTas // DeleteTask is the resolver for the deleteTask field. func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTaskInput) (*types.DeleteTaskPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.TaskID.TenantID()) err := svc.Tasks.Delete(ctx, input.TaskID) if err != nil { @@ -694,7 +694,7 @@ func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTas // AssignTask is the resolver for the assignTask field. func (r *mutationResolver) AssignTask(ctx context.Context, input types.AssignTaskInput) (*types.AssignTaskPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.TaskID.TenantID()) task, err := svc.Tasks.Assign(ctx, input.TaskID, input.AssignedToID) if err != nil { @@ -708,7 +708,7 @@ func (r *mutationResolver) AssignTask(ctx context.Context, input types.AssignTas // UnassignTask is the resolver for the unassignTask field. func (r *mutationResolver) UnassignTask(ctx context.Context, input types.UnassignTaskInput) (*types.UnassignTaskPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.TaskID.TenantID()) task, err := svc.Tasks.Unassign(ctx, input.TaskID) if err != nil { @@ -722,7 +722,7 @@ func (r *mutationResolver) UnassignTask(ctx context.Context, input types.Unassig // CreateRisk is the resolver for the createRisk field. func (r *mutationResolver) CreateRisk(ctx context.Context, input types.CreateRiskInput) (*types.CreateRiskPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID()) risk, err := svc.Risks.Create( ctx, @@ -750,7 +750,7 @@ func (r *mutationResolver) CreateRisk(ctx context.Context, input types.CreateRis // UpdateRisk is the resolver for the updateRisk field. func (r *mutationResolver) UpdateRisk(ctx context.Context, input types.UpdateRiskInput) (*types.UpdateRiskPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.ID.TenantID()) risk, err := svc.Risks.Update( ctx, @@ -778,7 +778,7 @@ func (r *mutationResolver) UpdateRisk(ctx context.Context, input types.UpdateRis // DeleteRisk is the resolver for the deleteRisk field. func (r *mutationResolver) DeleteRisk(ctx context.Context, input types.DeleteRiskInput) (*types.DeleteRiskPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.RiskID.TenantID()) err := svc.Risks.Delete(ctx, input.RiskID) if err != nil { @@ -792,7 +792,7 @@ func (r *mutationResolver) DeleteRisk(ctx context.Context, input types.DeleteRis // CreateRiskMesureMapping is the resolver for the createRiskMesureMapping field. func (r *mutationResolver) CreateRiskMesureMapping(ctx context.Context, input types.CreateRiskMesureMappingInput) (*types.CreateRiskMesureMappingPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.RiskID.TenantID()) err := svc.Risks.CreateMesureMapping(ctx, input.RiskID, input.MesureID) if err != nil { @@ -806,7 +806,7 @@ func (r *mutationResolver) CreateRiskMesureMapping(ctx context.Context, input ty // DeleteRiskMesureMapping is the resolver for the deleteRiskMesureMapping field. func (r *mutationResolver) DeleteRiskMesureMapping(ctx context.Context, input types.DeleteRiskMesureMappingInput) (*types.DeleteRiskMesureMappingPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.RiskID.TenantID()) err := svc.Risks.DeleteMesureMapping(ctx, input.RiskID, input.MesureID) if err != nil { @@ -820,7 +820,7 @@ func (r *mutationResolver) DeleteRiskMesureMapping(ctx context.Context, input ty // CreateRiskPolicyMapping is the resolver for the createRiskPolicyMapping field. func (r *mutationResolver) CreateRiskPolicyMapping(ctx context.Context, input types.CreateRiskPolicyMappingInput) (*types.CreateRiskPolicyMappingPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.RiskID.TenantID()) err := svc.Risks.CreatePolicyMapping(ctx, input.RiskID, input.PolicyID) if err != nil { @@ -834,7 +834,7 @@ func (r *mutationResolver) CreateRiskPolicyMapping(ctx context.Context, input ty // DeleteRiskPolicyMapping is the resolver for the deleteRiskPolicyMapping field. func (r *mutationResolver) DeleteRiskPolicyMapping(ctx context.Context, input types.DeleteRiskPolicyMappingInput) (*types.DeleteRiskPolicyMappingPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.RiskID.TenantID()) err := svc.Risks.DeletePolicyMapping(ctx, input.RiskID, input.PolicyID) if err != nil { @@ -848,7 +848,7 @@ func (r *mutationResolver) DeleteRiskPolicyMapping(ctx context.Context, input ty // RequestEvidence is the resolver for the requestEvidence field. func (r *mutationResolver) RequestEvidence(ctx context.Context, input types.RequestEvidenceInput) (*types.RequestEvidencePayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.TaskID.TenantID()) evidence, err := svc.Evidences.Request( ctx, @@ -870,7 +870,7 @@ func (r *mutationResolver) RequestEvidence(ctx context.Context, input types.Requ // FulfillEvidence is the resolver for the fulfillEvidence field. func (r *mutationResolver) FulfillEvidence(ctx context.Context, input types.FulfillEvidenceInput) (*types.FulfillEvidencePayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.EvidenceID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.EvidenceID.TenantID()) req := probo.FulfilledEvidenceRequest{ EvidenceID: input.EvidenceID, @@ -897,7 +897,7 @@ func (r *mutationResolver) FulfillEvidence(ctx context.Context, input types.Fulf // CreateEvidence is the resolver for the createEvidence field. func (r *mutationResolver) CreateEvidence(ctx context.Context, input types.CreateEvidenceInput) (*types.CreateEvidencePayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.TaskID.TenantID()) req := probo.CreateEvidenceRequest{ TaskID: input.TaskID, @@ -929,7 +929,7 @@ func (r *mutationResolver) CreateEvidence(ctx context.Context, input types.Creat // DeleteEvidence is the resolver for the deleteEvidence field. func (r *mutationResolver) DeleteEvidence(ctx context.Context, input types.DeleteEvidenceInput) (*types.DeleteEvidencePayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.EvidenceID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.EvidenceID.TenantID()) err := svc.Evidences.Delete(ctx, input.EvidenceID) if err != nil { @@ -943,7 +943,7 @@ func (r *mutationResolver) DeleteEvidence(ctx context.Context, input types.Delet // UploadVendorComplianceReport is the resolver for the uploadVendorComplianceReport field. func (r *mutationResolver) UploadVendorComplianceReport(ctx context.Context, input types.UploadVendorComplianceReportInput) (*types.UploadVendorComplianceReportPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.VendorID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.VendorID.TenantID()) vendorComplianceReport, err := svc.VendorComplianceReports.Upload( ctx, @@ -966,7 +966,7 @@ func (r *mutationResolver) UploadVendorComplianceReport(ctx context.Context, inp // DeleteVendorComplianceReport is the resolver for the deleteVendorComplianceReport field. func (r *mutationResolver) DeleteVendorComplianceReport(ctx context.Context, input types.DeleteVendorComplianceReportInput) (*types.DeleteVendorComplianceReportPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.ReportID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.ReportID.TenantID()) err := svc.VendorComplianceReports.Delete(ctx, input.ReportID) if err != nil { @@ -980,7 +980,7 @@ func (r *mutationResolver) DeleteVendorComplianceReport(ctx context.Context, inp // CreatePolicy is the resolver for the createPolicy field. func (r *mutationResolver) CreatePolicy(ctx context.Context, input types.CreatePolicyInput) (*types.CreatePolicyPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.OrganizationID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID()) policy, err := svc.Policies.Create(ctx, probo.CreatePolicyRequest{ OrganizationID: input.OrganizationID, @@ -1001,7 +1001,7 @@ func (r *mutationResolver) CreatePolicy(ctx context.Context, input types.CreateP // UpdatePolicy is the resolver for the updatePolicy field. func (r *mutationResolver) UpdatePolicy(ctx context.Context, input types.UpdatePolicyInput) (*types.UpdatePolicyPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.ID.TenantID()) policy, err := svc.Policies.Update(ctx, probo.UpdatePolicyRequest{ ID: input.ID, @@ -1022,7 +1022,7 @@ func (r *mutationResolver) UpdatePolicy(ctx context.Context, input types.UpdateP // DeletePolicy is the resolver for the deletePolicy field. func (r *mutationResolver) DeletePolicy(ctx context.Context, input types.DeletePolicyInput) (*types.DeletePolicyPayload, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, input.PolicyID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, input.PolicyID.TenantID()) err := svc.Policies.Delete(ctx, input.PolicyID) if err != nil { @@ -1036,7 +1036,7 @@ func (r *mutationResolver) DeletePolicy(ctx context.Context, input types.DeleteP // LogoURL is the resolver for the logoUrl field. func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) return svc.Organizations.GenerateLogoURL(ctx, obj.ID, 1*time.Hour) } @@ -1064,9 +1064,34 @@ func (r *organizationResolver) Users(ctx context.Context, obj *types.Organizatio return types.NewUserConnection(page), nil } +// Connectors is the resolver for the connectors field. +func (r *organizationResolver) Connectors(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ConnectorOrder) (*types.ConnectorConnection, error) { + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) + + pageOrderBy := page.OrderBy[coredata.ConnectorOrderField]{ + Field: coredata.ConnectorOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.ConnectorOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + page, err := svc.Connectors.ListForOrganizationID(ctx, obj.ID, cursor) + if err != nil { + panic(fmt.Errorf("cannot list organization connectors: %w", err)) + } + + return types.NewConnectorConnection(page), nil +} + // Frameworks is the resolver for the frameworks field. func (r *organizationResolver) Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.FrameworkOrderBy) (*types.FrameworkConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.FrameworkOrderField]{ Field: coredata.FrameworkOrderFieldCreatedAt, @@ -1091,7 +1116,7 @@ func (r *organizationResolver) Frameworks(ctx context.Context, obj *types.Organi // Vendors is the resolver for the vendors field. func (r *organizationResolver) Vendors(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.VendorOrderField]{ Field: coredata.VendorOrderFieldCreatedAt, @@ -1116,7 +1141,7 @@ func (r *organizationResolver) Vendors(ctx context.Context, obj *types.Organizat // Peoples is the resolver for the peoples field. func (r *organizationResolver) Peoples(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PeopleOrderBy) (*types.PeopleConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.PeopleOrderField]{ Field: coredata.PeopleOrderFieldCreatedAt, @@ -1141,7 +1166,7 @@ func (r *organizationResolver) Peoples(ctx context.Context, obj *types.Organizat // Policies is the resolver for the policies field. func (r *organizationResolver) Policies(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyOrderBy) (*types.PolicyConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.PolicyOrderField]{ Field: coredata.PolicyOrderFieldName, @@ -1166,7 +1191,7 @@ func (r *organizationResolver) Policies(ctx context.Context, obj *types.Organiza // Mesures is the resolver for the mesures field. func (r *organizationResolver) Mesures(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MesureOrderBy) (*types.MesureConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.MesureOrderField]{ Field: coredata.MesureOrderFieldCreatedAt, @@ -1191,7 +1216,7 @@ func (r *organizationResolver) Mesures(ctx context.Context, obj *types.Organizat // Risks is the resolver for the risks field. func (r *organizationResolver) Risks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.RiskOrderBy) (*types.RiskConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.RiskOrderField]{ Field: coredata.RiskOrderFieldCreatedAt, @@ -1216,7 +1241,7 @@ func (r *organizationResolver) Risks(ctx context.Context, obj *types.Organizatio // Owner is the resolver for the owner field. func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.People, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) policy, err := svc.Policies.Get(ctx, obj.ID) if err != nil { @@ -1234,7 +1259,7 @@ func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.P // Controls is the resolver for the controls field. func (r *policyResolver) Controls(ctx context.Context, obj *types.Policy, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.ControlOrderField]{ Field: coredata.ControlOrderFieldCreatedAt, @@ -1259,7 +1284,7 @@ func (r *policyResolver) Controls(ctx context.Context, obj *types.Policy, first // Node is the resolver for the node field. func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, id.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, id.TenantID()) switch id.EntityType() { case coredata.OrganizationEntityType: @@ -1355,7 +1380,7 @@ func (r *queryResolver) Viewer(ctx context.Context) (*types.Viewer, error) { // Owner is the resolver for the owner field. func (r *riskResolver) Owner(ctx context.Context, obj *types.Risk) (*types.People, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) risk, err := svc.Risks.Get(ctx, obj.ID) if err != nil { @@ -1376,7 +1401,7 @@ func (r *riskResolver) Owner(ctx context.Context, obj *types.Risk) (*types.Peopl // Mesures is the resolver for the mesures field. func (r *riskResolver) Mesures(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MesureOrderBy) (*types.MesureConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.MesureOrderField]{ Field: coredata.MesureOrderFieldCreatedAt, @@ -1401,7 +1426,7 @@ func (r *riskResolver) Mesures(ctx context.Context, obj *types.Risk, first *int, // Policies is the resolver for the policies field. func (r *riskResolver) Policies(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyOrderBy) (*types.PolicyConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.PolicyOrderField]{ Field: coredata.PolicyOrderFieldCreatedAt, @@ -1426,7 +1451,7 @@ func (r *riskResolver) Policies(ctx context.Context, obj *types.Risk, first *int // Controls is the resolver for the controls field. func (r *riskResolver) Controls(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.ControlOrderField]{ Field: coredata.ControlOrderFieldCreatedAt, @@ -1451,7 +1476,7 @@ func (r *riskResolver) Controls(ctx context.Context, obj *types.Risk, first *int // AssignedTo is the resolver for the assignedTo field. func (r *taskResolver) AssignedTo(ctx context.Context, obj *types.Task) (*types.People, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) task, err := svc.Tasks.Get(ctx, obj.ID) if err != nil { @@ -1472,7 +1497,7 @@ func (r *taskResolver) AssignedTo(ctx context.Context, obj *types.Task) (*types. // Evidences is the resolver for the evidences field. func (r *taskResolver) Evidences(ctx context.Context, obj *types.Task, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.EvidenceOrderBy) (*types.EvidenceConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.EvidenceOrderField]{ Field: coredata.EvidenceOrderFieldCreatedAt, @@ -1496,7 +1521,7 @@ func (r *taskResolver) Evidences(ctx context.Context, obj *types.Task, first *in // ComplianceReports is the resolver for the complianceReports field. func (r *vendorResolver) ComplianceReports(ctx context.Context, obj *types.Vendor, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorComplianceReportOrderBy) (*types.VendorComplianceReportConnection, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) pageOrderBy := page.OrderBy[coredata.VendorComplianceReportOrderField]{ Field: coredata.VendorComplianceReportOrderFieldReportDate, @@ -1521,7 +1546,7 @@ func (r *vendorResolver) ComplianceReports(ctx context.Context, obj *types.Vendo // BusinessOwner is the resolver for the businessOwner field. func (r *vendorResolver) BusinessOwner(ctx context.Context, obj *types.Vendor) (*types.People, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) vendor, err := svc.Vendors.Get(ctx, obj.ID) if err != nil { @@ -1542,7 +1567,7 @@ func (r *vendorResolver) BusinessOwner(ctx context.Context, obj *types.Vendor) ( // SecurityOwner is the resolver for the securityOwner field. func (r *vendorResolver) SecurityOwner(ctx context.Context, obj *types.Vendor) (*types.People, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) vendor, err := svc.Vendors.Get(ctx, obj.ID) if err != nil { @@ -1563,7 +1588,7 @@ func (r *vendorResolver) SecurityOwner(ctx context.Context, obj *types.Vendor) ( // Vendor is the resolver for the vendor field. func (r *vendorComplianceReportResolver) Vendor(ctx context.Context, obj *types.VendorComplianceReport) (*types.Vendor, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) vendor, err := svc.Vendors.Get(ctx, obj.ID) if err != nil { @@ -1575,7 +1600,7 @@ func (r *vendorComplianceReportResolver) Vendor(ctx context.Context, obj *types. // FileURL is the resolver for the fileUrl field. func (r *vendorComplianceReportResolver) FileURL(ctx context.Context, obj *types.VendorComplianceReport) (string, error) { - svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID()) fileURL, err := svc.VendorComplianceReports.GenerateFileURL(ctx, obj.ID, 1*time.Hour) if err != nil {