LCOV - code coverage report
Current view: top level - dashql-app/src/connections - salesforce_connection_settings.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 52.5 % 360 189
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : import * as style from './connection_settings.module.css';
       3              : import * as connection from '@ankoh/dashql-jsonschema/connection.js';
       4              : 
       5              : import { KeyIcon, PersonIcon } from '../ui/foundations/symbol_icon.js';
       6              : 
       7              : import { useConnectionById, useConnectionDraftForm } from './connection_registry.js';
       8              : import { useConnectionSave } from './connection_persistence.js';
       9              : import { useConnectionSetup } from './connection_setup.js';
      10              : import { ConnectionHealth, ConnectionStatus } from './connection_state.js';
      11              : import { performHealthCheck } from './health_check.js';
      12              : import { useQueryExecutor } from './query_executor.js';
      13              : import { useSalesforceSetup } from './salesforce/salesforce_connector.js';
      14              : import type { SalesforceConnectorType } from './salesforce/salesforce_connection_setup.js';
      15              : import { getSalesforceConnectionDetails } from './salesforce/salesforce_connection_state.js';
      16              : import {
      17              :     TextField,
      18              :     TextFieldValidationStatus,
      19              :     VALIDATION_ERROR,
      20              :     VALIDATION_UNKNOWN,
      21              : } from '../ui/foundations/text_field.js';
      22              : import { IndicatorStatus } from '../ui/foundations/status_indicator.js';
      23              : import { classNames } from '../utils/classnames.js';
      24              : import { Logger } from '../platform/logger/logger.js';
      25              : import { CONNECTOR_INFOS, ConnectorType, HYPER_CONNECTOR, SALESFORCE_DATA_CLOUD_CONNECTOR, SIDB_CONNECTOR, TRINO_CONNECTOR } from './connector_info.js';
      26              : import { CONNECTOR_DESCRIPTORS, connectionParamsOf } from './connector_descriptor.js';
      27              : import { ConnectionStateDetailsVariant } from './connection_state_details.js';
      28              : import type { DetailedError } from '../utils/error.js';
      29              : import { ConnectionInlineHeader } from './connection_inline_header.js';
      30              : import { collectSalesforceAuthInfo } from './salesforce/salesforce_api_client.js';
      31              : import { SIDB_DEFAULT_DATABASE_NAME } from './salesforce/salesforce_catalog_update.js';
      32              : 
      33            1 : const LOG_CTX = "sf_connector";
      34              : 
      35              : interface PageState {
      36              :     instanceUrl: string;
      37              :     appConsumerKey: string;
      38              :     login: string;
      39              :     databaseName: string;
      40              : };
      41              : 
      42            1 : export function getConnectionStatusText(status: ConnectionStatus | undefined, logger: Logger) {
      43            1 :     switch (status) {
      44            1 :         case ConnectionStatus.NOT_STARTED:
      45            1 :             return "Disconnected";
      46            0 :         case ConnectionStatus.AUTH_STARTED:
      47            0 :             return "Starting authorization";
      48            0 :         case ConnectionStatus.AUTH_CANCELLED:
      49            0 :             return "Cancelled authorization";
      50            0 :         case ConnectionStatus.AUTH_FAILED:
      51            0 :             return "Authorization failed";
      52            0 :         case ConnectionStatus.PKCE_GENERATION_STARTED:
      53            0 :             return "Generating PKCE challenge";
      54            0 :         case ConnectionStatus.PKCE_GENERATED:
      55            0 :             return "Generated PKCE challenge";
      56            0 :         case ConnectionStatus.WAITING_FOR_OAUTH_CODE_VIA_LINK:
      57            0 :         case ConnectionStatus.WAITING_FOR_OAUTH_CODE_VIA_WINDOW:
      58            0 :             return "Waiting for OAuth code";
      59            0 :         case ConnectionStatus.OAUTH_CODE_RECEIVED:
      60            0 :             return "Received OAuth code";
      61            0 :         case ConnectionStatus.ACCESS_TOKEN_REQUESTED:
      62            0 :             return "Requesting access token";
      63            0 :         case ConnectionStatus.ACCESS_TOKEN_RECEIVED:
      64            0 :             return "Received access token";
      65            0 :         case ConnectionStatus.CORE_ACCESS_TOKEN_REQUESTED:
      66            0 :             return "Requesting Core access token";
      67            0 :         case ConnectionStatus.CORE_ACCESS_TOKEN_RECEIVED:
      68            0 :             return "Received Core access token";
      69            0 :         case ConnectionStatus.DATA_CLOUD_TOKEN_REQUESTED:
      70            0 :             return "Requesting Data Cloud access token";
      71            0 :         case ConnectionStatus.DATA_CLOUD_TOKEN_RECEIVED:
      72            0 :             return "Received Data Cloud access token";
      73            1 :         case ConnectionStatus.CHANNEL_SETUP_STARTED:
      74            1 :             return "Creating channel";
      75            0 :         case ConnectionStatus.CHANNEL_SETUP_FAILED:
      76            0 :             return "Failed to create channel";
      77            0 :         case ConnectionStatus.CHANNEL_SETUP_CANCELLED:
      78            0 :             return "Cancelled channel setup";
      79            0 :         case ConnectionStatus.CHANNEL_READY:
      80            0 :             return "Channel is ready";
      81            1 :         case ConnectionStatus.HEALTH_CHECK_STARTED:
      82            1 :             return "Health check started";
      83            0 :         case ConnectionStatus.HEALTH_CHECK_FAILED:
      84            0 :             return "Health check failed";
      85            0 :         case ConnectionStatus.HEALTH_CHECK_CANCELLED:
      86            0 :             return "Health check cancelled";
      87            1 :         case ConnectionStatus.HEALTH_CHECK_SUCCEEDED:
      88            1 :             return "Health check succeeded";
      89            1 :         case undefined:
      90            1 :             break;
      91            0 :         default:
      92            0 :             logger.warn("unexpected connection status", { "status": status });
      93            1 :     }
      94            1 :     return "";
      95            1 : }
      96              : 
      97            1 : export function getConnectionHealthIndicator(health: ConnectionHealth | null) {
      98            1 :     switch (health) {
      99            1 :         case ConnectionHealth.NOT_STARTED:
     100            1 :             return IndicatorStatus.None;
     101            1 :         case ConnectionHealth.ONLINE:
     102            1 :             return IndicatorStatus.Succeeded;
     103            1 :         case ConnectionHealth.FAILED:
     104            1 :             return IndicatorStatus.Failed;
     105            1 :         case ConnectionHealth.CONNECTING:
     106            1 :             return IndicatorStatus.Running;
     107            1 :         default:
     108            1 :             return IndicatorStatus.None;
     109            1 :     }
     110            1 : }
     111              : 
     112            1 : export function getConnectionError(status: ConnectionStateDetailsVariant | null): (DetailedError | null) {
     113            1 :     switch (status?.type) {
     114            1 :         case TRINO_CONNECTOR:
     115            1 :             return (status.value.proto.channelError ?? status.value.proto.healthCheckError ?? null) as DetailedError | null;
     116            0 :         case SALESFORCE_DATA_CLOUD_CONNECTOR:
     117            0 :         case SIDB_CONNECTOR:
     118            0 :             return (status.value.proto.channelError ?? status.value.proto.healthCheckError ?? null) as DetailedError | null;
     119            1 :         case HYPER_CONNECTOR:
     120            1 :             return (status.value.proto.channelError ?? status.value.proto.healthCheckError ?? null) as DetailedError | null;
     121            1 :         default:
     122            1 :             return null;
     123            1 :     }
     124            1 : }
     125              : 
     126              : interface Props {
     127              :     connectionId: string | null;
     128              :     /// The connector of the settings
     129              :     connector: SalesforceConnectorType;
     130              :     onClose?: () => void;
     131              : }
     132              : 
     133              : export interface SalesforceConnectionAliasField {
     134              :     inputRef?: React.Ref<HTMLInputElement>;
     135              :     value: string;
     136              :     onChange: React.ChangeEventHandler<HTMLInputElement>;
     137              :     validation?: TextFieldValidationStatus;
     138              :     name?: string;
     139              :     caption?: string;
     140              : }
     141              : 
     142              : interface SalesforceConnectionSettingsPageProps {
     143              :     connectionState: ReturnType<typeof useConnectionById>[0];
     144              :     /// The connector of the settings, Salesforce Data Cloud if not set
     145              :     connector?: SalesforceConnectorType;
     146              :     wrongPlatform: boolean;
     147              :     freezeInput: boolean;
     148              :     instanceUrl: string;
     149              :     appConsumerKey: string;
     150              :     login: string;
     151              :     /// The Hyper Cloud database, only shown for SIDB connections
     152              :     databaseName?: string;
     153              :     coreAccessToken: string;
     154              :     dataCloudAccessToken: string;
     155              :     dataCloudInstanceUrl: string;
     156              :     coreTenantId: string;
     157              :     dataCloudTenantId: string;
     158              :     instanceUrlValidation: TextFieldValidationStatus;
     159              :     appConsumerValidation: TextFieldValidationStatus;
     160              :     updateInstanceUrl: React.ChangeEventHandler<HTMLInputElement>;
     161              :     updateAppConsumerKey: React.ChangeEventHandler<HTMLInputElement>;
     162              :     updateLogin: React.ChangeEventHandler<HTMLInputElement>;
     163              :     updateDatabaseName?: React.ChangeEventHandler<HTMLInputElement>;
     164              :     setupConnection: () => void;
     165              :     cancelSetup: () => void;
     166              :     resetSetup: () => void;
     167              :     onClose?: () => void;
     168              :     alias?: SalesforceConnectionAliasField;
     169              :     connectorNameAction?: React.ReactNode;
     170              :     statusText?: string;
     171              :     indicatorStatus?: IndicatorStatus;
     172              :     statusError?: string | null;
     173              :     connectionHealth?: ConnectionHealth;
     174              : }
     175              : 
     176            1 : export const SalesforceConnectionSettingsPage: React.FC<SalesforceConnectionSettingsPageProps> = props => {
     177            1 :     const connectorInfo = CONNECTOR_INFOS[props.connector ?? ConnectorType.SALESFORCE_DATA_CLOUD];
     178            1 :     return (
     179            1 :         <div className={style.layout}>
     180            1 :             <ConnectionInlineHeader
     181            1 :                 connector={connectorInfo}
     182            1 :                 connection={props.connectionState}
     183            1 :                 wrongPlatform={props.wrongPlatform}
     184            1 :                 setupConnection={props.setupConnection}
     185            1 :                 cancelSetup={props.cancelSetup}
     186            1 :                 resetSetup={props.resetSetup}
     187            1 :                 freezeInput={props.freezeInput}
     188            1 :                 onClose={props.onClose}
     189            1 :                 statusText={props.statusText}
     190            1 :                 indicatorStatus={props.indicatorStatus}
     191            1 :                 statusError={props.statusError}
     192            1 :                 connectionHealth={props.connectionHealth}
     193            1 :                 connectorNameAction={props.connectorNameAction}
     194            1 :             />
     195            1 :             <div className={style.body_container}>
     196            1 :                 {props.alias && (
     197            1 :                     <div className={style.section}>
     198            1 :                         <div className={classNames(style.section_layout, style.body_section_layout)}>
     199            1 :                             <TextField
     200            1 :                                 inputRef={props.alias.inputRef}
     201            1 :                                 data-testid="salesforce-alias"
     202            1 :                                 name={props.alias.name ?? 'Attached Database Alias'}
     203            1 :                                 caption={props.alias.caption ?? 'Name saved in login history and used to address this attached database in the shell'}
     204            1 :                                 value={props.alias.value}
     205            1 :                                 onChange={props.alias.onChange}
     206            1 :                                 placeholder="Alias"
     207            1 :                                 leadingVisual={() => <div>AS</div>}
     208            1 :                                 validation={props.alias.validation}
     209            1 :                                 logContext={LOG_CTX}
     210            1 :                                 disabled={props.freezeInput}
     211            1 :                                 readOnly={props.freezeInput}
     212            1 :                             />
     213            1 :                         </div>
     214            1 :                     </div>
     215              :                 )}
     216            1 :                 <div className={style.section}>
     217            1 :                     <div className={classNames(style.section_layout, style.body_section_layout)}>
     218            1 :                         <TextField
     219            1 :                             name="Salesforce Instance URL"
     220            1 :                             data-testid="salesforce-instance-url"
     221            1 :                             caption="URL of the Salesforce Instance"
     222            1 :                             value={props.instanceUrl}
     223            1 :                             onChange={props.updateInstanceUrl}
     224            1 :                             placeholder="Salesforce Instance"
     225            1 :                             leadingVisual={() => <div>URL</div>}
     226            1 :                             validation={props.instanceUrlValidation}
     227            1 :                             logContext={LOG_CTX}
     228            1 :                             disabled={props.freezeInput}
     229            1 :                             readOnly={props.freezeInput}
     230            1 :                         />
     231            1 :                         <TextField
     232            1 :                             name="Connected App"
     233            1 :                             data-testid="salesforce-app-consumer-key"
     234            1 :                             caption="Setup > App Manager > [App] > Manage Consumer Details"
     235            1 :                             value={props.appConsumerKey}
     236            1 :                             onChange={props.updateAppConsumerKey}
     237            1 :                             placeholder="Consumer Key"
     238            1 :                             leadingVisual={() => <div>ID</div>}
     239            1 :                             validation={props.appConsumerValidation}
     240            1 :                             logContext={LOG_CTX}
     241            1 :                             disabled={props.freezeInput}
     242            1 :                             readOnly={props.freezeInput}
     243            1 :                         />
     244            1 :                         <TextField
     245            1 :                             name="Salesforce Login"
     246            1 :                             data-testid="salesforce-login"
     247            1 :                             caption="Account offered as the sign-in hint"
     248            1 :                             value={props.login}
     249            1 :                             placeholder=""
     250            1 :                             leadingVisual={PersonIcon}
     251            1 :                             onChange={props.updateLogin}
     252            1 :                             readOnly={props.freezeInput}
     253            1 :                             disabled={props.freezeInput}
     254            1 :                             logContext={LOG_CTX}
     255            1 :                         />
     256            1 :                         {props.connector === ConnectorType.SIDB && (
     257            0 :                             <TextField
     258            0 :                                 name="Database Name"
     259            0 :                                 data-testid="sidb-database-name"
     260            0 :                                 caption="Hyper Cloud database that is queried"
     261            0 :                                 value={props.databaseName ?? ""}
     262            0 :                                 placeholder={SIDB_DEFAULT_DATABASE_NAME}
     263            0 :                                 leadingVisual={() => <div>DB</div>}
     264            0 :                                 onChange={props.updateDatabaseName}
     265            0 :                                 readOnly={props.freezeInput}
     266            0 :                                 disabled={props.freezeInput}
     267            0 :                                 logContext={LOG_CTX}
     268            0 :                             />
     269              :                         )}
     270            1 :                     </div>
     271            1 :                 </div>
     272            1 :                 <div className={style.section}>
     273            1 :                     <div className={classNames(style.section_layout, style.body_section_layout)}>
     274            1 :                         <TextField
     275            1 :                             name="Data Cloud Instance URL"
     276            1 :                             data-testid="salesforce-data-cloud-instance-url"
     277            1 :                             caption="URL of the Data Cloud instance derived from the access token"
     278            1 :                             value={props.dataCloudInstanceUrl}
     279            1 :                             placeholder=""
     280            1 :                             leadingVisual={() => <div>URL</div>}
     281            1 :                             readOnly
     282            1 :                             disabled
     283            1 :                             logContext={LOG_CTX}
     284            1 :                         />
     285            1 :                         <TextField
     286            1 :                             name="Data Cloud Tenant ID"
     287            1 :                             data-testid="salesforce-data-cloud-tenant-id"
     288            1 :                             caption="Tenant id for Data Cloud apis derived from the access token"
     289            1 :                             value={props.dataCloudTenantId}
     290            1 :                             placeholder=""
     291            1 :                             leadingVisual={() => <div>ID</div>}
     292            1 :                             readOnly
     293            1 :                             disabled
     294            1 :                             logContext={LOG_CTX}
     295            1 :                         />
     296            1 :                     </div>
     297            1 :                 </div>
     298            1 :                 <div className={style.section}>
     299            1 :                     <div className={classNames(style.section_layout, style.body_section_layout)}>
     300            1 :                         <TextField
     301            1 :                             name="Core Access Token"
     302            1 :                             data-testid="salesforce-core-access-token"
     303            1 :                             caption="Access Token for Salesforce Core"
     304            1 :                             value={props.coreAccessToken}
     305            1 :                             placeholder=""
     306            1 :                             leadingVisual={KeyIcon}
     307            1 :                             readOnly
     308            1 :                             disabled
     309            1 :                             logContext={LOG_CTX}
     310            1 :                         />
     311            1 :                         <TextField
     312            1 :                             name="Data Cloud Access Token"
     313            1 :                             data-testid="salesforce-data-cloud-access-token"
     314            1 :                             caption="Raw Data Cloud JWT"
     315            1 :                             value={props.dataCloudAccessToken}
     316            1 :                             placeholder=""
     317            1 :                             leadingVisual={KeyIcon}
     318            1 :                             readOnly
     319            1 :                             disabled
     320            1 :                             logContext={LOG_CTX}
     321            1 :                         />
     322            1 :                         <TextField
     323            1 :                             name="Core Tenant ID"
     324            1 :                             data-testid="salesforce-core-tenant-id"
     325            1 :                             caption="Tenant id for core apis"
     326            1 :                             value={props.coreTenantId}
     327            1 :                             placeholder=""
     328            1 :                             leadingVisual={() => <div>ID</div>}
     329            1 :                             readOnly
     330            1 :                             disabled
     331            1 :                             logContext={LOG_CTX}
     332            1 :                         />
     333            1 :                     </div>
     334            1 :                 </div>
     335            1 :             </div>
     336            1 :         </div>
     337              :     );
     338            1 : };
     339              : 
     340            1 : export const SalesforceConnectorSettings: React.FC<Props> = (props: Props) => {
     341            0 :     const sfSetup = useSalesforceSetup();
     342            0 :     const queryExecutor = useQueryExecutor();
     343              : 
     344              :     // Resolve connection state
     345            0 :     const [connectionState, dispatchConnectionState] = useConnectionById(props.connectionId);
     346            0 :     const connectionSetup = useConnectionSetup(props.connectionId);
     347            0 :     const { saving } = useConnectionSave(props.connectionId);
     348            0 :     const salesforceConnection = getSalesforceConnectionDetails(connectionState);
     349            0 :     const paramsKey = CONNECTOR_DESCRIPTORS[props.connector].paramsKey as 'salesforce' | 'sidb';
     350            0 :     const isSidb = props.connector === ConnectorType.SIDB;
     351            0 :     const editableParams = connectionState?.paramsDraft ?? connectionState?.params;
     352            0 :     const salesforceParams = editableParams && paramsKey in editableParams ? (editableParams as Record<typeof paramsKey, connection.SalesforceConnectionParams>)[paramsKey] : undefined;
     353              : 
     354            0 :     const buildPageState = (params: connection.SalesforceConnectionParams | undefined): PageState => ({
     355            0 :         instanceUrl: params?.instanceUrl ?? "",
     356            0 :         appConsumerKey: params?.appConsumerKey ?? "",
     357            0 :         login: params?.login ?? "",
     358            0 :         databaseName: params?.databaseName ?? SIDB_DEFAULT_DATABASE_NAME,
     359            0 :     });
     360            0 :     const [pageState, setPageState] = useConnectionDraftForm(props.connectionId,
     361            0 :         params => buildPageState(params && paramsKey in params ? (params as Record<typeof paramsKey, connection.SalesforceConnectionParams>)[paramsKey] : undefined),
     362            0 :         ({ databaseName, ...form }) => connectionParamsOf(CONNECTOR_DESCRIPTORS[props.connector], {
     363            0 :             ...salesforceParams,
     364            0 :             ...form,
     365            0 :             appConsumerSecret: salesforceParams?.appConsumerSecret ?? '',
     366            0 :             ...(isSidb ? { databaseName } : {}),
     367            0 :         }));
     368            0 :     const updateInstanceUrl: React.ChangeEventHandler<HTMLInputElement> = ev => setPageState(s => ({ ...s, instanceUrl: ev.target.value }));
     369            0 :     const updateAppConsumerKey: React.ChangeEventHandler<HTMLInputElement> = ev => setPageState(s => ({ ...s, appConsumerKey: ev.target.value }));
     370            0 :     const updateLogin: React.ChangeEventHandler<HTMLInputElement> = ev => setPageState(s => ({ ...s, login: ev.target.value }));
     371            0 :     const updateDatabaseName: React.ChangeEventHandler<HTMLInputElement> = ev => setPageState(s => ({ ...s, databaseName: ev.target.value }));
     372              : 
     373              :     // Maintain setting validations
     374            0 :     const [instanceUrlValidation, setInstanceUrlValidation] = React.useState<TextFieldValidationStatus>({
     375            0 :         type: VALIDATION_UNKNOWN,
     376            0 :         value: null
     377            0 :     });
     378            0 :     const [appConsumerValidation, setAppConsumerValidation] = React.useState<TextFieldValidationStatus>({
     379            0 :         type: VALIDATION_UNKNOWN,
     380            0 :         value: null
     381            0 :     });
     382            0 :     React.useEffect(() => {
     383            0 :         setInstanceUrlValidation({ type: VALIDATION_UNKNOWN, value: null });
     384            0 :         setAppConsumerValidation({ type: VALIDATION_UNKNOWN, value: null });
     385            0 :     }, [connectionState?.paramsDraft]);
     386              : 
     387            0 :     const setupParams = React.useMemo<connection.SalesforceConnectionParams>(() => ({
     388            0 :         instanceUrl: pageState.instanceUrl,
     389            0 :         appConsumerKey: pageState.appConsumerKey,
     390            0 :         appConsumerSecret: salesforceParams?.appConsumerSecret ?? '',
     391            0 :         login: pageState.login,
     392            0 :         ...(isSidb ? { databaseName: pageState.databaseName } : {}),
     393            0 :     }), [pageState, salesforceParams?.appConsumerSecret, isSidb]);
     394            0 :     const setupConnection = async () => {
     395            0 :         let validationSucceeded = true;
     396            0 :         if (pageState.instanceUrl == "") {
     397            0 :             validationSucceeded = false;
     398            0 :             setInstanceUrlValidation({ type: VALIDATION_ERROR, value: "Instance URL cannot be empty" });
     399            0 :         } else {
     400            0 :             setInstanceUrlValidation({ type: VALIDATION_UNKNOWN, value: null });
     401              :         }
     402            0 :         if (pageState.appConsumerKey === "") {
     403            0 :             validationSucceeded = false;
     404            0 :             setAppConsumerValidation({ type: VALIDATION_ERROR, value: "Connected App cannot be empty" });
     405            0 :         } else {
     406            0 :             setAppConsumerValidation({ type: VALIDATION_UNKNOWN, value: null });
     407              :         }
     408            0 :         if (!validationSucceeded || !sfSetup) return;
     409              : 
     410            0 :         await connectionSetup.run(async signal => {
     411            0 :             try {
     412            0 :                 const sfChannel = await sfSetup.setup(dispatchConnectionState, props.connector, setupParams, signal);
     413            0 :                 if (connectionState != null) {
     414            0 :                     await performHealthCheck(queryExecutor, connectionState.connectionId, { type: 'salesforce', channel: sfChannel }, dispatchConnectionState, signal);
     415              :                 }
     416            0 :             } catch {
     417              :                 // Setup updates connection state with the failure details.
     418            0 :             }
     419            0 :         });
     420            0 :     };
     421            0 :     const cancelSetup = connectionSetup.cancel;
     422            0 :     const resetSetup = async () => {
     423            0 :         if (sfSetup) {
     424            0 :             await sfSetup.reset(dispatchConnectionState);
     425              :         }
     426            0 :     };
     427              : 
     428              :     // Saving writes the settings into every session that uses the connection
     429            0 :     let freezeInput = saving;
     430            0 :     switch (connectionState?.connectionHealth) {
     431            0 :         case ConnectionHealth.NOT_STARTED:
     432            0 :         case ConnectionHealth.FAILED:
     433            0 :         case ConnectionHealth.CANCELLED:
     434            0 :             break;
     435            0 :         case ConnectionHealth.CONNECTING:
     436            0 :             freezeInput = true;
     437            0 :             break;
     438            0 :         case ConnectionHealth.ONLINE:
     439              :             break;
     440            0 :     }
     441              : 
     442              :     // Read the auth info
     443            0 :     const coreAccessToken = salesforceConnection?.proto.oauthState?.coreAccessToken;
     444            0 :     const dcAccessToken = salesforceConnection?.proto.oauthState?.dataCloudAccessToken;
     445            0 :     const dcAuthInfo = coreAccessToken && dcAccessToken
     446            0 :         ? collectSalesforceAuthInfo(coreAccessToken, dcAccessToken)
     447            0 :         : null;
     448              : 
     449              :     // Lock any changes?
     450            0 :     return (
     451            0 :         <SalesforceConnectionSettingsPage
     452            0 :             connectionState={connectionState}
     453            0 :             connector={props.connector}
     454            0 :             wrongPlatform={false}
     455            0 :             freezeInput={freezeInput}
     456            0 :             instanceUrl={pageState.instanceUrl}
     457            0 :             appConsumerKey={pageState.appConsumerKey}
     458            0 :             login={pageState.login}
     459            0 :             databaseName={pageState.databaseName}
     460            0 :             coreAccessToken={coreAccessToken?.accessToken ?? ''}
     461            0 :             dataCloudAccessToken={dcAuthInfo?.offcoreRawJwt ?? ''}
     462            0 :             dataCloudInstanceUrl={dcAuthInfo?.offcoreInstanceUrl ?? ''}
     463            0 :             coreTenantId={dcAuthInfo?.coreTenantId ?? ''}
     464            0 :             dataCloudTenantId={dcAuthInfo?.offcoreTenantId ?? ''}
     465            0 :             instanceUrlValidation={instanceUrlValidation}
     466            0 :             appConsumerValidation={appConsumerValidation}
     467            0 :             updateInstanceUrl={updateInstanceUrl}
     468            0 :             updateAppConsumerKey={updateAppConsumerKey}
     469            0 :             updateLogin={updateLogin}
     470            0 :             updateDatabaseName={updateDatabaseName}
     471            0 :             setupConnection={setupConnection}
     472            0 :             cancelSetup={cancelSetup}
     473            0 :             resetSetup={resetSetup}
     474            0 :             onClose={props.onClose}
     475            0 :         />
     476              :     );
     477            0 : };
        

Generated by: LCOV version 2.0-1