/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { observer } from 'mobx-react-lite'; import { useState } from 'react'; import { type RelationalDatabaseConnectionValueState, RELATIONAL_DATABASE_TAB_TYPE, POST_PROCESSOR_TYPE, } from '../../../../stores/editor/editor-state/element-editor-state/connection/ConnectionEditorState.js'; import { ResizablePanelGroup, ResizablePanel, ResizablePanelSplitter, CustomSelectorInput, ErrorIcon, FilledWindowMaximizeIcon, PanelHeader, PlusIcon, PanelFormTextField, ContextMenu, MenuContent, MenuContentItem, PanelListSelectorItem, ControlledDropdownMenu, PanelTabs, BlankPanelContent, ResizablePanelSplitterLine, PanelContent, Panel, Badge, PanelListSelectorItemLabel, PanelFormBooleanField, PanelHeaderActions, PanelDivider, PanelFormSection, Dialog, Modal, ModalBody, ModalFooter, ModalFooterButton, ModalHeader, Button, } from '@finos/legend-art'; import { type RelationalDatabaseConnection, type Store, type PostProcessor, type DatasourceSpecification, type AuthenticationStrategy, type PackageableConnection, DatabaseType, DelegatedKerberosAuthenticationStrategy, OAuthAuthenticationStrategy, SnowflakePublicAuthenticationStrategy, ApiTokenAuthenticationStrategy, UsernamePasswordAuthenticationStrategy, GCPWorkloadIdentityFederationAuthenticationStrategy, MiddleTierUsernamePasswordAuthenticationStrategy, TrinoDelegatedKerberosAuthenticationStrategy, EmbeddedH2DatasourceSpecification, LocalH2DatasourceSpecification, SnowflakeDatasourceSpecification, DatabricksDatasourceSpecification, StaticDatasourceSpecification, BigQueryDatasourceSpecification, RedshiftDatasourceSpecification, PackageableElementExplicitReference, MapperPostProcessor, SpannerDatasourceSpecification, TrinoDatasourceSpecification, guaranteeRelationalDatabaseConnection, } from '@finos/legend-graph'; import type { LegendStudioApplicationPlugin } from '../../../../stores/LegendStudioApplicationPlugin.js'; import type { STO_Relational_LegendStudioApplicationPlugin_Extension } from '../../../../stores/extensions/STO_Relational_LegendStudioApplicationPlugin_Extension.js'; import { useEditorStore } from '../../EditorStoreProvider.js'; import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor'; import { CodeEditor } from '@finos/legend-lego/code-editor'; import { buildElementOption, type PackageableElementOption, } from '@finos/legend-lego/graph-editor'; import { connection_setStore } from '../../../../stores/graph-modifier/DSL_Mapping_GraphModifierHelper.js'; import { apiTokenAuthenticationStrategy_setApiToken, bigQueryDatasourceSpecification_setDefaultDataset, bigQueryDatasourceSpecification_setProjectId, bigQueryDatasourceSpecification_setProxyHost, bigQueryDatasourceSpecification_setProxyPort, databricksDatasourceSpecification_setHostName, databricksDatasourceSpecification_setHttpPath, databricksDatasourceSpecification_setPort, databricksDatasourceSpecification_setProtocol, dBConnection_setQuoteIdentifiers, dBConnection_setType, delegatedKerberosAuthenticationStrategy_setServerPrincipal, embeddedH2DatasourceSpecification_setAutoServerMode, embeddedH2DatasourceSpecification_setDatabaseName, embeddedH2DatasourceSpecification_setDirectory, localH2DatasourceSpecification_setTestDataSetupSqls, oAuthAuthenticationStrategy_setOauthKey, oAuthAuthenticationStrategy_setScopeName, redshiftDatasourceSpecification_setClusterID, redshiftDatasourceSpecification_setDatabaseName, redshiftDatasourceSpecification_setEndpointURL, redshiftDatasourceSpecification_setHost, redshiftDatasourceSpecification_setPort, redshiftDatasourceSpecification_setRegion, snowflakeDatasourceSpec_setAccountName, snowflakeDatasourceSpec_setAccountType, snowflakeDatasourceSpec_setCloudType, snowflakeDatasourceSpec_setDatabaseName, snowflakeDatasourceSpec_setNonProxyHosts, snowflakeDatasourceSpec_setOrganization, snowflakeDatasourceSpec_setProxyHost, snowflakeDatasourceSpec_setProxyPort, snowflakeDatasourceSpec_setQuotedIdentifiersIgnoreCase, snowflakeDatasourceSpec_setEnableQueryTags, snowflakeDatasourceSpec_setRegion, snowflakeDatasourceSpec_setRole, snowflakeDatasourceSpec_setWarehouseName, snowflakePublicAuthenticationStrategy_setPassPhraseVaultReference, snowflakePublicAuthenticationStrategy_setPrivateKeyVaultReference, snowflakePublicAuthenticationStrategy_setPublicUserName, spannerDatasourceSpecification_setDatabaseId, spannerDatasourceSpecification_setProxyHost, spannerDatasourceSpecification_setInstanceId, spannerDatasourceSpecification_setProxyPort, spannerDatasourceSpecification_setProjectId, staticDatasourceSpecification_setDatabaseName, staticDatasourceSpecification_setHost, staticDatasourceSpecification_setPort, trinoDatasourceSpecification_setHost, trinoDatasourceSpecification_setPort, trinoDatasourceSpecification_setCatalog, trinoDatasourceSpecification_setSchema, trinoDatasourceSpecification_setClientTags, trinoDatasourceSpecification_setSsl, trinoDatasourceSpecification_setTrustStorePathVaultReference, trinoDatasourceSpecification_setTrustStorePasswordVaultReference, trinoDelegatedKerberosAuthenticationStrategy_setKerberosRemoteServiceName, trinoDelegatedKerberosAuthenticationStrategy_setKerberosUseCanonicalHostname, usernamePasswordAuthenticationStrategy_setBaseVaultReference, usernamePasswordAuthenticationStrategy_setPasswordVaultReference, usernamePasswordAuthenticationStrategy_setUserNameVaultReference, gcpWorkloadIdentityFederationAuthenticationStrategy_setServiceAccountEmail, gcpWorkloadIdentityFederationAuthenticationStrategy_setAdditionalGcpScopes, middleTierUsernamePasswordAuthenticationStrategy_setVaultReference, relationalDatabaseConnection_addPostProcessor, relationalDatabaseConnection_deletePostProcessor, snowflakeDatasourceSpec_setTempTableDb, snowflakeDatasourceSpec_setTempTableSchema, dBConnection_setQueryTimeOut, } from '../../../../stores/graph-modifier/STO_Relational_GraphModifierHelper.js'; import { MapperPostProcessorEditor } from './post-processor-editor/MapperPostProcessorEditor.js'; import { UnsupportedEditorPanel } from '../UnsupportedElementEditor.js'; import type { MapperPostProcessorEditorState } from '../../../../stores/editor/editor-state/element-editor-state/connection/PostProcessorEditorState.js'; import { prettyCONSTName, uniq } from '@finos/legend-shared'; import { useApplicationStore } from '@finos/legend-application'; export type RelationalDatabaseConnectionOption = { label: React.ReactNode; value: PackageableConnection; }; export const buildRelationalDatabaseConnectionOption = ( connection: PackageableConnection, ): RelationalDatabaseConnectionOption => { const connectionValue = guaranteeRelationalDatabaseConnection(connection); return { value: connection, label: (
{connection.name}
{connectionValue.type}
{connection.path}
), }; }; const LocalH2DatasourceSpecificationEditor = observer( (props: { sourceSpec: LocalH2DatasourceSpecification; isReadOnly: boolean; }) => { const applicationStore = useApplicationStore(); const { sourceSpec, isReadOnly } = props; const [showPopUp, setShowPopUp] = useState(false); const openInPopUp = (): void => setShowPopUp(true); const closePopUp = (): void => setShowPopUp(false); // TODO?: support CSV and toggler to go to CSV mode const SQLValue = sourceSpec.testDataSetupSqls.join('\n'); return ( <> {showPopUp && (
localH2DatasourceSpecification_setTestDataSetupSqls( sourceSpec, value ? [value] : [], ) } isReadOnly={isReadOnly} language={CODE_EDITOR_LANGUAGE.SQL} />
)}
Test Data Setup SQL
localH2DatasourceSpecification_setTestDataSetupSqls( sourceSpec, value ? [value] : [], ) } isReadOnly={isReadOnly} language={CODE_EDITOR_LANGUAGE.SQL} />
); }, ); const StaticDatasourceSpecificationEditor = observer( (props: { sourceSpec: StaticDatasourceSpecification; isReadOnly: boolean; }) => { const { sourceSpec, isReadOnly } = props; const changePort: React.ChangeEventHandler = (event) => { const val = event.target.value; staticDatasourceSpecification_setPort(sourceSpec, parseInt(val, 10)); }; return ( <> staticDatasourceSpecification_setHost(sourceSpec, value ?? '') } />
port
staticDatasourceSpecification_setDatabaseName( sourceSpec, value ?? '', ) } /> ); }, ); const EmbeddedH2DatasourceSpecificationEditor = observer( (props: { sourceSpec: EmbeddedH2DatasourceSpecification; isReadOnly: boolean; }) => { const { sourceSpec, isReadOnly } = props; return ( <> embeddedH2DatasourceSpecification_setDatabaseName( sourceSpec, value ?? '', ) } /> embeddedH2DatasourceSpecification_setDirectory( sourceSpec, value ?? '', ) } /> embeddedH2DatasourceSpecification_setAutoServerMode( sourceSpec, Boolean(value), ) } /> ); }, ); const DatabricksDatasourceSpecificationEditor = observer( (props: { sourceSpec: DatabricksDatasourceSpecification; isReadOnly: boolean; }) => { const { sourceSpec, isReadOnly } = props; return ( <> databricksDatasourceSpecification_setHostName( sourceSpec, value ?? '', ) } /> databricksDatasourceSpecification_setPort(sourceSpec, value ?? '') } /> databricksDatasourceSpecification_setProtocol( sourceSpec, value ?? '', ) } /> databricksDatasourceSpecification_setHttpPath( sourceSpec, value ?? '', ) } /> ); }, ); const TrinoDatasourceSpecificationEditor = observer( (props: { sourceSpec: TrinoDatasourceSpecification; isReadOnly: boolean; }) => { const { sourceSpec, isReadOnly } = props; const changePort: React.ChangeEventHandler = (event) => { const val = event.target.value; trinoDatasourceSpecification_setPort(sourceSpec, parseInt(val, 10)); }; return ( <> trinoDatasourceSpecification_setHost(sourceSpec, value ?? '') } />
port (required)
trinoDatasourceSpecification_setCatalog(sourceSpec, value) } /> trinoDatasourceSpecification_setSchema(sourceSpec, value) } /> trinoDatasourceSpecification_setClientTags(sourceSpec, value) } /> trinoDatasourceSpecification_setSsl(sourceSpec, Boolean(value)) } /> trinoDatasourceSpecification_setTrustStorePathVaultReference( sourceSpec, value, ) } /> trinoDatasourceSpecification_setTrustStorePasswordVaultReference( sourceSpec, value, ) } /> ); }, ); const SnowflakeDatasourceSpecificationEditor = observer( (props: { sourceSpec: SnowflakeDatasourceSpecification; isReadOnly: boolean; }) => { const { sourceSpec, isReadOnly } = props; return ( <> snowflakeDatasourceSpec_setAccountName(sourceSpec, value ?? '') } /> snowflakeDatasourceSpec_setRegion(sourceSpec, value ?? '') } /> snowflakeDatasourceSpec_setWarehouseName(sourceSpec, value ?? '') } /> snowflakeDatasourceSpec_setDatabaseName(sourceSpec, value ?? '') } /> snowflakeDatasourceSpec_setCloudType(sourceSpec, value) } /> snowflakeDatasourceSpec_setProxyHost(sourceSpec, value) } /> snowflakeDatasourceSpec_setProxyPort(sourceSpec, value) } /> snowflakeDatasourceSpec_setNonProxyHosts(sourceSpec, value) } /> snowflakeDatasourceSpec_setOrganization(sourceSpec, value) } /> snowflakeDatasourceSpec_setAccountType(sourceSpec, value) } /> snowflakeDatasourceSpec_setRole(sourceSpec, value) } /> snowflakeDatasourceSpec_setTempTableDb( sourceSpec, value ?? undefined, ) } /> snowflakeDatasourceSpec_setTempTableSchema( sourceSpec, value ?? undefined, ) } /> {/* TODO: we should reconsider adding this field, it's an optional boolean, should we default it to `undefined` when it's `false`?*/} snowflakeDatasourceSpec_setQuotedIdentifiersIgnoreCase( sourceSpec, Boolean(value), ) } /> snowflakeDatasourceSpec_setEnableQueryTags( sourceSpec, Boolean(value), ) } /> ); }, ); const RedshiftDatasourceSpecificationEditor = observer( (props: { sourceSpec: RedshiftDatasourceSpecification; isReadOnly: boolean; }) => { const { sourceSpec, isReadOnly } = props; const changePort: React.ChangeEventHandler = (event) => { const val = event.target.value; redshiftDatasourceSpecification_setPort(sourceSpec, parseInt(val, 10)); }; return ( <> redshiftDatasourceSpecification_setHost(sourceSpec, value ?? '') } />
port (required)
redshiftDatasourceSpecification_setDatabaseName( sourceSpec, value ?? '', ) } /> redshiftDatasourceSpecification_setRegion(sourceSpec, value ?? '') } /> redshiftDatasourceSpecification_setClusterID( sourceSpec, value ?? '', ) } /> redshiftDatasourceSpecification_setEndpointURL( sourceSpec, value ?? '', ) } /> ); }, ); const BigQueryDatasourceSpecificationEditor = observer( (props: { sourceSpec: BigQueryDatasourceSpecification; isReadOnly: boolean; }) => { const { sourceSpec, isReadOnly } = props; return ( <> bigQueryDatasourceSpecification_setProjectId( sourceSpec, value ?? '', ) } /> bigQueryDatasourceSpecification_setDefaultDataset( sourceSpec, value ?? '', ) } /> bigQueryDatasourceSpecification_setProxyHost( sourceSpec, value ?? '', ) } /> bigQueryDatasourceSpecification_setProxyPort( sourceSpec, value ?? '', ) } /> ); }, ); const SpannerDatasourceSpecificationEditor = observer( (props: { sourceSpec: SpannerDatasourceSpecification; isReadOnly: boolean; }) => { const { sourceSpec, isReadOnly } = props; return ( <> spannerDatasourceSpecification_setProjectId(sourceSpec, value ?? '') } /> spannerDatasourceSpecification_setInstanceId( sourceSpec, value ?? '', ) } /> spannerDatasourceSpecification_setDatabaseId( sourceSpec, value ?? '', ) } /> spannerDatasourceSpecification_setProxyHost(sourceSpec, value ?? '') } /> spannerDatasourceSpecification_setProxyPort(sourceSpec, value ?? '') } /> ); }, ); // auth strategy const DelegatedKerberosAuthenticationStrategyEditor = observer( (props: { authSpec: DelegatedKerberosAuthenticationStrategy; isReadOnly: boolean; }) => { const { authSpec, isReadOnly } = props; return ( <> delegatedKerberosAuthenticationStrategy_setServerPrincipal( authSpec, value ?? '', ) } /> ); }, ); const ApiTokenAuthenticationStrategyEditor = observer( (props: { authSpec: ApiTokenAuthenticationStrategy; isReadOnly: boolean; }) => { const { authSpec, isReadOnly } = props; return ( <> apiTokenAuthenticationStrategy_setApiToken(authSpec, value ?? '') } /> ); }, ); const SnowflakePublicAuthenticationStrategyEditor = observer( (props: { authSpec: SnowflakePublicAuthenticationStrategy; isReadOnly: boolean; }) => { const { authSpec, isReadOnly } = props; return ( <> snowflakePublicAuthenticationStrategy_setPrivateKeyVaultReference( authSpec, value ?? '', ) } /> snowflakePublicAuthenticationStrategy_setPassPhraseVaultReference( authSpec, value ?? '', ) } /> snowflakePublicAuthenticationStrategy_setPublicUserName( authSpec, value ?? '', ) } /> ); }, ); const OAuthAuthenticationStrategyEditor = observer( (props: { authSpec: OAuthAuthenticationStrategy; isReadOnly: boolean }) => { const { authSpec, isReadOnly } = props; return ( <> oAuthAuthenticationStrategy_setOauthKey(authSpec, value ?? '') } /> oAuthAuthenticationStrategy_setScopeName(authSpec, value ?? '') } /> ); }, ); const UsernamePasswordAuthenticationStrategyEditor = observer( (props: { authSpec: UsernamePasswordAuthenticationStrategy; isReadOnly: boolean; }) => { const { authSpec, isReadOnly } = props; return ( <> usernamePasswordAuthenticationStrategy_setBaseVaultReference( authSpec, value, ) } /> usernamePasswordAuthenticationStrategy_setUserNameVaultReference( authSpec, value ?? '', ) } /> usernamePasswordAuthenticationStrategy_setPasswordVaultReference( authSpec, value ?? '', ) } /> ); }, ); // Middle Tier Username Password Authentication Strategy obtains credentials(username and password) from the provided vault reference const MiddleTierUsernamePasswordAuthenticationStrategyEditor = observer( (props: { authSpec: MiddleTierUsernamePasswordAuthenticationStrategy; isReadOnly: boolean; }) => { const { authSpec, isReadOnly } = props; return ( <> middleTierUsernamePasswordAuthenticationStrategy_setVaultReference( authSpec, value ?? '', ) } /> ); }, ); const TrinoDelegatedKerberosAuthenticationStrategyEditor = observer( (props: { authSpec: TrinoDelegatedKerberosAuthenticationStrategy; isReadOnly: boolean; }) => { const { authSpec, isReadOnly } = props; return ( <> trinoDelegatedKerberosAuthenticationStrategy_setKerberosRemoteServiceName( authSpec, value ?? '', ) } /> trinoDelegatedKerberosAuthenticationStrategy_setKerberosUseCanonicalHostname( authSpec, Boolean(value), ) } /> ); }, ); const GCPWorkloadIdentityFederationAuthenticationStrategyEditor = observer( (props: { authSpec: GCPWorkloadIdentityFederationAuthenticationStrategy; isReadOnly: boolean; }) => { const { authSpec, isReadOnly } = props; const GCPScopes = authSpec.additionalGcpScopes.join('\n'); return ( <> gcpWorkloadIdentityFederationAuthenticationStrategy_setServiceAccountEmail( authSpec, value ?? '', ) } /> gcpWorkloadIdentityFederationAuthenticationStrategy_setAdditionalGcpScopes( authSpec, value ? [value] : [], ) } /> ); }, ); const RelationalConnectionStoreEditor = observer( (props: { connectionValueState: RelationalDatabaseConnectionValueState; isReadOnly: boolean; }) => { const { connectionValueState, isReadOnly } = props; const applicationStore = connectionValueState.editorStore.applicationStore; const connection = connectionValueState.connection; // store const isStoreEmpty = connectionValueState.storeValidationResult; const noStoreLabel = (
(none)
); const stores = connectionValueState.editorStore.graphManagerState.graph.ownStores; const options = stores.map(buildElementOption); const store = connection.store?.value; const selectedStore = { value: store, label: isStoreEmpty ? noStoreLabel : store?.path, } as PackageableElementOption; const onStoreChange = ( val: PackageableElementOption | null, ): void => { if (val) { connection_setStore( connection, PackageableElementExplicitReference.create(val.value), ); } }; const openDatabaseBuilder = (): void => connectionValueState.editorStore.explorerTreeState.buildDatabase( connection, isReadOnly, ); return (
Store
); }, ); const renderEditorPostProcessor = ( connectionValueState: RelationalDatabaseConnectionValueState, postProcessor: PostProcessor, isReadOnly: boolean, plugins: LegendStudioApplicationPlugin[], ): React.ReactNode => { if (postProcessor instanceof MapperPostProcessor) { return ( ); } else { const extraPostProcessorEditorRenderers = plugins.flatMap( (plugin) => ( plugin as STO_Relational_LegendStudioApplicationPlugin_Extension ).getExtraPostProcessorEditorRenderers?.() ?? [], ); for (const editorRenderer of extraPostProcessorEditorRenderers) { const editor = editorRenderer(postProcessor, connectionValueState, false); if (editor) { return editor; } } return ( ); } }; const PostProcessorRelationalConnectionEditor = observer( (props: { connectionValueState: RelationalDatabaseConnectionValueState; isReadOnly: boolean; }) => { const { connectionValueState, isReadOnly } = props; const connection = connectionValueState.connection; const postProcessors = connection.postProcessors; const editorStore = useEditorStore(); const observerContext = editorStore.changeDetectionState.observerContext; const plugins = editorStore.pluginManager.getApplicationPlugins(); const postProcessorState = connectionValueState.postProcessorState; const deletePostProcessor = (postProcessor: PostProcessor): (() => void) => (): void => { relationalDatabaseConnection_deletePostProcessor( connectionValueState, postProcessor, ); if ( postProcessor === connectionValueState.postProcessorState?.postProcessor ) { connectionValueState.postProcessorState.setPostProcessorState( undefined, ); } }; const postProcessorOptions = ( Object.values(POST_PROCESSOR_TYPE) as string[] ) .concat( plugins.flatMap( (plugin) => ( plugin as STO_Relational_LegendStudioApplicationPlugin_Extension ).getExtraPostProcessorClassifiers?.() ?? [], ), ) .map((type) => ({ value: type, label: type, })); const addPostProcessor = (postProcessorType: string): (() => void) => (): void => { switch (postProcessorType) { case POST_PROCESSOR_TYPE.MAPPER: { relationalDatabaseConnection_addPostProcessor( connectionValueState, new MapperPostProcessor(), observerContext, ); break; } default: { const extraPostProcessorCreators = plugins.flatMap( (plugin) => ( plugin as STO_Relational_LegendStudioApplicationPlugin_Extension ).getExtraPostProcessorCreators?.() ?? [], ); for (const creator of extraPostProcessorCreators) { creator(postProcessorType, connectionValueState, observerContext); } } } connectionValueState.postProcessorState?.setPostProcessorState( connectionValueState.connection.postProcessors[ connectionValueState.connection.postProcessors.length - 1 ], ); }; const selectPostProcessor = (postProcessor: PostProcessor): void => { connectionValueState.selectPostProcessor(postProcessor); }; const getPostProcessorLabel = (postProcessor: PostProcessor): string => { if (postProcessor instanceof MapperPostProcessor) { return POST_PROCESSOR_TYPE.MAPPER; } else { const extraPostProcessorEditorClassifier = plugins.flatMap( (plugin) => ( plugin as STO_Relational_LegendStudioApplicationPlugin_Extension ).getExtraPostProcessorClassifierGetters?.() ?? [], ); for (const classify of extraPostProcessorEditorClassifier) { const label = classify(postProcessor); if (label) { return label; } } } return 'unknown type'; }; return (
( New {postProcessorType.label} Post-Processor ), )} menuProps={{ anchorOrigin: { vertical: 'bottom', horizontal: 'right', }, transformOrigin: { vertical: 'top', horizontal: 'right', }, elevation: 7, }} > {postProcessors.map((postProcessor, idx) => ( Delete } menuProps={{ elevation: 7 }} > selectPostProcessor(postProcessor)} isSelected={ postProcessor === postProcessorState?.postProcessor } > ))} {postProcessorState?.postProcessor && renderEditorPostProcessor( connectionValueState, postProcessorState.postProcessor, isReadOnly, plugins, )} {!postProcessorState && ( {!postProcessors.length ? 'Add a post-processor' : 'Choose a post-processor to view'} )}
); }, ); const renderDatasourceSpecificationEditor = ( connection: RelationalDatabaseConnection, isReadOnly: boolean, plugins: LegendStudioApplicationPlugin[], ): React.ReactNode => { const sourceSpec = connection.datasourceSpecification; if (sourceSpec instanceof StaticDatasourceSpecification) { return ( ); } else if (sourceSpec instanceof EmbeddedH2DatasourceSpecification) { return ( ); } else if (sourceSpec instanceof DatabricksDatasourceSpecification) { return ( ); } else if (sourceSpec instanceof SnowflakeDatasourceSpecification) { return ( ); } else if (sourceSpec instanceof BigQueryDatasourceSpecification) { return ( ); } else if (sourceSpec instanceof RedshiftDatasourceSpecification) { return ( ); } else if (sourceSpec instanceof LocalH2DatasourceSpecification) { return ( ); } else if (sourceSpec instanceof SpannerDatasourceSpecification) { return ( ); } else if (sourceSpec instanceof TrinoDatasourceSpecification) { return ( ); } else { const extraDatasourceSpecificationEditorRenderers = plugins.flatMap( (plugin) => ( plugin as STO_Relational_LegendStudioApplicationPlugin_Extension ).getExtraDatasourceSpecificationEditorRenderers?.() ?? [], ); for (const editorRenderer of extraDatasourceSpecificationEditorRenderers) { const editor = editorRenderer(sourceSpec, isReadOnly); if (editor) { return editor; } } // TODO: create unsupported screen return null; } }; const renderAuthenticationStrategyEditor = ( connection: RelationalDatabaseConnection, isReadOnly: boolean, plugins: LegendStudioApplicationPlugin[], ): React.ReactNode => { const authSpec = connection.authenticationStrategy; if (authSpec instanceof DelegatedKerberosAuthenticationStrategy) { return ( ); } else if (authSpec instanceof SnowflakePublicAuthenticationStrategy) { return ( ); } else if (authSpec instanceof ApiTokenAuthenticationStrategy) { return ( ); } else if (authSpec instanceof OAuthAuthenticationStrategy) { return ( ); } else if (authSpec instanceof UsernamePasswordAuthenticationStrategy) { return ( ); } else if ( authSpec instanceof MiddleTierUsernamePasswordAuthenticationStrategy ) { return ( ); } else if ( authSpec instanceof GCPWorkloadIdentityFederationAuthenticationStrategy ) { return ( ); } else if (authSpec instanceof TrinoDelegatedKerberosAuthenticationStrategy) { return ( ); } else { const extraAuthenticationStrategyEditorRenderers = plugins.flatMap( (plugin) => ( plugin as STO_Relational_LegendStudioApplicationPlugin_Extension ).getExtraAuthenticationStrategyEditorRenderers?.() ?? [], ); for (const editorRenderer of extraAuthenticationStrategyEditorRenderers) { const editor = editorRenderer(authSpec, isReadOnly); if (editor) { return editor; } } // TODO: create unsupported screen return null; } }; export const RelationalConnectionGeneralEditor = observer( (props: { connectionValueState: RelationalDatabaseConnectionValueState; isReadOnly: boolean; hideHeader?: boolean; }) => { const { connectionValueState, isReadOnly, hideHeader } = props; const connection = connectionValueState.connection; const editorStore = useEditorStore(); const applicationStore = editorStore.applicationStore; const plugins = editorStore.pluginManager.getApplicationPlugins(); const databseTypeConfigs = connectionValueState.editorStore.graphState .relationalDatabseTypeConfigurations; const availableDbTypes = databseTypeConfigs?.length ? // Currently H2 Flow is not returned in relational configs. We will remove this once it is properly returned as a supported flow in engine uniq([DatabaseType.H2, ...databseTypeConfigs.map((e) => e.type)]) : Object.values(DatabaseType); const dbTypes = availableDbTypes.map((dbType) => ({ value: dbType, label: dbType, })); const selectedDbType = { value: connection.type, label: connection.type, }; const changeTimeOut: React.ChangeEventHandler = ( event, ) => { const val = event.target.value; dBConnection_setQueryTimeOut( connection, val === '' ? undefined : parseInt(val, 10), ); }; const onTypeChange = ( val: { label: string; value: string } | null, ): void => { dBConnection_setType(connection, val?.value ?? DatabaseType.H2); if (connectionValueState.selectedValidDatasources[0]) { connectionValueState.changeDatasourceSpec( connectionValueState.selectedValidDatasources[0], ); } if (connectionValueState.selectedValidAuthenticationStrategies[0]) { connectionValueState.changeAuthenticationStrategy( connectionValueState.selectedValidAuthenticationStrategies[0], ); } }; // source spec type const sourceSpecOptions = connectionValueState.selectedValidDatasources.map( (type) => ({ value: type, label: prettyCONSTName(type), }), ); const selectedSourceSpec = ( spec: DatasourceSpecification, ): { label: string; value: string | undefined } => ({ label: prettyCONSTName( connectionValueState.selectedDatasourceSpecificationType(spec) ?? 'Unknown', ), value: connectionValueState.selectedDatasourceSpecificationType(spec), }); const onSourceSpecChange = ( val: { label: string; value: string | undefined } | null, ): void => { if (val?.value) { connectionValueState.changeDatasourceSpec(val.value); } }; // auth type const authOptions = connectionValueState.selectedValidAuthenticationStrategies.map( (type) => ({ value: type, label: prettyCONSTName(type), }), ); const selectedAuth = ( auth: AuthenticationStrategy, ): { label: string; value: string | undefined } => ({ label: prettyCONSTName( connectionValueState.selectedAuthenticationStrategyType(auth) ?? 'Unknown', ), value: connectionValueState.selectedAuthenticationStrategyType(auth), }); const onAuthStrategyChange = ( val: { label: string; value: string | undefined } | null, ): void => { if (val?.value) { connectionValueState.changeAuthenticationStrategy(val.value); } }; // HACKY: if (connection.localMode) { return (
Database Type
); } return (
{!hideHeader && }
Database Type
dBConnection_setQuoteIdentifiers(connection, Boolean(value)) } />
Query timeout (in seconds)
Datasource
{renderDatasourceSpecificationEditor( connection, isReadOnly, plugins, )}
Authentication
{renderAuthenticationStrategyEditor( connection, isReadOnly, plugins, )}
); }, ); export const RelationalDatabaseConnectionEditor = observer( (props: { connectionValueState: RelationalDatabaseConnectionValueState; isReadOnly: boolean; }) => { const { connectionValueState, isReadOnly } = props; const selectedTab = connectionValueState.selectedTab; const changeTab = (tab: string) => (): void => { connectionValueState.setSelectedTab(tab as RELATIONAL_DATABASE_TAB_TYPE); }; return ( {selectedTab === RELATIONAL_DATABASE_TAB_TYPE.GENERAL && ( )} {selectedTab === RELATIONAL_DATABASE_TAB_TYPE.STORE && ( )} {selectedTab === RELATIONAL_DATABASE_TAB_TYPE.POST_PROCESSORS && ( )} ); }, );