mong support WIP

This commit is contained in:
Jan Prochazka 2021-04-02 18:40:07 +02:00
parent d4bd6e03c9
commit 394c6028c9
19 changed files with 347 additions and 10 deletions

View File

@ -92,6 +92,13 @@ module.exports = {
return res;
},
collectionData_meta: 'post',
async collectionData({ conid, database, options }) {
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'collectionData', options });
return res;
},
status_meta: 'get',
async status({ conid, database }) {
const existing = this.opened.find(x => x.conid == conid && x.database == database);

View File

@ -17,7 +17,7 @@ module.exports = {
listObjects_meta: 'get',
async listObjects({ conid, database }) {
const opened = await databaseConnections.ensureOpened(conid, database);
const types = ['tables', 'views', 'procedures', 'functions', 'triggers'];
const types = ['tables', 'collections', 'views', 'procedures', 'functions', 'triggers'];
return types.reduce(
(res, type) => ({
...res,

View File

@ -94,6 +94,17 @@ async function handleQueryData({ msgid, sql }) {
}
}
async function handleCollectionData({ msgid, options }) {
await waitConnected();
const driver = requireEngineDriver(storedConnection);
try {
const result = await driver.readCollection(systemConnection, options);
process.send({ msgtype: 'response', msgid, result });
} catch (err) {
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
}
}
async function handleSqlPreview({ msgid, objects, options }) {
await waitConnected();
const driver = requireEngineDriver(storedConnection);
@ -129,6 +140,7 @@ function handlePing() {
const messageHandlers = {
connect: handleConnect,
queryData: handleQueryData,
collectionData: handleCollectionData,
sqlPreview: handleSqlPreview,
ping: handlePing,
// runCommand: handleRunCommand,

View File

@ -0,0 +1,46 @@
import _ from 'lodash';
import { GridDisplay, ChangeCacheFunc, ChangeConfigFunc } from './GridDisplay';
import { EngineDriver, ViewInfo, ColumnInfo, CollectionInfo } from 'dbgate-types';
import { GridConfig, GridCache } from './GridConfig';
export class CollectionGridDisplay extends GridDisplay {
constructor(
public collection: CollectionInfo,
driver: EngineDriver,
config: GridConfig,
setConfig: ChangeConfigFunc,
cache: GridCache,
setCache: ChangeCacheFunc
) {
super(config, setConfig, cache, setCache, driver);
this.columns = [];
this.filterable = true;
this.sortable = true;
this.editable = false;
this.supportsReload = true;
}
// getDisplayColumns(view: ViewInfo) {
// return (
// view?.columns
// ?.map(col => this.getDisplayColumn(view, col))
// ?.map(col => ({
// ...col,
// isChecked: this.isColumnChecked(col),
// })) || []
// );
// }
// getDisplayColumn(view: ViewInfo, col: ColumnInfo) {
// const uniquePath = [col.columnName];
// const uniqueName = uniquePath.join('.');
// return {
// ...col,
// pureName: view.pureName,
// schemaName: view.schemaName,
// headerText: col.columnName,
// uniqueName,
// uniquePath,
// };
// }
}

View File

@ -49,7 +49,7 @@ export class DatabaseAnalyser {
}
const res = {};
for (const field of ['tables', 'views', 'functions', 'procedures', 'triggers']) {
for (const field of ['tables', 'collections', 'views', 'functions', 'procedures', 'triggers']) {
const removedIds = this.modifications
.filter(x => x.action == 'remove' && x.objectTypeField == field)
.map(x => extractObjectId(x));
@ -78,6 +78,7 @@ export class DatabaseAnalyser {
static createEmptyStructure(): DatabaseInfo {
return {
tables: [],
collections: [],
views: [],
functions: [],
procedures: [],

View File

@ -56,6 +56,10 @@ function fillTableExtendedInfo(db: DatabaseInfo): DatabaseInfo {
constraintType: 'unique',
})),
})),
collections: (db.collections || []).map(obj => ({
...obj,
objectTypeField: 'collections',
})),
views: (db.views || []).map(obj => ({
...obj,
objectTypeField: 'views',

View File

@ -74,6 +74,8 @@ export interface TableInfo extends DatabaseObjectInfo {
checks?: CheckInfo[];
}
export interface CollectionInfo extends DatabaseObjectInfo {}
export interface ViewInfo extends SqlObjectInfo {
columns: ColumnInfo[];
}
@ -91,6 +93,7 @@ export interface SchemaInfo {
export interface DatabaseInfoObjects {
tables: TableInfo[];
collections: CollectionInfo[];
views: ViewInfo[];
procedures: ProcedureInfo[];
functions: FunctionInfo[];

View File

@ -8,4 +8,5 @@ export interface SqlDialect {
explicitDropConstraint?: boolean;
anonymousPrimaryKey?: boolean;
enableConstraintsPerTable?: boolean;
nosql?: boolean; // mongo
}

View File

@ -24,6 +24,14 @@ export interface EngineAuthType {
disabledFields: string[];
}
export interface ReadCollectionOptions {
pureName: string;
schemaName?: string;
countDocuments?: boolean;
skip?: number;
limit?: number;
}
export interface EngineDriver {
engine: string;
title: string;
@ -52,6 +60,7 @@ export interface EngineDriver {
dialect: SqlDialect;
createDumper(): SqlDumper;
getAuthTypes(): EngineAuthType[];
readCollection(pool: any, options: ReadCollectionOptions): Promise<any>;
analyserClass?: any;
dumperClass?: any;

View File

@ -4,6 +4,7 @@
const icons = {
tables: 'img table',
collections: 'img collection',
views: 'img view',
procedures: 'img procedure',
functions: 'img function',
@ -11,6 +12,7 @@
const defaultTabs = {
tables: 'TableDataTab',
collections: 'CollectionDataTab',
views: 'ViewDataTab',
};

View File

@ -0,0 +1,189 @@
<script context="module" lang="ts">
async function loadDataPage(props, offset, limit) {
const { conid, database } = props;
const response = await axiosInstance.request({
url: 'database-connections/collection-data',
method: 'post',
params: {
conid,
database,
},
data: {
options: {
limit,
skip: offset,
},
},
});
if (response.data.errorMessage) return response.data;
return response.data.rows;
}
function dataPageAvailable(props) {
const { display } = props;
const sql = display.getPageQuery(0, 1);
return !!sql;
}
async function loadRowCount(props) {
const { conid, database } = props;
const response = await axiosInstance.request({
url: 'database-connections/collection-data',
method: 'post',
params: {
conid,
database,
},
data: {
options: {
countDocuments: true,
},
},
});
return response.data.count;
}
</script>
<script lang="ts">
import { changeSetToSql, createChangeSet } from 'dbgate-datalib';
import { scriptToSql } from 'dbgate-sqltree';
import ConfirmSqlModal from '../modals/ConfirmSqlModal.svelte';
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
import ImportExportModal from '../modals/ImportExportModal.svelte';
import { showModal } from '../modals/modalTools';
import axiosInstance from '../utility/axiosInstance';
import openNewTab from '../utility/openNewTab';
import ChangeSetGrider from './ChangeSetGrider';
import LoadingDataGridCore from './LoadingDataGridCore.svelte';
export let conid;
export let display;
export let database;
export let schemaName;
export let pureName;
export let config;
export let changeSetState;
export let dispatchChangeSet;
export let macroPreview;
export let macroValues;
export let selectedCellsPublished;
// export let onChangeGrider = undefined;
let loadedRows = [];
// $: console.log('loadedRows BIND', loadedRows);
$: grider = new ChangeSetGrider(
loadedRows,
changeSetState,
dispatchChangeSet,
display,
macroPreview,
macroValues,
selectedCellsPublished
);
// $: console.log('GRIDER', grider);
// $: if (onChangeGrider) onChangeGrider(grider);
async function handleConfirmSql(sql) {
const resp = await axiosInstance.request({
url: 'database-connections/query-data',
method: 'post',
params: {
conid,
database,
},
data: { sql },
});
const { errorMessage } = resp.data || {};
if (errorMessage) {
showModal(ErrorMessageModal, { title: 'Error when saving', message: errorMessage });
} else {
dispatchChangeSet({ type: 'reset', value: createChangeSet() });
display.reload();
}
}
function handleSave() {
const script = changeSetToSql(changeSetState && changeSetState.value, display.dbinfo);
const sql = scriptToSql(display.driver, script);
showModal(ConfirmSqlModal, {
sql,
onConfirm: () => handleConfirmSql(sql),
engine: display.engine,
});
}
function exportGrid() {
const initialValues: any = {};
initialValues.sourceStorageType = 'query';
initialValues.sourceConnectionId = conid;
initialValues.sourceDatabaseName = database;
initialValues.sourceSql = display.getExportQuery();
initialValues.sourceList = display.baseTable ? [display.baseTable.pureName] : [];
showModal(ImportExportModal, { initialValues });
}
function openQuery() {
openNewTab(
{
title: 'Query #',
icon: 'img sql-file',
tabComponent: 'QueryTab',
props: {
schemaName: display.baseTable.schemaName,
pureName: display.baseTable.pureName,
conid,
database,
},
},
{
editor: display.getExportQuery(),
}
);
}
function openActiveChart() {
openNewTab(
{
title: 'Chart #',
icon: 'img chart',
tabComponent: 'ChartTab',
props: {
conid,
database,
},
},
{
editor: {
config: { chartType: 'bar' },
sql: display.getExportQuery(select => {
select.orderBy = null;
}),
},
}
);
}
</script>
<LoadingDataGridCore
{...$$props}
{loadDataPage}
{dataPageAvailable}
{loadRowCount}
onExportGrid={exportGrid}
onOpenQuery={openQuery}
onOpenActiveChart={openActiveChart}
bind:loadedRows
frameSelection={!!macroPreview}
{grider}
onSave={handleSave}
isDynamicStructure
/>

View File

@ -69,7 +69,7 @@
name="references"
height="30%"
collapsed={isDetailView}
skip={!showReferences || !display.hasReferences}
skip={!showReferences || !display?.hasReferences}
>
<ReferenceManager {...$$props} {managerSize} />
</WidgetColumnBarItem>

View File

@ -269,6 +269,7 @@
export let isLoadedAll;
export let loadedTime;
export let changeSetStore;
export let isDynamicStructure = false;
// export let generalAllowSave = false;
const wheelRowCount = 5;
@ -1025,7 +1026,7 @@
}
</script>
{#if !columns || columns.length == 0}
{#if !isDynamicStructure && (!columns || columns.length == 0)}
<LoadingInfo wrapper message="Waiting for structure" />
{:else if errorMessage}
<ErrorInfo message={errorMessage} />

View File

@ -92,6 +92,7 @@
'img database': 'mdi mdi-database color-icon-gold',
'img table': 'mdi mdi-table color-icon-blue',
'img collection': 'mdi mdi-table color-icon-red',
'img view': 'mdi mdi-table color-icon-magenta',
'img procedure': 'mdi mdi-cog color-icon-blue',
'img function': 'mdi mdi-function-variant',

View File

@ -0,0 +1,49 @@
<script lang="ts" context="module">
export const matchingProps = ['conid', 'database', 'schemaName', 'pureName'];
export const allowAddToFavorites = props => true;
</script>
<script lang="ts">
import App from '../App.svelte';
import TableDataGrid from '../datagrid/TableDataGrid.svelte';
import useGridConfig from '../utility/useGridConfig';
import {
createChangeSet,
createGridCache,
createGridConfig,
TableFormViewDisplay,
TableGridDisplay,
} from 'dbgate-datalib';
import { findEngineDriver } from 'dbgate-tools';
import { writable } from 'svelte/store';
import createUndoReducer from '../utility/createUndoReducer';
import invalidateCommands from '../commands/invalidateCommands';
export let tabid;
export let conid;
export let database;
export let schemaName;
export let pureName;
const config = useGridConfig(tabid);
const cache = writable(createGridCache());
const [changeSetStore, dispatchChangeSet] = createUndoReducer(createChangeSet());
$: {
$changeSetStore;
invalidateCommands();
}
</script>
<TableDataGrid
{...$$props}
config={$config}
setConfig={config.update}
cache={$cache}
setCache={cache.update}
changeSetState={$changeSetStore}
focusOnVisible
{changeSetStore}
{dispatchChangeSet}
/>

View File

@ -1,4 +1,5 @@
import * as TableDataTab from './TableDataTab.svelte';
import * as CollectionDataTab from './CollectionDataTab.svelte';
import * as ViewDataTab from './ViewDataTab.svelte';
import * as TableStructureTab from './TableStructureTab.svelte';
import * as QueryTab from './QueryTab.svelte';
@ -15,6 +16,7 @@ import * as QueryDesignTab from './QueryDesignTab.svelte';
export default {
TableDataTab,
CollectionDataTab,
ViewDataTab,
TableStructureTab,
QueryTab,

View File

@ -1,16 +1,24 @@
<script lang="ts">
<script lang="ts">
import { findEngineDriver } from 'dbgate-tools';
import { currentDatabase, extensions } from '../stores';
import { useConnectionInfo } from '../utility/metadataLoaders';
import ConnectionList from './ConnectionList.svelte';
import SqlObjectListWrapper from './SqlObjectListWrapper.svelte';
import SqlObjectListWrapper from './SqlObjectListWrapper.svelte';
import WidgetColumnBar from './WidgetColumnBar.svelte';
import WidgetColumnBarItem from './WidgetColumnBarItem.svelte';
$: conid = $currentDatabase?.connection?._id;
$: connection = useConnectionInfo({ conid });
$: driver = findEngineDriver($connection, $extensions);
</script>
<WidgetColumnBar>
<WidgetColumnBarItem title="Connections" name="connections" height="50%">
<ConnectionList />
</WidgetColumnBarItem>
<WidgetColumnBarItem title="Tables, views, functions" name="dbObjects">
<SqlObjectListWrapper />
<WidgetColumnBarItem title={driver?.dialect?.nosql ? 'Collections' : 'Tables, views, functions'} name="dbObjects">
<SqlObjectListWrapper />
</WidgetColumnBarItem>
</WidgetColumnBar>

View File

@ -21,8 +21,10 @@
$: objects = useDatabaseInfo({ conid, database });
$: status = useDatabaseStatus({ conid, database });
// $: console.log('objects', $objects);
$: objectList = _.flatten(
['tables', 'views', 'procedures', 'functions'].map(objectTypeField =>
['tables', 'collections', 'views', 'procedures', 'functions'].map(objectTypeField =>
_.sortBy(
(($objects || {})[objectTypeField] || []).map(obj => ({ ...obj, objectTypeField })),
['schemaName', 'pureName']

View File

@ -1,7 +1,7 @@
<script lang="ts">
import _ from 'lodash';
import { currentDatabase } from '../stores';
import ErrorInfo from '../elements/ErrorInfo.svelte';
import ErrorInfo from '../elements/ErrorInfo.svelte';
import SqlObjectList from './SqlObjectList.svelte';
import WidgetsInnerContainer from './WidgetsInnerContainer.svelte';