UNPKG

@finos/legend-studio

Version:
476 lines (443 loc) 15.9 kB
/** * 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 { computed, observable, action, makeObservable } from 'mobx'; import type { EditorStore } from '../../../EditorStore.js'; import { guaranteeType, uuid, UnsupportedOperationError, } from '@finos/legend-shared'; import { ElementEditorState } from './../ElementEditorState.js'; import type { StoreRelational_LegendStudioApplicationPlugin_Extension } from '../../../StoreRelational_LegendStudioApplicationPlugin_Extension.js'; import { DatabaseBuilderState } from './DatabaseBuilderState.js'; import { type PackageableElement, type Connection, type ValidationIssue, type AuthenticationStrategy, type DatasourceSpecification, PackageableConnection, JsonModelConnection, FlatDataConnection, RelationalDatabaseConnection, DefaultH2AuthenticationStrategy, DelegatedKerberosAuthenticationStrategy, OAuthAuthenticationStrategy, UsernamePasswordAuthenticationStrategy, ApiTokenAuthenticationStrategy, SnowflakePublicAuthenticationStrategy, GCPApplicationDefaultCredentialsAuthenticationStrategy, GCPWorkloadIdentityFederationAuthenticationStrategy, EmbeddedH2DatasourceSpecification, LocalH2DatasourceSpecification, DatabricksDatasourceSpecification, SnowflakeDatasourceSpecification, BigQueryDatasourceSpecification, StaticDatasourceSpecification, RedshiftDatasourceSpecification, createValidationError, isStubbed_PackageableElement, } from '@finos/legend-graph'; import type { DSLMapping_LegendStudioApplicationPlugin_Extension } from '../../../DSLMapping_LegendStudioApplicationPlugin_Extension.js'; import { relationDbConnection_setNewAuthenticationStrategy, relationDbConnection_setDatasourceSpecification, } from '../../../graphModifier/StoreRelational_GraphModifierHelper.js'; export abstract class ConnectionValueState { editorStore: EditorStore; connection: Connection; constructor(editorStore: EditorStore, connection: Connection) { this.editorStore = editorStore; this.connection = connection; } abstract label(): string; } export enum RELATIONAL_DATABASE_TAB_TYPE { GENERAL = 'GENERAL', STORE = 'STORE', } export enum CORE_DATASOURCE_SPEC_TYPE { STATIC = 'STATIC', H2_LOCAL = 'H2_LOCAL', H2_EMBEDDED = 'H2_EMBEDDED', DATABRICKS = 'DATABRICKS', SNOWFLAKE = 'SNOWFLAKE', REDSHIFT = 'REDSHIFT', BIGQUERY = 'BIGQUERY', } export enum CORE_AUTHENTICATION_STRATEGY_TYPE { DELEGATED_KERBEROS = 'DELEGATED_KERBEROS', H2_DEFAULT = 'H2_DEFAULT', SNOWFLAKE_PUBLIC = 'SNOWFLAKE_PUBLIC', GCP_APPLICATION_DEFAULT_CREDENTIALS = 'GCP_APPLICATION_DEFAULT_CREDENTIALS', API_TOKEN = 'API_TOKEN', OAUTH = 'OAUTH', USERNAME_PASSWORD = 'USERNAME_PASSWORD', GCP_WORKLOAD_IDENTITY_FEDERATION = 'GCP_WORKLOAD_IDENTITY_FEDERATION', } export class RelationalDatabaseConnectionValueState extends ConnectionValueState { override connection: RelationalDatabaseConnection; selectedTab = RELATIONAL_DATABASE_TAB_TYPE.GENERAL; databaseBuilderState: DatabaseBuilderState; constructor( editorStore: EditorStore, connection: RelationalDatabaseConnection, ) { super(editorStore, connection); makeObservable(this, { setSelectedTab: action, databaseBuilderState: observable, selectedTab: observable, }); this.connection = connection; this.databaseBuilderState = new DatabaseBuilderState( editorStore, connection, ); } get storeValidationResult(): ValidationIssue | undefined { return isStubbed_PackageableElement(this.connection.store.value) ? createValidationError(['Connection database cannot be empty']) : undefined; } setSelectedTab(val: RELATIONAL_DATABASE_TAB_TYPE): void { this.selectedTab = val; } label(): string { return `${this.connection.type} connection`; } get selectedDatasourceSpec(): string { const spec = this.connection.datasourceSpecification; if (spec instanceof StaticDatasourceSpecification) { return CORE_DATASOURCE_SPEC_TYPE.STATIC; } else if (spec instanceof EmbeddedH2DatasourceSpecification) { return CORE_DATASOURCE_SPEC_TYPE.H2_EMBEDDED; } else if (spec instanceof DatabricksDatasourceSpecification) { return CORE_DATASOURCE_SPEC_TYPE.DATABRICKS; } else if (spec instanceof SnowflakeDatasourceSpecification) { return CORE_DATASOURCE_SPEC_TYPE.SNOWFLAKE; } else if (spec instanceof BigQueryDatasourceSpecification) { return CORE_DATASOURCE_SPEC_TYPE.BIGQUERY; } else if (spec instanceof LocalH2DatasourceSpecification) { return CORE_DATASOURCE_SPEC_TYPE.H2_LOCAL; } else if (spec instanceof RedshiftDatasourceSpecification) { return CORE_DATASOURCE_SPEC_TYPE.REDSHIFT; } const extraDatasourceSpecificationTypeGetters = this.editorStore.pluginManager .getApplicationPlugins() .flatMap( (plugin) => ( plugin as StoreRelational_LegendStudioApplicationPlugin_Extension ).getExtraDatasourceSpecificationTypeGetters?.() ?? [], ); for (const typeGetter of extraDatasourceSpecificationTypeGetters) { const type = typeGetter(spec); if (type) { return type; } } throw new UnsupportedOperationError( `Can't classify datasource specification: no compatible classifer available from plugins`, spec, ); } changeDatasourceSpec(type: string): void { const observerContext = this.editorStore.changeDetectionState.observerContext; let dataSpec: DatasourceSpecification | undefined; switch (type) { case CORE_DATASOURCE_SPEC_TYPE.STATIC: { dataSpec = new StaticDatasourceSpecification('', 80, ''); break; } case CORE_DATASOURCE_SPEC_TYPE.H2_LOCAL: { dataSpec = new LocalH2DatasourceSpecification(); break; } case CORE_DATASOURCE_SPEC_TYPE.H2_EMBEDDED: { dataSpec = new EmbeddedH2DatasourceSpecification('', '', false); break; } case CORE_DATASOURCE_SPEC_TYPE.DATABRICKS: { dataSpec = new DatabricksDatasourceSpecification('', '', '', ''); break; } case CORE_DATASOURCE_SPEC_TYPE.SNOWFLAKE: { dataSpec = new SnowflakeDatasourceSpecification('', '', '', ''); break; } case CORE_DATASOURCE_SPEC_TYPE.REDSHIFT: { dataSpec = new RedshiftDatasourceSpecification( '', '', 5439, '', '', '', ); break; } case CORE_DATASOURCE_SPEC_TYPE.BIGQUERY: { dataSpec = new BigQueryDatasourceSpecification('', ''); break; } default: { const extraDatasourceSpecificationCreators = this.editorStore.pluginManager .getApplicationPlugins() .flatMap( (plugin) => ( plugin as StoreRelational_LegendStudioApplicationPlugin_Extension ).getExtraDatasourceSpecificationCreators?.() ?? [], ); for (const creator of extraDatasourceSpecificationCreators) { const spec = creator(type); if (spec) { dataSpec = spec; break; } } } } if (!dataSpec) { throw new UnsupportedOperationError( `Can't create datasource specification of type '${type}': no compatible creator available from plugins`, ); } relationDbConnection_setDatasourceSpecification( this.connection, dataSpec, observerContext, ); } get selectedAuth(): string { const auth = this.connection.authenticationStrategy; if (auth instanceof DelegatedKerberosAuthenticationStrategy) { return CORE_AUTHENTICATION_STRATEGY_TYPE.DELEGATED_KERBEROS; } else if (auth instanceof DefaultH2AuthenticationStrategy) { return CORE_AUTHENTICATION_STRATEGY_TYPE.H2_DEFAULT; } else if (auth instanceof OAuthAuthenticationStrategy) { return CORE_AUTHENTICATION_STRATEGY_TYPE.OAUTH; } else if (auth instanceof ApiTokenAuthenticationStrategy) { return CORE_AUTHENTICATION_STRATEGY_TYPE.API_TOKEN; } else if (auth instanceof SnowflakePublicAuthenticationStrategy) { return CORE_AUTHENTICATION_STRATEGY_TYPE.SNOWFLAKE_PUBLIC; } else if (auth instanceof UsernamePasswordAuthenticationStrategy) { return CORE_AUTHENTICATION_STRATEGY_TYPE.USERNAME_PASSWORD; } else if ( auth instanceof GCPApplicationDefaultCredentialsAuthenticationStrategy ) { return CORE_AUTHENTICATION_STRATEGY_TYPE.GCP_APPLICATION_DEFAULT_CREDENTIALS; } else if ( auth instanceof GCPWorkloadIdentityFederationAuthenticationStrategy ) { return CORE_AUTHENTICATION_STRATEGY_TYPE.GCP_WORKLOAD_IDENTITY_FEDERATION; } const extraAuthenticationStrategyTypeGetters = this.editorStore.pluginManager .getApplicationPlugins() .flatMap( (plugin) => ( plugin as StoreRelational_LegendStudioApplicationPlugin_Extension ).getExtraAuthenticationStrategyTypeGetters?.() ?? [], ); for (const typeGetter of extraAuthenticationStrategyTypeGetters) { const type = typeGetter(auth); if (type) { return type; } } throw new UnsupportedOperationError( `Can't classify authentication strategy: no compatible classifier available from plugins`, auth, ); } changeAuthenticationStrategy(type: string): void { const observerContext = this.editorStore.changeDetectionState.observerContext; let authStrategy: AuthenticationStrategy | undefined; switch (type) { case CORE_AUTHENTICATION_STRATEGY_TYPE.DELEGATED_KERBEROS: { authStrategy = new DelegatedKerberosAuthenticationStrategy(); break; } case CORE_AUTHENTICATION_STRATEGY_TYPE.API_TOKEN: { authStrategy = new ApiTokenAuthenticationStrategy(''); break; } case CORE_AUTHENTICATION_STRATEGY_TYPE.SNOWFLAKE_PUBLIC: { authStrategy = new SnowflakePublicAuthenticationStrategy('', '', ''); break; } case CORE_AUTHENTICATION_STRATEGY_TYPE.GCP_APPLICATION_DEFAULT_CREDENTIALS: { authStrategy = new GCPApplicationDefaultCredentialsAuthenticationStrategy(); break; } case CORE_AUTHENTICATION_STRATEGY_TYPE.GCP_WORKLOAD_IDENTITY_FEDERATION: { authStrategy = new GCPWorkloadIdentityFederationAuthenticationStrategy( '', [], ); break; } case CORE_AUTHENTICATION_STRATEGY_TYPE.H2_DEFAULT: { authStrategy = new DefaultH2AuthenticationStrategy(); break; } case CORE_AUTHENTICATION_STRATEGY_TYPE.USERNAME_PASSWORD: { authStrategy = new UsernamePasswordAuthenticationStrategy('', ''); break; } case CORE_AUTHENTICATION_STRATEGY_TYPE.OAUTH: { authStrategy = new OAuthAuthenticationStrategy('', ''); break; } default: { const extraAuthenticationStrategyCreators = this.editorStore.pluginManager .getApplicationPlugins() .flatMap( (plugin) => ( plugin as StoreRelational_LegendStudioApplicationPlugin_Extension ).getExtraAuthenticationStrategyCreators?.() ?? [], ); for (const creator of extraAuthenticationStrategyCreators) { const auth = creator(type); if (auth) { authStrategy = auth; break; } } } } if (!authStrategy) { throw new UnsupportedOperationError( `Can't create authentication strategy of type '${type}': no compatible creator available from plugins`, ); } relationDbConnection_setNewAuthenticationStrategy( this.connection, authStrategy, observerContext, ); } } export class JsonModelConnectionValueState extends ConnectionValueState { override connection: JsonModelConnection; constructor(editorStore: EditorStore, connection: JsonModelConnection) { super(editorStore, connection); this.connection = connection; } label(): string { return 'Pure model connection'; } } export class FlatDataConnectionValueState extends ConnectionValueState { override connection: FlatDataConnection; constructor(editorStore: EditorStore, connection: FlatDataConnection) { super(editorStore, connection); this.connection = connection; } label(): string { return 'flat-data connection'; } } export class UnsupportedConnectionValueState extends ConnectionValueState { label(): string { return 'unsupported connection'; } } export class ConnectionEditorState { /** * NOTE: used to force component remount on state change */ readonly uuid = uuid(); editorStore: EditorStore; connection: Connection; connectionValueState: ConnectionValueState; constructor(editorStore: EditorStore, connection: Connection) { this.editorStore = editorStore; this.connection = connection; this.connectionValueState = this.buildConnectionValueEditorState(); } buildConnectionValueEditorState(): ConnectionValueState { const connection = this.connection; if (connection instanceof JsonModelConnection) { return new JsonModelConnectionValueState(this.editorStore, connection); } else if (connection instanceof FlatDataConnection) { return new FlatDataConnectionValueState(this.editorStore, connection); } else if (connection instanceof RelationalDatabaseConnection) { return new RelationalDatabaseConnectionValueState( this.editorStore, connection, ); } else { const extraConnectionValueEditorStateBuilders = this.editorStore.pluginManager .getApplicationPlugins() .flatMap( (plugin) => ( plugin as DSLMapping_LegendStudioApplicationPlugin_Extension ).getExtraConnectionValueEditorStateBuilders?.() ?? [], ); for (const stateBuilder of extraConnectionValueEditorStateBuilders) { const state = stateBuilder(this.editorStore, connection); if (state) { return state; } } return new UnsupportedConnectionValueState(this.editorStore, connection); } } } export class PackageableConnectionEditorState extends ElementEditorState { connectionState: ConnectionEditorState; constructor(editorStore: EditorStore, element: PackageableElement) { super(editorStore, element); makeObservable(this, { connection: computed, reprocess: action, }); this.connectionState = new ConnectionEditorState( editorStore, this.connection.connectionValue, ); } get connection(): PackageableConnection { return guaranteeType( this.element, PackageableConnection, `Element inside connection editor state must be a packageable connection`, ); } reprocess( newElement: PackageableElement, editorStore: EditorStore, ): ElementEditorState { const editorState = new PackageableConnectionEditorState( editorStore, newElement, ); return editorState; } }