/**
* 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 && (
)}
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 ?? '')
}
/>
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 ?? '')
}
/>
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 = (
);
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 (
);
},
);
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 (
);
}
return (
{!hideHeader && }
Database Type
dBConnection_setQuoteIdentifiers(connection, Boolean(value))
}
/>
{renderDatasourceSpecificationEditor(
connection,
isReadOnly,
plugins,
)}
{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 && (
)}
);
},
);