From ec42409219e88cb8fbcb768972d1f3cea74ea15a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Sibiril?= <81782+aureliensibiril@users.noreply.github.com> Date: Wed, 8 Apr 2026 09:09:08 +0200 Subject: [PATCH] Expose oauth2Scopes on Connector instead of AccessSource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AccessSource.oauth2Scopes field duplicated knowledge that naturally belongs on the Connector object that AccessSource already exposes via its connector field. Move it to Connector so every type that holds a connector (AccessSource, SCIMBridge, etc.) can reach the scopes through the connector relationship. AccessSourceRow now queries accessSource.connector { oauth2Scopes } in its reconnect flow. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com> --- .../_components/AccessSourceRow.tsx | 10 ++--- pkg/server/api/console/v1/schema.graphql | 2 +- pkg/server/api/console/v1/v1_resolver.go | 37 +++++++------------ 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/apps/console/src/pages/organizations/access-reviews/_components/AccessSourceRow.tsx b/apps/console/src/pages/organizations/access-reviews/_components/AccessSourceRow.tsx index ae4199797..fdbc8fc70 100644 --- a/apps/console/src/pages/organizations/access-reviews/_components/AccessSourceRow.tsx +++ b/apps/console/src/pages/organizations/access-reviews/_components/AccessSourceRow.tsx @@ -44,8 +44,8 @@ const fragment = graphql` connectorId connector { provider + oauth2Scopes } - oauth2Scopes connectionStatus selectedOrganization needsConfiguration @@ -206,15 +206,15 @@ export function AccessSourceRow({ fKey, connectionId, organizationId }: Props) { }; const handleReconnect = () => { - const provider = accessSource.connector?.provider; - if (!provider || !accessSource.connectorId) return; + const connector = accessSource.connector; + if (!connector || !accessSource.connectorId) return; const baseURL = import.meta.env.VITE_API_URL || window.location.origin; const url = new URL("/api/console/v1/connectors/initiate", baseURL); url.searchParams.append("organization_id", organizationId); - url.searchParams.append("provider", provider); + url.searchParams.append("provider", connector.provider); url.searchParams.append("connector_id", accessSource.connectorId); - for (const scope of accessSource.oauth2Scopes) { + for (const scope of connector.oauth2Scopes) { url.searchParams.append("scope", scope); } url.searchParams.append( diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql index badc3ce95..1c3d42d43 100644 --- a/pkg/server/api/console/v1/schema.graphql +++ b/pkg/server/api/console/v1/schema.graphql @@ -2184,6 +2184,7 @@ input ConnectorFilter { type Connector { id: ID! provider: ConnectorProvider! + oauth2Scopes: [String!]! @goField(forceResolver: true) createdAt: Datetime! } @@ -6804,7 +6805,6 @@ type AccessSource implements Node { needsConfiguration: Boolean! @goField(forceResolver: true) connectionStatus: AccessSourceConnectionStatus! @goField(forceResolver: true) selectedOrganization: String @goField(forceResolver: true) - oauth2Scopes: [String!]! @goField(forceResolver: true) createdAt: Datetime! updatedAt: Datetime! diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index 4c9559be3..c2ecb9898 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -522,30 +522,6 @@ func (r *accessSourceResolver) SelectedOrganization(ctx context.Context, obj *ty return nil, nil } -// Oauth2Scopes is the resolver for the oauth2Scopes field. -func (r *accessSourceResolver) Oauth2Scopes(ctx context.Context, obj *types.AccessSource) ([]string, error) { - if obj.ConnectorID == nil { - return []string{}, nil - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - dbConnector, err := prb.Connectors.Get(ctx, *obj.ConnectorID) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - return []string{}, nil - } - r.logger.ErrorCtx(ctx, "cannot get connector for oauth2 scopes", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - scopes := drivers.ProviderOAuth2Scopes(dbConnector.Provider) - if scopes == nil { - return []string{}, nil - } - return scopes, nil -} - // Permission is the resolver for the permission field. func (r *accessSourceResolver) Permission(ctx context.Context, obj *types.AccessSource, action string) (bool, error) { return r.Resolver.Permission(ctx, obj, action) @@ -1007,6 +983,15 @@ func (r *complianceFrameworkResolver) Framework(ctx context.Context, obj *types. return types.NewFramework(framework), nil } +// Oauth2Scopes is the resolver for the oauth2Scopes field. +func (r *connectorResolver) Oauth2Scopes(ctx context.Context, obj *types.Connector) ([]string, error) { + scopes := drivers.ProviderOAuth2Scopes(obj.Provider) + if scopes == nil { + return []string{}, nil + } + return scopes, nil +} + // Organization is the resolver for the organization field. func (r *controlResolver) Organization(ctx context.Context, obj *types.Control) (*types.Organization, error) { if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil { @@ -11880,6 +11865,9 @@ func (r *Resolver) ComplianceFramework() schema.ComplianceFrameworkResolver { return &complianceFrameworkResolver{r} } +// Connector returns schema.ConnectorResolver implementation. +func (r *Resolver) Connector() schema.ConnectorResolver { return &connectorResolver{r} } + // Control returns schema.ControlResolver implementation. func (r *Resolver) Control() schema.ControlResolver { return &controlResolver{r} } @@ -12227,6 +12215,7 @@ type auditLogEntryResolver struct{ *Resolver } type auditLogEntryConnectionResolver struct{ *Resolver } type complianceExternalURLResolver struct{ *Resolver } type complianceFrameworkResolver struct{ *Resolver } +type connectorResolver struct{ *Resolver } type controlResolver struct{ *Resolver } type controlConnectionResolver struct{ *Resolver } type customDomainResolver struct{ *Resolver }