Browse Source

feat(gui-v2): test connection, project create integration

- show dialog after test connection
- use certificate values

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/2730/head
Pranav C 2 years ago
parent
commit
f93ca9df18
  1. 42
      packages/nc-gui-v2/components/monaco/index.vue
  2. 31
      packages/nc-gui-v2/lib/types.ts
  3. 172
      packages/nc-gui-v2/pages/projects/index/create-external.vue
  4. 13
      packages/nc-gui-v2/utils/deepCompare.ts
  5. 8
      packages/nc-gui-v2/utils/projectCreateUtils.ts

42
packages/nc-gui-v2/components/monaco/index.vue

@ -6,31 +6,43 @@ const { modelValue } = defineProps<{ modelValue: any }>()
const emit = defineEmits(['update:modelValue'])
const root = ref<HTMLDivElement>()
const editor = ref<monaco.editor.IStandaloneCodeEditor>()
let editor = $ref<monaco.editor.IStandaloneCodeEditor>()
onMounted(() => {
if (root.value) {
editor.value = monaco.editor.create(root?.value, {
value: JSON.stringify(modelValue, null, 2),
language: 'json',
const model = monaco.editor.createModel(JSON.stringify(modelValue, null, 2), 'json')
// configure the JSON language support with schemas and schema associations
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
})
editor = monaco.editor.create(root.value, {
model,
theme: 'dark',
})
editor.value.onDidChangeModelContent((e) => {
editor.onDidChangeModelContent(async (e) => {
try {
emit('update:modelValue', JSON.parse(editor?.value?.getValue() as string))
} catch {}
// console.log(e)
// const obj = JSON.parse(editor.value.getValue())
// if (!deepCompare(modelValue, obj)) emit('update:modelValue', obj)
} catch (e) {
console.log(e)
}
})
}
})
watch(
() => modelValue && JSON.stringify(modelValue, null, 2),
(v) => {
if (editor?.value && v) {
editor.value.setValue(v)
}
},
)
// watch(
// () => modelValue,
// (v) => {
// if (editor?.value && v && !deepCompare(v, JSON.parse(editor?.value?.getValue() as string))) {
// debugger
// editor.value.setValue(JSON.stringify(v, null, 2))
// }
// },
// )
</script>
<template>

31
packages/nc-gui-v2/lib/types.ts

@ -32,3 +32,34 @@ export interface Actions {
export type ReadonlyState = Readonly<Pick<State, 'token' | 'user'>> & Omit<State, 'token' | 'user'>
export type GlobalState = Getters & Actions & ToRefs<ReadonlyState>
export type ClientType = 'mysql2' | 'mssql' | 'pg' | 'sqlite3' | 'vitess'
export interface ProjectCreateForm {
title: string
dataSource: {
client: ClientType
connection:
| {
host: string
database: string
user: string
password: string
port: number | string
ssl?: Record<string, string>
searchPath?: string[]
}
| {
client?: 'sqlite3'
database: string
connection?: {
filename?: string
}
useNullAsDefault?: boolean
}
}
inflection: {
inflection_column?: string
inflection_table?: string
}
}

172
packages/nc-gui-v2/pages/projects/index/create-external.vue

@ -2,8 +2,9 @@
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useToast } from 'vue-toastification'
import { Form } from 'ant-design-vue'
import { Form, Modal } from 'ant-design-vue'
import { navigateTo, useNuxtApp } from '#app'
import type { ProjectCreateForm } from '~/lib/types'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
import {
clientTypes,
@ -13,84 +14,28 @@ import {
projectTitleValidator,
sslUsage,
} from '~/utils/projectCreateUtils'
import MdiCheckIcon from '~icons/mdi/check-circle'
import { readFile } from '~/utils/fileUtils'
const onVal = (v) => {
console.log(v)
}
const useForm = Form.useForm
const name = ref('')
const loading = ref(false)
const valid = ref(false)
const testSuccess = ref(true)
const projectDatasource = ref()
const inflection = reactive({
tableName: 'camelize',
columnName: 'camelize',
})
const { $api, $e } = useNuxtApp()
const toast = useToast()
const { t } = useI18n()
const createProject = async () => {
loading.value = true
try {
const result = await $api.project.create({
title: name.value,
bases: [
{
type: projectDatasource.value.client,
config: projectDatasource.value,
inflection_column: inflection.columnName,
inflection_table: inflection.tableName,
},
],
external: true,
})
await navigateTo(`/nc/${result.id}`)
} catch (e: any) {
// todo: toast
toast.error(await extractSdkResponseErrorMsg(e))
}
loading.value = false
}
const testConnection = async () => {
$e('a:project:create:extdb:test-connection', [])
try {
// this.handleSSL(projectDatasource)
if (projectDatasource.value.client === 'sqlite3') {
testSuccess.value = true
} else {
const testConnectionConfig = {
...projectDatasource,
connection: {
...projectDatasource.value.connection,
database: getTestDatabaseName(projectDatasource.value),
},
}
const result = await $api.utils.testConnection(testConnectionConfig)
if (result.code === 0) {
testSuccess.value = true
} else {
testSuccess.value = false
toast.error(`${t('msg.error.dbConnectionFailed')} ${result.message}`)
}
}
} catch (e: any) {
testSuccess.value = false
toast.error(await extractSdkResponseErrorMsg(e))
}
}
const formState = reactive<Record<string, any>>({
const formState = reactive<ProjectCreateForm>({
title: '',
dataSource: { ...getDefaultConnectionConfig('mysql2') },
inflection: {
tableName: 'camelize',
columnName: 'camelize',
inflection_column: 'camelize',
inflection_table: 'camelize',
},
})
@ -130,9 +75,88 @@ const certFileInput = ref<HTMLInputElement>()
const onFileSelect = (key: 'ca' | 'cert' | 'key', el: HTMLInputElement) => {
readFile(el, (content) => {
formState.dataSource.connection.ssl[key] = content ?? ''
if ('ssl' in formState.dataSource.connection && formState.dataSource.connection.ssl)
formState.dataSource.connection.ssl[key] = content ?? ''
})
}
function getConnectionConfig() {
const connection = {
...formState.dataSource.connection,
}
if ('ssl' in connection && connection.ssl && Object.values(connection.ssl).every((v) => !v)) {
delete connection.ssl
}
return connection
}
const createProject = async () => {
loading.value = true
try {
const connection = getConnectionConfig()
const config = { ...formState.dataSource, connection }
const result = await $api.project.create({
title: formState.title,
bases: [
{
type: formState.dataSource.client,
config,
inflection_column: formState.inflection.inflection_column,
inflection_table: formState.inflection.inflection_table,
},
],
external: true,
})
await navigateTo(`/nc/${result.id}`)
} catch (e: any) {
// todo: toast
toast.error(await extractSdkResponseErrorMsg(e))
}
loading.value = false
}
const testConnection = async () => {
$e('a:project:create:extdb:test-connection', [])
try {
// this.handleSSL(projectDatasource)
if (formState.dataSource.client === 'sqlite3') {
testSuccess.value = true
} else {
const connection: any = getConnectionConfig()
connection.database = getTestDatabaseName(formState.dataSource)
const testConnectionConfig = {
...formState.dataSource,
connection,
}
const result = await $api.utils.testConnection(testConnectionConfig)
if (result.code === 0) {
testSuccess.value = true
Modal.confirm({
title: t('msg.info.dbConnected'),
icon: null,
type: 'success',
okText: t('activity.OkSaveProject'),
okType: 'primary',
cancelText: 'Cancel',
onOk: createProject,
})
} else {
testSuccess.value = false
toast.error(`${t('msg.error.dbConnectionFailed')} ${result.message}`)
}
}
} catch (e: any) {
testSuccess.value = false
toast.error(await extractSdkResponseErrorMsg(e))
}
}
</script>
<template>
@ -238,13 +262,13 @@ const onFileSelect = (key: 'ca' | 'cert' | 'key', el: HTMLInputElement) => {
<input ref="certFileInput" type="file" class="!hidden" @change="onFileSelect('cert', certFileInput)" />
<input ref="keyFileInput" type="file" class="!hidden" @change="onFileSelect('key', keyFileInput)" />
<a-form-item :label="$t('labels.inflection.tableName')" v-bind="validateInfos['dataSource.client']">
<a-select v-model:value="formState.inflection.tableName" size="small" @change="onClientChange">
<a-form-item :label="$t('labels.inflection.inflection_column')" v-bind="validateInfos['dataSource.client']">
<a-select v-model:value="formState.inflection.inflection_table" size="small" @change="onClientChange">
<a-select-option v-for="type in inflectionTypes" :key="type" :value="type">{{ type }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item :label="$t('labels.inflection.columnName')" v-bind="validateInfos['dataSource.type']">
<a-select v-model:value="formState.inflection.columnName" size="small" @change="onClientChange">
<a-form-item :label="$t('labels.inflection.inflection_column')" v-bind="validateInfos['dataSource.type']">
<a-select v-model:value="formState.inflection.inflection_column" size="small" @change="onClientChange">
<a-select-option v-for="type in inflectionTypes" :key="type" :value="type">{{ type }}</a-select-option>
</a-select>
</a-form-item>
@ -260,17 +284,15 @@ const onFileSelect = (key: 'ca' | 'cert' | 'key', el: HTMLInputElement) => {
<a-form-item class="flex justify-center">
<div class="flex justify-center gap-2">
<a-button type="primary" html-type="submit">Submit</a-button>
<a-button type="primary" html-type="submit">Test Connection</a-button>
<a-button type="primary" @click="createProject">Submit</a-button>
<a-button type="primary" @click="testConnection">Test Connection</a-button>
</div>
</a-form-item>
</a-form>
<v-dialog v-model="configEditDlg">
<Monaco v-if="configEditDlg" v-model="formState" class="h-[320px] w-[600px]"></Monaco>
<Monaco v-if="configEditDlg" :model-value="formState" class="h-[320px] w-[600px]" @update:modelValue="onVal"></Monaco>
</v-dialog>
{{ formState }}
</a-card>
</template>

13
packages/nc-gui-v2/utils/deepCompare.ts

@ -0,0 +1,13 @@
export const deepCompare = (a: any, b: any) => {
if (a === b) return true
if (a == null || b === null) return false
if (typeof a !== typeof b) return false
if (typeof a !== 'object') return a === b
if (Object.keys(a).length !== Object.keys(b).length) return false
for (const k in a) {
if (!(k in b)) return false
if (!deepCompare(a[k], b[k])) return false
}
return true
}

8
packages/nc-gui-v2/utils/projectCreateUtils.ts

@ -1,3 +1,5 @@
import type { ClientType, ProjectCreateForm } from '~/lib/types'
const testDataBaseNames = {
mysql2: null,
mysql: null,
@ -7,8 +9,6 @@ const testDataBaseNames = {
sqlite3: 'a.sqlite',
}
export type ClientType = 'mysql2' | 'mssql' | 'pg' | 'sqlite3' | 'vitess'
export const getTestDatabaseName = (db: { client: ClientType; connection?: { database?: string } }) => {
if (db.client === 'pg') return db.connection?.database
return testDataBaseNames[db.client as keyof typeof testDataBaseNames]
@ -34,7 +34,7 @@ export const clientTypes = [
]
const homeDir = ''
const sampleConnectionData = {
const sampleConnectionData: Record<ClientType | string, ProjectCreateForm['dataSource']['connection']> = {
pg: {
host: 'localhost',
port: '5432',
@ -167,7 +167,7 @@ const sampleConnectionData = {
},
}
export const getDefaultConnectionConfig = (client: ClientType): { client: ClientType; connection: any } => {
export const getDefaultConnectionConfig = (client: ClientType): ProjectCreateForm['dataSource'] => {
return {
client,
connection: sampleConnectionData[client],

Loading…
Cancel
Save