Browse Source

fix(gui): stop event propagation in view rename input

re #4686

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/4696/head
Pranav C 2 years ago
parent
commit
c841c767f0
  1. 7
      packages/nc-gui/components/dashboard/TreeView.vue
  2. 2
      packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue
  3. 1
      packages/nc-gui/composables/useGlobal/types.ts
  4. 82
      packages/nc-gui/pages/index/index/create-external.vue
  5. 16
      packages/nocodb/src/lib/Noco.ts
  6. 19
      packages/nocodb/src/lib/db/sql-client/lib/SqlClientFactory.ts
  7. 19
      packages/nocodb/src/lib/db/sql-client/lib/ee/SqlClientFactoryEE.ts
  8. 3
      packages/nocodb/src/lib/meta/api/utilApis.ts
  9. 1
      packages/nocodb/src/lib/migrations/v2/nc_013_sync_source.ts

7
packages/nc-gui/components/dashboard/TreeView.vue

@ -26,6 +26,7 @@ import {
useToggle, useToggle,
useUIPermission, useUIPermission,
watchEffect, watchEffect,
useGlobal
} from '#imports' } from '#imports'
import MdiView from '~icons/mdi/eye-circle-outline' import MdiView from '~icons/mdi/eye-circle-outline'
import MdiTableLarge from '~icons/mdi/table-large' import MdiTableLarge from '~icons/mdi/table-large'
@ -46,6 +47,8 @@ const route = useRoute()
const [searchActive, toggleSearchActive] = useToggle() const [searchActive, toggleSearchActive] = useToggle()
const { appInfo } = useGlobal()
const toggleDialog = inject(ToggleDialogInj, () => {}) const toggleDialog = inject(ToggleDialogInj, () => {})
const keys = $ref<Record<string, number>>({}) const keys = $ref<Record<string, number>>({})
@ -414,7 +417,7 @@ const setIcon = async (icon: string, table: TableType) => {
MSSQL MSSQL
</div> </div>
</a-menu-item> </a-menu-item>
<a-menu-item key="connect-new-source" @click="toggleDialog(true, 'dataSources', ClientType.SNOWFLAKE)"> <a-menu-item v-if="appInfo.ee" key="connect-new-source" @click="toggleDialog(true, 'dataSources', ClientType.SNOWFLAKE)">
<div class="color-transition nc-project-menu-item group"> <div class="color-transition nc-project-menu-item group">
<LogosSnowflakeIcon class="group-hover:text-accent" /> <LogosSnowflakeIcon class="group-hover:text-accent" />
Snowflake Snowflake
@ -530,7 +533,7 @@ const setIcon = async (icon: string, table: TableType) => {
MSSQL MSSQL
</div> </div>
</a-menu-item> </a-menu-item>
<a-menu-item key="connect-new-source" @click="toggleDialog(true, 'dataSources', ClientType.SNOWFLAKE)"> <a-menu-item v-if="appInfo.ee" key="connect-new-source" @click="toggleDialog(true, 'dataSources', ClientType.SNOWFLAKE)">
<div class="color-transition nc-project-menu-item group"> <div class="color-transition nc-project-menu-item group">
<LogosSnowflakeIcon class="group-hover:text-accent" /> <LogosSnowflakeIcon class="group-hover:text-accent" />
Snowflake Snowflake

2
packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue

@ -179,7 +179,7 @@ function onStopEdit() {
</a-dropdown> </a-dropdown>
</div> </div>
<a-input v-if="isEditing" :ref="focusInput" v-model:value="vModel.title" @blur="onCancel" @keydown="onKeyDown($event)" /> <a-input v-if="isEditing" :ref="focusInput" v-model:value="vModel.title" @blur="onCancel" @keydown.stop="onKeyDown($event)" />
<div v-else> <div v-else>
<LazyGeneralTruncateText>{{ vModel.alias || vModel.title }}</LazyGeneralTruncateText> <LazyGeneralTruncateText>{{ vModel.alias || vModel.title }}</LazyGeneralTruncateText>

1
packages/nc-gui/composables/useGlobal/types.ts

@ -25,6 +25,7 @@ export interface AppInfo {
teleEnabled: boolean teleEnabled: boolean
type: string type: string
version: string version: string
ee?: boolean
} }
export interface StoredState { export interface StoredState {

82
packages/nc-gui/pages/index/index/create-external.vue

@ -7,7 +7,7 @@ import {
Form, Form,
Modal, Modal,
SSLUsage, SSLUsage,
clientTypes, clientTypes as _clientTypes,
computed, computed,
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
fieldRequiredValidator, fieldRequiredValidator,
@ -28,6 +28,8 @@ import {
watch, watch,
} from '#imports' } from '#imports'
const { appInfo } = useGlobal()
const useForm = Form.useForm const useForm = Form.useForm
const testSuccess = ref(false) const testSuccess = ref(false)
@ -64,23 +66,29 @@ const customFormState = ref<ProjectCreateForm>({
extraParameters: [], extraParameters: [],
}) })
const clientTypes = computed(() => {
return _clientTypes.filter((type) => {
return appInfo.value?.ee || type.value !== ClientType.SNOWFLAKE
})
})
const validators = computed(() => { const validators = computed(() => {
return { let clientValidations: Record<string, any[]> = {
'title': [ 'dataSource.connection.host': [fieldRequiredValidator()],
{ 'dataSource.connection.port': [fieldRequiredValidator()],
required: true, 'dataSource.connection.user': [fieldRequiredValidator()],
message: 'Project name is required', 'dataSource.connection.password': [fieldRequiredValidator()],
}, 'dataSource.connection.database': [fieldRequiredValidator()],
projectTitleValidator, }
],
'extraParameters': [extraParameterValidator], switch (formState.dataSource.client) {
'dataSource.client': [fieldRequiredValidator()], case ClientType.SQLITE:
...(formState.dataSource.client === ClientType.SQLITE clientValidations = {
? {
'dataSource.connection.connection.filename': [fieldRequiredValidator()], 'dataSource.connection.connection.filename': [fieldRequiredValidator()],
} }
: formState.dataSource.client === ClientType.SNOWFLAKE break
? { case ClientType.SNOWFLAKE:
clientValidations = {
'dataSource.connection.account': [fieldRequiredValidator()], 'dataSource.connection.account': [fieldRequiredValidator()],
'dataSource.connection.username': [fieldRequiredValidator()], 'dataSource.connection.username': [fieldRequiredValidator()],
'dataSource.connection.password': [fieldRequiredValidator()], 'dataSource.connection.password': [fieldRequiredValidator()],
@ -88,18 +96,24 @@ const validators = computed(() => {
'dataSource.connection.database': [fieldRequiredValidator()], 'dataSource.connection.database': [fieldRequiredValidator()],
'dataSource.connection.schema': [fieldRequiredValidator()], 'dataSource.connection.schema': [fieldRequiredValidator()],
} }
: { break
'dataSource.connection.host': [fieldRequiredValidator()], case ClientType.PG:
'dataSource.connection.port': [fieldRequiredValidator()], case ClientType.MSSQL:
'dataSource.connection.user': [fieldRequiredValidator()], clientValidations['dataSource.searchPath.0'] = [fieldRequiredValidator()]
'dataSource.connection.password': [fieldRequiredValidator()], break
'dataSource.connection.database': [fieldRequiredValidator()],
...([ClientType.PG, ClientType.MSSQL].includes(formState.dataSource.client)
? {
'dataSource.searchPath.0': [fieldRequiredValidator()],
} }
: {}),
}), return {
'title': [
{
required: true,
message: 'Project name is required',
},
projectTitleValidator,
],
'extraParameters': [extraParameterValidator],
'dataSource.client': [fieldRequiredValidator()],
...clientValidations,
} }
}) })
@ -483,8 +497,10 @@ onMounted(async () => {
</div> </div>
</template> </template>
<a-form-item label="SSL mode"> <a-form-item label="SSL mode">
<a-select v-model:value="formState.sslUse" dropdown-class-name="nc-dropdown-ssl-mode" @select="onSSLModeChange"> <a-select v-model:value="formState.sslUse" dropdown-class-name="nc-dropdown-ssl-mode"
<a-select-option v-for="opt in Object.values(SSLUsage)" :key="opt" :value="opt">{{ opt }}</a-select-option> @select="onSSLModeChange">
<a-select-option v-for="opt in Object.values(SSLUsage)" :key="opt" :value="opt">{{ opt }}
</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
@ -526,14 +542,16 @@ onMounted(async () => {
<input ref="caFileInput" type="file" class="!hidden" @change="onFileSelect(CertTypes.ca, caFileInput)" /> <input ref="caFileInput" type="file" class="!hidden" @change="onFileSelect(CertTypes.ca, caFileInput)" />
<input ref="certFileInput" type="file" class="!hidden" @change="onFileSelect(CertTypes.cert, certFileInput)" /> <input ref="certFileInput" type="file" class="!hidden"
@change="onFileSelect(CertTypes.cert, certFileInput)" />
<input ref="keyFileInput" type="file" class="!hidden" @change="onFileSelect(CertTypes.key, keyFileInput)" /> <input ref="keyFileInput" type="file" class="!hidden" @change="onFileSelect(CertTypes.key, keyFileInput)" />
<a-divider /> <a-divider />
<!-- Extra connection parameters --> <!-- Extra connection parameters -->
<a-form-item class="mb-2" :label="$t('labels.extraConnectionParameters')" v-bind="validateInfos.extraParameters"> <a-form-item class="mb-2" :label="$t('labels.extraConnectionParameters')"
v-bind="validateInfos.extraParameters">
<a-card> <a-card>
<div v-for="(item, index) of formState.extraParameters" :key="index"> <div v-for="(item, index) of formState.extraParameters" :key="index">
<div class="flex py-1 items-center gap-1"> <div class="flex py-1 items-center gap-1">
@ -547,7 +565,9 @@ onMounted(async () => {
</div> </div>
</div> </div>
<a-button type="dashed" class="w-full caption mt-2" @click="addNewParam"> <a-button type="dashed" class="w-full caption mt-2" @click="addNewParam">
<div class="flex items-center justify-center"><MdiPlus /></div> <div class="flex items-center justify-center">
<MdiPlus />
</div>
</a-button> </a-button>
</a-card> </a-card>
</a-form-item> </a-form-item>

16
packages/nocodb/src/lib/Noco.ts

@ -16,7 +16,9 @@ import requestIp from 'request-ip';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { NcConfig } from '../interface/config'; import { NcConfig } from '../interface/config';
import { NC_LICENSE_KEY } from './constants';
import Migrator from './db/sql-migrator/lib/KnexMigrator'; import Migrator from './db/sql-migrator/lib/KnexMigrator';
import Store from './models/Store';
import NcConfigFactory from './utils/NcConfigFactory'; import NcConfigFactory from './utils/NcConfigFactory';
import { Tele } from 'nc-help'; import { Tele } from 'nc-help';
@ -53,6 +55,7 @@ const NcProjectBuilder = process.env.EE
export default class Noco { export default class Noco {
private static _this: Noco; private static _this: Noco;
private static ee: boolean;
public static get dashboardUrl(): string { public static get dashboardUrl(): string {
let siteUrl = `http://localhost:${process.env.PORT || 8080}`; let siteUrl = `http://localhost:${process.env.PORT || 8080}`;
@ -192,6 +195,7 @@ export default class Noco {
} }
await Noco._ncMeta.metaInit(); await Noco._ncMeta.metaInit();
await Noco.loadEEState();
await this.initJwt(); await this.initJwt();
await initAdminFromEnv(); await initAdminFromEnv();
@ -542,4 +546,16 @@ export default class Noco {
public static getConfig(): NcConfig { public static getConfig(): NcConfig {
return Noco.config; return Noco.config;
} }
public static isEE(): boolean {
return Noco.ee;
}
public static async loadEEState(): Promise<boolean> {
try {
return (Noco.ee = !!(await Store.get(NC_LICENSE_KEY)));
} catch {
return (Noco.ee = false);
}
}
} }

19
packages/nocodb/src/lib/db/sql-client/lib/SqlClientFactory.ts

@ -1,3 +1,5 @@
import Noco from '../../../Noco';
import SqlClientFactoryEE from './ee/SqlClientFactoryEE';
import MySqlClient from './mysql/MysqlClient'; import MySqlClient from './mysql/MysqlClient';
import MssqlClient from './mssql/MssqlClient'; import MssqlClient from './mssql/MssqlClient';
import OracleClient from './oracle/OracleClient'; import OracleClient from './oracle/OracleClient';
@ -6,10 +8,8 @@ import PgClient from './pg/PgClient';
import YugabyteClient from './pg/YugabyteClient'; import YugabyteClient from './pg/YugabyteClient';
import TidbClient from './mysql/TidbClient'; import TidbClient from './mysql/TidbClient';
import VitessClient from './mysql/VitessClient'; import VitessClient from './mysql/VitessClient';
import SfClient from './snowflake/SnowflakeClient';
import { SnowflakeClient } from 'nc-help';
class SqlClientFactory { export class SqlClientFactory {
static create(connectionConfig) { static create(connectionConfig) {
connectionConfig.meta = connectionConfig.meta || {}; connectionConfig.meta = connectionConfig.meta || {};
connectionConfig.pool = connectionConfig.pool || { min: 0, max: 5 }; connectionConfig.pool = connectionConfig.pool || { min: 0, max: 5 };
@ -33,13 +33,18 @@ class SqlClientFactory {
if (connectionConfig.meta.dbtype === 'yugabyte') if (connectionConfig.meta.dbtype === 'yugabyte')
return new YugabyteClient(connectionConfig); return new YugabyteClient(connectionConfig);
return new PgClient(connectionConfig); return new PgClient(connectionConfig);
} else if (connectionConfig.client === 'snowflake') {
connectionConfig.client = SnowflakeClient;
return new SfClient(connectionConfig);
} }
throw new Error('Database not supported'); throw new Error('Database not supported');
} }
} }
export default SqlClientFactory; export default class {
static create(connectionConfig) {
if (Noco.isEE()) {
return SqlClientFactoryEE.create(connectionConfig);
}
return SqlClientFactory.create(connectionConfig);
}
}

19
packages/nocodb/src/lib/db/sql-client/lib/ee/SqlClientFactoryEE.ts

@ -0,0 +1,19 @@
import { SqlClientFactory } from '../SqlClientFactory';
import SfClient from '../snowflake/SnowflakeClient';
import { SnowflakeClient } from 'nc-help';
class SqlClientFactoryEE {
static create(connectionConfig) {
connectionConfig.meta = connectionConfig.meta || {};
connectionConfig.pool = connectionConfig.pool || { min: 0, max: 5 };
connectionConfig.meta.dbtype = connectionConfig.meta.dbtype || '';
if (connectionConfig.client === 'snowflake') {
connectionConfig.client = SnowflakeClient;
return new SfClient(connectionConfig);
}
return SqlClientFactory.create(connectionConfig);
}
}
export default SqlClientFactoryEE;

3
packages/nocodb/src/lib/meta/api/utilApis.ts

@ -3,7 +3,9 @@ import { Request, Response } from 'express';
import { compareVersions, validate } from 'compare-versions'; import { compareVersions, validate } from 'compare-versions';
import { ViewTypes } from 'nocodb-sdk'; import { ViewTypes } from 'nocodb-sdk';
import { NC_LICENSE_KEY } from '../../constants';
import Project from '../../models/Project'; import Project from '../../models/Project';
import Store from '../../models/Store';
import Noco from '../../Noco'; import Noco from '../../Noco';
import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2'; import NcConnectionMgrv2 from '../../utils/common/NcConnectionMgrv2';
import { MetaTable } from '../../utils/globals'; import { MetaTable } from '../../utils/globals';
@ -55,6 +57,7 @@ export async function appInfo(req: Request, res: Response) {
ncMin: !!process.env.NC_MIN, ncMin: !!process.env.NC_MIN,
teleEnabled: !process.env.NC_DISABLE_TELE, teleEnabled: !process.env.NC_DISABLE_TELE,
ncSiteUrl: (req as any).ncSiteUrl, ncSiteUrl: (req as any).ncSiteUrl,
ee: !!(await Store.get(NC_LICENSE_KEY)),
}; };
res.json(result); res.json(result);

1
packages/nocodb/src/lib/migrations/v2/nc_013_sync_source.ts

@ -38,6 +38,7 @@ const up = async (knex: Knex) => {
}; };
const down = async (knex) => { const down = async (knex) => {
await knex.schema.dropTable(MetaTable.SYNC_LOGS);
await knex.schema.dropTable(MetaTable.SYNC_SOURCE); await knex.schema.dropTable(MetaTable.SYNC_SOURCE);
}; };

Loading…
Cancel
Save