refactor(sso): standardize SSO identity provider query names (#10335)

Updated method, query, and variable names to align with a consistent
naming convention for fetching SSO identity providers. Added
comprehensive unit tests to validate SSO service logic, ensuring better
reliability and maintainability.

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Antoine Moreaux
2025-02-19 17:39:31 +01:00
committed by GitHub
parent 4fd0c28439
commit 984eeda807
8 changed files with 303 additions and 34 deletions

View File

@ -1,6 +1,5 @@
/* eslint-disable */
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
import { PermissionsOnAllObjectRecords } from 'twenty-shared';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
@ -33,6 +32,33 @@ export type ActivateWorkspaceInput = {
displayName?: InputMaybe<Scalars['String']['input']>;
};
export type AdminPanelHealthServiceData = {
__typename?: 'AdminPanelHealthServiceData';
details?: Maybe<Scalars['String']['output']>;
queues?: Maybe<Array<AdminPanelWorkerQueueHealth>>;
status: AdminPanelHealthServiceStatus;
};
export enum AdminPanelHealthServiceStatus {
OPERATIONAL = 'OPERATIONAL',
OUTAGE = 'OUTAGE'
}
export enum AdminPanelIndicatorHealthStatusInputEnum {
DATABASE = 'DATABASE',
MESSAGE_SYNC = 'MESSAGE_SYNC',
REDIS = 'REDIS',
WORKER = 'WORKER'
}
export type AdminPanelWorkerQueueHealth = {
__typename?: 'AdminPanelWorkerQueueHealth';
metrics: WorkerQueueMetrics;
name: Scalars['String']['output'];
status: AdminPanelHealthServiceStatus;
workers: Scalars['Float']['output'];
};
export type Analytics = {
__typename?: 'Analytics';
/** Boolean that confirms query was dispatched */
@ -257,6 +283,7 @@ export type ClientConfig = {
debugMode: Scalars['Boolean']['output'];
defaultSubdomain?: Maybe<Scalars['String']['output']>;
frontDomain: Scalars['String']['output'];
isAttachmentPreviewEnabled: Scalars['Boolean']['output'];
isEmailVerificationRequired: Scalars['Boolean']['output'];
isGoogleCalendarEnabled: Scalars['Boolean']['output'];
isGoogleMessagingEnabled: Scalars['Boolean']['output'];
@ -809,6 +836,7 @@ export type Mutation = {
activateWorkspace: Workspace;
authorizeApp: AuthorizeApp;
buildDraftServerlessFunction: ServerlessFunction;
checkCustomDomainValidRecords?: Maybe<CustomDomainValidRecords>;
checkoutSession: BillingSessionOutput;
computeStepOutputSchema: Scalars['JSON']['output'];
createDraftFromWorkflowVersion: WorkflowVersion;
@ -1305,6 +1333,13 @@ export type PageInfo = {
startCursor?: Maybe<Scalars['ConnectionCursor']['output']>;
};
export enum PermissionsOnAllObjectRecords {
DESTROY_ALL_OBJECT_RECORDS = 'DESTROY_ALL_OBJECT_RECORDS',
READ_ALL_OBJECT_RECORDS = 'READ_ALL_OBJECT_RECORDS',
SOFT_DELETE_ALL_OBJECT_RECORDS = 'SOFT_DELETE_ALL_OBJECT_RECORDS',
UPDATE_ALL_OBJECT_RECORDS = 'UPDATE_ALL_OBJECT_RECORDS'
}
export type PostgresCredentials = {
__typename?: 'PostgresCredentials';
id: Scalars['UUID']['output'];
@ -1343,7 +1378,6 @@ export type PublishServerlessFunctionInput = {
export type Query = {
__typename?: 'Query';
billingPortalSession: BillingSessionOutput;
checkCustomDomainValidRecords?: Maybe<CustomDomainValidRecords>;
checkUserExists: UserExistsOutput;
checkWorkspaceInviteHashIsValid: WorkspaceInviteHashValid;
clientConfig: ClientConfig;
@ -1361,18 +1395,20 @@ export type Query = {
findWorkspaceInvitations: Array<WorkspaceInvitation>;
getAvailablePackages: Scalars['JSON']['output'];
getEnvironmentVariablesGrouped: EnvironmentVariablesOutput;
getIndicatorHealthStatus: AdminPanelHealthServiceData;
getPostgresCredentials?: Maybe<PostgresCredentials>;
getProductPrices: BillingProductPricesOutput;
getPublicWorkspaceDataByDomain: PublicWorkspaceDataOutput;
getRoles: Array<Role>;
getSSOIdentityProviders: Array<FindAvailableSsoidpOutput>;
getServerlessFunctionSourceCode?: Maybe<Scalars['JSON']['output']>;
getSystemHealthStatus: SystemHealth;
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal;
getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal;
getTimelineThreadsFromPersonId: TimelineThreadsWithTotal;
index: Index;
indexMetadatas: IndexConnection;
listSSOIdentityProvidersByWorkspaceId: Array<FindAvailableSsoidpOutput>;
object: Object;
objects: ObjectConnection;
plans: Array<BillingPlanOutput>;
@ -1443,6 +1479,11 @@ export type QueryGetAvailablePackagesArgs = {
};
export type QueryGetIndicatorHealthStatusArgs = {
indicatorName: AdminPanelIndicatorHealthStatusInputEnum;
};
export type QueryGetProductPricesArgs = {
product: Scalars['String']['input'];
};
@ -1633,6 +1674,10 @@ export type ResendEmailVerificationTokenOutput = {
export type Role = {
__typename?: 'Role';
canDestroyAllObjectRecords: Scalars['Boolean']['output'];
canReadAllObjectRecords: Scalars['Boolean']['output'];
canSoftDeleteAllObjectRecords: Scalars['Boolean']['output'];
canUpdateAllObjectRecords: Scalars['Boolean']['output'];
canUpdateAllSettings: Scalars['Boolean']['output'];
description?: Maybe<Scalars['String']['output']>;
id: Scalars['String']['output'];
@ -1798,6 +1843,14 @@ export type Support = {
supportFrontChatId?: Maybe<Scalars['String']['output']>;
};
export type SystemHealth = {
__typename?: 'SystemHealth';
database: AdminPanelHealthServiceData;
messageSync: AdminPanelHealthServiceData;
redis: AdminPanelHealthServiceData;
worker: AdminPanelHealthServiceData;
};
export type TimelineCalendarEvent = {
__typename?: 'TimelineCalendarEvent';
conferenceLink: LinksMetadata;
@ -2050,8 +2103,8 @@ export type UserWorkspace = {
createdAt: Scalars['DateTime']['output'];
deletedAt?: Maybe<Scalars['DateTime']['output']>;
id: Scalars['UUID']['output'];
settingsPermissions?: Maybe<Array<SettingsFeatures>>;
objectRecordsPermissions?: Maybe<Array<PermissionsOnAllObjectRecords>>;
settingsPermissions?: Maybe<Array<SettingsFeatures>>;
updatedAt: Scalars['DateTime']['output'];
user: User;
userId: Scalars['String']['output'];
@ -2065,6 +2118,16 @@ export type ValidatePasswordResetToken = {
id: Scalars['String']['output'];
};
export type WorkerQueueMetrics = {
__typename?: 'WorkerQueueMetrics';
active: Scalars['Float']['output'];
completed: Scalars['Float']['output'];
delayed: Scalars['Float']['output'];
failed: Scalars['Float']['output'];
prioritized: Scalars['Float']['output'];
waiting: Scalars['Float']['output'];
};
export type WorkflowAction = {
__typename?: 'WorkflowAction';
id: Scalars['UUID']['output'];

View File

@ -1264,6 +1264,7 @@ export type Query = {
getProductPrices: BillingProductPricesOutput;
getPublicWorkspaceDataByDomain: PublicWorkspaceDataOutput;
getRoles: Array<Role>;
getSSOIdentityProviders: Array<FindAvailableSsoidpOutput>;
getServerlessFunctionSourceCode?: Maybe<Scalars['JSON']>;
getSystemHealthStatus: SystemHealth;
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
@ -1272,7 +1273,6 @@ export type Query = {
getTimelineThreadsFromPersonId: TimelineThreadsWithTotal;
index: Index;
indexMetadatas: IndexConnection;
listSSOIdentityProvidersByWorkspaceId: Array<FindAvailableSsoidpOutput>;
object: Object;
objects: ObjectConnection;
plans: Array<BillingPlanOutput>;
@ -2364,10 +2364,10 @@ export type EditSsoIdentityProviderMutationVariables = Exact<{
export type EditSsoIdentityProviderMutation = { __typename?: 'Mutation', editSSOIdentityProvider: { __typename?: 'EditSsoOutput', id: string, type: IdentityProviderType, issuer: string, name: string, status: SsoIdentityProviderStatus } };
export type ListSsoIdentityProvidersByWorkspaceIdQueryVariables = Exact<{ [key: string]: never; }>;
export type GetSsoIdentityProvidersQueryVariables = Exact<{ [key: string]: never; }>;
export type ListSsoIdentityProvidersByWorkspaceIdQuery = { __typename?: 'Query', listSSOIdentityProvidersByWorkspaceId: Array<{ __typename?: 'FindAvailableSSOIDPOutput', type: IdentityProviderType, id: string, name: string, issuer: string, status: SsoIdentityProviderStatus }> };
export type GetSsoIdentityProvidersQuery = { __typename?: 'Query', getSSOIdentityProviders: Array<{ __typename?: 'FindAvailableSSOIDPOutput', type: IdentityProviderType, id: string, name: string, issuer: string, status: SsoIdentityProviderStatus }> };
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array<SettingsFeatures> | null, objectRecordsPermissions?: Array<PermissionsOnAllObjectRecords> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }> } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> };
@ -4392,9 +4392,9 @@ export function useEditSsoIdentityProviderMutation(baseOptions?: Apollo.Mutation
export type EditSsoIdentityProviderMutationHookResult = ReturnType<typeof useEditSsoIdentityProviderMutation>;
export type EditSsoIdentityProviderMutationResult = Apollo.MutationResult<EditSsoIdentityProviderMutation>;
export type EditSsoIdentityProviderMutationOptions = Apollo.BaseMutationOptions<EditSsoIdentityProviderMutation, EditSsoIdentityProviderMutationVariables>;
export const ListSsoIdentityProvidersByWorkspaceIdDocument = gql`
query ListSSOIdentityProvidersByWorkspaceId {
listSSOIdentityProvidersByWorkspaceId {
export const GetSsoIdentityProvidersDocument = gql`
query GetSSOIdentityProviders {
getSSOIdentityProviders {
type
id
name
@ -4405,31 +4405,31 @@ export const ListSsoIdentityProvidersByWorkspaceIdDocument = gql`
`;
/**
* __useListSsoIdentityProvidersByWorkspaceIdQuery__
* __useGetSsoIdentityProvidersQuery__
*
* To run a query within a React component, call `useListSsoIdentityProvidersByWorkspaceIdQuery` and pass it any options that fit your needs.
* When your component renders, `useListSsoIdentityProvidersByWorkspaceIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
* To run a query within a React component, call `useGetSsoIdentityProvidersQuery` and pass it any options that fit your needs.
* When your component renders, `useGetSsoIdentityProvidersQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useListSsoIdentityProvidersByWorkspaceIdQuery({
* const { data, loading, error } = useGetSsoIdentityProvidersQuery({
* variables: {
* },
* });
*/
export function useListSsoIdentityProvidersByWorkspaceIdQuery(baseOptions?: Apollo.QueryHookOptions<ListSsoIdentityProvidersByWorkspaceIdQuery, ListSsoIdentityProvidersByWorkspaceIdQueryVariables>) {
export function useGetSsoIdentityProvidersQuery(baseOptions?: Apollo.QueryHookOptions<GetSsoIdentityProvidersQuery, GetSsoIdentityProvidersQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ListSsoIdentityProvidersByWorkspaceIdQuery, ListSsoIdentityProvidersByWorkspaceIdQueryVariables>(ListSsoIdentityProvidersByWorkspaceIdDocument, options);
return Apollo.useQuery<GetSsoIdentityProvidersQuery, GetSsoIdentityProvidersQueryVariables>(GetSsoIdentityProvidersDocument, options);
}
export function useListSsoIdentityProvidersByWorkspaceIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ListSsoIdentityProvidersByWorkspaceIdQuery, ListSsoIdentityProvidersByWorkspaceIdQueryVariables>) {
export function useGetSsoIdentityProvidersLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetSsoIdentityProvidersQuery, GetSsoIdentityProvidersQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ListSsoIdentityProvidersByWorkspaceIdQuery, ListSsoIdentityProvidersByWorkspaceIdQueryVariables>(ListSsoIdentityProvidersByWorkspaceIdDocument, options);
return Apollo.useLazyQuery<GetSsoIdentityProvidersQuery, GetSsoIdentityProvidersQueryVariables>(GetSsoIdentityProvidersDocument, options);
}
export type ListSsoIdentityProvidersByWorkspaceIdQueryHookResult = ReturnType<typeof useListSsoIdentityProvidersByWorkspaceIdQuery>;
export type ListSsoIdentityProvidersByWorkspaceIdLazyQueryHookResult = ReturnType<typeof useListSsoIdentityProvidersByWorkspaceIdLazyQuery>;
export type ListSsoIdentityProvidersByWorkspaceIdQueryResult = Apollo.QueryResult<ListSsoIdentityProvidersByWorkspaceIdQuery, ListSsoIdentityProvidersByWorkspaceIdQueryVariables>;
export type GetSsoIdentityProvidersQueryHookResult = ReturnType<typeof useGetSsoIdentityProvidersQuery>;
export type GetSsoIdentityProvidersLazyQueryHookResult = ReturnType<typeof useGetSsoIdentityProvidersLazyQuery>;
export type GetSsoIdentityProvidersQueryResult = Apollo.QueryResult<GetSsoIdentityProvidersQuery, GetSsoIdentityProvidersQueryVariables>;
export const DeleteUserAccountDocument = gql`
mutation DeleteUserAccount {
deleteUser {

View File

@ -49,7 +49,6 @@ export const SettingsAccountsRowDropdownMenu = ({
<>
<Dropdown
dropdownId={dropdownId}
className={className}
dropdownPlacement="right-start"
dropdownHotkeyScope={{ scope: dropdownId }}
clickableComponent={

View File

@ -15,7 +15,7 @@ import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { useRecoilState, useRecoilValue } from 'recoil';
import { IconKey } from 'twenty-ui';
import { useListSsoIdentityProvidersByWorkspaceIdQuery } from '~/generated/graphql';
import { useGetSsoIdentityProvidersQuery } from '~/generated/graphql';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
const StyledLink = styled(Link, {
@ -36,13 +36,11 @@ export const SettingsSSOIdentitiesProvidersListCard = () => {
SSOIdentitiesProvidersState,
);
const { loading } = useListSsoIdentityProvidersByWorkspaceIdQuery({
const { loading } = useGetSsoIdentityProvidersQuery({
fetchPolicy: 'network-only',
skip: currentWorkspace?.hasValidEnterpriseKey === false,
onCompleted: (data) => {
setSSOIdentitiesProviders(
data?.listSSOIdentityProvidersByWorkspaceId ?? [],
);
setSSOIdentitiesProviders(data?.getSSOIdentityProviders ?? []);
},
onError: (error: Error) => {
enqueueSnackBar(error.message, {

View File

@ -2,9 +2,9 @@
import { gql } from '@apollo/client';
export const LIST_WORKSPACE_SSO_IDENTITY_PROVIDERS = gql`
query ListSSOIdentityProvidersByWorkspaceId {
listSSOIdentityProvidersByWorkspaceId {
export const GET_SSO_IDENTITY_PROVIDERS = gql`
query GetSSOIdentityProviders {
getSSOIdentityProviders {
type
id
name

View File

@ -0,0 +1,209 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { SSOService } from 'src/engine/core-modules/sso/services/sso.service';
import { BillingService } from 'src/engine/core-modules/billing/services/billing.service';
import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
import { SSOException } from 'src/engine/core-modules/sso/sso.exception';
describe('SSOService', () => {
let service: SSOService;
let repository: Repository<WorkspaceSSOIdentityProvider>;
let billingService: BillingService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
SSOService,
{
provide: getRepositoryToken(WorkspaceSSOIdentityProvider, 'core'),
useClass: Repository,
},
{
provide: BillingService,
useValue: {
hasEntitlement: jest.fn(),
},
},
{
provide: EnvironmentService,
useValue: {
get: jest.fn(),
},
},
{
provide: ExceptionHandlerService,
useValue: {
captureExceptions: jest.fn(),
},
},
],
}).compile();
service = module.get<SSOService>(SSOService);
repository = module.get<Repository<WorkspaceSSOIdentityProvider>>(
getRepositoryToken(WorkspaceSSOIdentityProvider, 'core'),
);
billingService = module.get<BillingService>(BillingService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('createOIDCIdentityProvider', () => {
it('should create an OIDC identity provider successfully', async () => {
const workspaceId = 'workspace-123';
const data = {
issuer: 'https://example.com',
clientID: 'client-id',
clientSecret: 'client-secret',
name: 'Test Provider',
};
const mockIssuer = { metadata: { issuer: 'https://example.com' } };
const mockSavedProvider = {
id: 'provider-123',
type: 'OIDC',
name: 'Test Provider',
status: 'ACTIVE',
issuer: 'https://example.com',
};
jest.spyOn(billingService, 'hasEntitlement').mockResolvedValue(true);
jest
.spyOn(service as any, 'getIssuerForOIDC')
.mockResolvedValue(mockIssuer);
jest
.spyOn(repository, 'save')
.mockResolvedValue(mockSavedProvider as any);
const result = await service.createOIDCIdentityProvider(
data,
workspaceId,
);
expect(result).toEqual({
id: 'provider-123',
type: 'OIDC',
name: 'Test Provider',
status: 'ACTIVE',
issuer: 'https://example.com',
});
expect(billingService.hasEntitlement).toHaveBeenCalledWith(
workspaceId,
'SSO',
);
expect(repository.save).toHaveBeenCalledWith({
type: 'OIDC',
clientID: 'client-id',
clientSecret: 'client-secret',
issuer: 'https://example.com',
name: 'Test Provider',
workspaceId,
});
});
it('should throw an exception when SSO is disabled', async () => {
const workspaceId = 'workspace-123';
const data = {
issuer: 'https://example.com',
clientID: 'client-id',
clientSecret: 'client-secret',
name: 'Test Provider',
};
jest.spyOn(billingService, 'hasEntitlement').mockResolvedValue(false);
const result = await service.createOIDCIdentityProvider(
data,
workspaceId,
);
expect(result).toBeInstanceOf(SSOException);
});
});
describe('deleteSSOIdentityProvider', () => {
it('should delete the identity provider successfully', async () => {
const identityProviderId = 'provider-123';
const workspaceId = 'workspace-123';
const mockProvider = { id: identityProviderId };
jest.spyOn(repository, 'findOne').mockResolvedValue(mockProvider as any);
jest.spyOn(repository, 'delete').mockResolvedValue(null as any);
const result = await service.deleteSSOIdentityProvider(
identityProviderId,
workspaceId,
);
expect(result).toEqual({ identityProviderId: 'provider-123' });
expect(repository.findOne).toHaveBeenCalledWith({
where: { id: identityProviderId, workspaceId },
});
expect(repository.delete).toHaveBeenCalledWith({
id: identityProviderId,
});
});
it('should throw an exception if the identity provider does not exist', async () => {
const identityProviderId = 'provider-123';
const workspaceId = 'workspace-123';
jest.spyOn(repository, 'findOne').mockResolvedValue(null);
await expect(
service.deleteSSOIdentityProvider(identityProviderId, workspaceId),
).rejects.toThrow(SSOException);
});
});
describe('getAuthorizationUrlForSSO', () => {
it('should return an authorization URL', async () => {
const identityProviderId = 'provider-123';
const searchParams = { client: 'web' };
const mockIdentityProvider = {
id: 'provider-123',
type: 'OIDC',
};
jest
.spyOn(repository, 'findOne')
.mockResolvedValue(mockIdentityProvider as any);
jest
.spyOn(service as any, 'buildIssuerURL')
.mockReturnValue('https://example.com/auth');
const result = await service.getAuthorizationUrlForSSO(
identityProviderId,
searchParams,
);
expect(result).toEqual({
id: 'provider-123',
authorizationURL: 'https://example.com/auth',
type: 'OIDC',
});
expect(repository.findOne).toHaveBeenCalledWith({
where: { id: identityProviderId },
});
});
it('should throw an exception if the identity provider is not found', async () => {
const identityProviderId = 'provider-123';
const searchParams = {};
jest.spyOn(repository, 'findOne').mockResolvedValue(null);
await expect(
service.getAuthorizationUrlForSSO(identityProviderId, searchParams),
).rejects.toThrow(SSOException);
});
});
});

View File

@ -223,7 +223,7 @@ export class SSOService {
};
}
async listSSOIdentityProvidersByWorkspaceId(workspaceId: string) {
async getSSOIdentityProviders(workspaceId: string) {
return (await this.workspaceSSOIdentityProviderRepository.find({
where: { workspaceId },
select: ['id', 'name', 'type', 'issuer', 'status'],

View File

@ -42,12 +42,12 @@ export class SSOResolver {
);
}
@UseGuards(EnterpriseFeaturesEnabledGuard)
@UseGuards(WorkspaceAuthGuard, EnterpriseFeaturesEnabledGuard)
@Query(() => [FindAvailableSSOIDPOutput])
async listSSOIdentityProvidersByWorkspaceId(
async getSSOIdentityProviders(
@AuthWorkspace() { id: workspaceId }: Workspace,
) {
return this.sSOService.listSSOIdentityProvidersByWorkspaceId(workspaceId);
return this.sSOService.getSSOIdentityProviders(workspaceId);
}
@UseGuards(WorkspaceAuthGuard, EnterpriseFeaturesEnabledGuard)