create collection - generic operation

This commit is contained in:
Jan Prochazka
2024-08-16 12:40:44 +02:00
parent ecde2da2af
commit a89cb607b4
12 changed files with 174 additions and 18 deletions

View File

@@ -182,6 +182,15 @@ module.exports = {
return res;
},
runOperation_meta: true,
async runOperation({ conid, database, operation, useTransaction }, req) {
testConnectionPermission(conid, req);
logger.info({ conid, database, operation }, 'Processing operation');
const opened = await this.ensureOpened(conid, database);
const res = await this.sendRequest(opened, { msgtype: 'runOperation', operation, useTransaction });
return res;
},
collectionData_meta: true,
async collectionData({ conid, database, options }, req) {
testConnectionPermission(conid, req);

View File

@@ -170,6 +170,18 @@ async function handleRunScript({ msgid, sql, useTransaction }, skipReadonlyCheck
}
}
async function handleRunOperation({ msgid, operation, useTransaction }, skipReadonlyCheck = false) {
await waitConnected();
const driver = requireEngineDriver(storedConnection);
try {
if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
await driver.operation(systemConnection, operation, { useTransaction });
process.send({ msgtype: 'response', msgid });
} catch (err) {
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
}
}
async function handleQueryData({ msgid, sql }, skipReadonlyCheck = false) {
await waitConnected();
const driver = requireEngineDriver(storedConnection);
@@ -311,6 +323,7 @@ const messageHandlers = {
connect: handleConnect,
queryData: handleQueryData,
runScript: handleRunScript,
runOperation: handleRunOperation,
updateCollection: handleUpdateCollection,
collectionData: handleCollectionData,
loadKeys: handleLoadKeys,

View File

@@ -81,6 +81,9 @@ export const driverBase = {
runCommandOnDriver(pool, this, dmp => dmp.commitTransaction());
}
},
async operation(pool, operation, options: RunScriptOptions) {
throw new Error('Operation not defined in target driver');
},
getNewObjectTemplates() {
if (this.databaseEngineTypes.includes('sql')) {
return [{ label: 'New view', sql: 'CREATE VIEW myview\nAS\nSELECT * FROM table1' }];

View File

@@ -142,6 +142,7 @@ export interface EngineDriver {
dropDatabase(pool: any, name: string): Promise;
getQuerySplitterOptions(usage: 'stream' | 'script' | 'editor'): any;
script(pool: any, sql: string, options?: RunScriptOptions): Promise;
operation(pool: any, operation: {}, options?: RunScriptOptions): Promise;
getNewObjectTemplates(): NewObjectTemplate[];
// direct call of pool method, only some methods could be supported, on only some drivers
callMethod(pool, method, args);

View File

@@ -1,5 +1,5 @@
<script lang="ts" context="module">
import {copyTextToClipboard} from "../utility/clipboard";
import { copyTextToClipboard } from '../utility/clipboard';
export const extractKey = props => props.name;
@@ -93,10 +93,18 @@
const handleNewCollection = () => {
showModal(InputTextModal, {
value: '',
label: 'New collection name',
header: 'Create collection',
label: 'New collection/container name',
header: 'Create collection/container',
onConfirm: async newCollection => {
saveScriptToDatabase({ conid: connection._id, database: name }, `db.createCollection('${newCollection}')`);
runOperationOnDatabase(
{ conid: connection._id, database: name },
{
type: 'createCollection',
collection: newCollection,
}
);
// saveScriptToDatabase({ conid: connection._id, database: name }, `db.createCollection('${newCollection}')`);
},
});
};
@@ -173,7 +181,7 @@
const handleCopyName = async () => {
copyTextToClipboard(name);
}
};
const handleDisconnect = () => {
disconnectDatabaseConnection(connection._id, name);
@@ -286,7 +294,7 @@
return [
{ onClick: handleNewQuery, text: 'New query', isNewQuery: true },
driver?.databaseEngineTypes?.includes('sql') && { onClick: handleNewTable, text: 'New table' },
driver?.databaseEngineTypes?.includes('document') && { onClick: handleNewCollection, text: 'New collection' },
driver?.databaseEngineTypes?.includes('document') && { onClick: handleNewCollection, text: 'New collection/container' },
driver?.databaseEngineTypes?.includes('sql') && { onClick: handleQueryDesigner, text: 'Design query' },
driver?.databaseEngineTypes?.includes('sql') && {
onClick: handleNewPerspective,
@@ -370,7 +378,7 @@
import { openJsonDocument } from '../tabs/JsonTab.svelte';
import { apiCall } from '../utility/api';
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
import ConfirmSqlModal, { saveScriptToDatabase } from '../modals/ConfirmSqlModal.svelte';
import ConfirmSqlModal, { runOperationOnDatabase, saveScriptToDatabase } from '../modals/ConfirmSqlModal.svelte';
import { filterAppsForDatabase } from '../utility/appTools';
import newQuery from '../query/newQuery';
import { exportSqlDump } from '../utility/exportFileTools';

View File

@@ -522,8 +522,8 @@
copyTextToClipboard(data.pureName);
} else if (menu.isRenameCollection) {
showModal(InputTextModal, {
label: 'New collection name',
header: 'Rename collection',
label: 'New collection/container name',
header: 'Rename collection/container',
value: data.pureName,
onConfirm: async newName => {
const dbid = _.pick(data, ['conid', 'database']);

View File

@@ -39,6 +39,7 @@ import { isMac } from '../utility/common';
import { doLogout, internalRedirectTo } from '../clientAuth';
import { disconnectServerConnection } from '../appobj/ConnectionAppObject.svelte';
import UploadErrorModal from '../modals/UploadErrorModal.svelte';
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
// function themeCommand(theme: ThemeDefinition) {
// return {
@@ -277,7 +278,7 @@ registerCommand({
icon: 'icon table',
name: 'Collection',
toolbar: true,
toolbarName: 'New collection',
toolbarName: 'New collection/container',
testEnabled: () => {
const driver = findEngineDriver(get(currentDatabase)?.connection, getExtensions());
return !!get(currentDatabase) && driver?.databaseEngineTypes?.includes('document');
@@ -291,11 +292,25 @@ registerCommand({
showModal(InputTextModal, {
value: '',
label: 'New collection name',
header: 'Create collection',
label: 'New collection/container name',
header: 'Create collection/container',
onConfirm: async newCollection => {
await apiCall('database-connections/run-script', { ...dbid, sql: `db.createCollection('${newCollection}')` });
apiCall('database-connections/sync-model', dbid);
// await apiCall('database-connections/run-script', { ...dbid, sql: `db.createCollection('${newCollection}')` });
const resp = await apiCall('database-connections/run-operation', {
...dbid,
operation: {
type: 'createCollection',
collection: newCollection,
},
});
const { errorMessage } = resp || {};
if (errorMessage) {
showModal(ErrorMessageModal, { title: 'Error when executing operation', message: errorMessage });
} else {
showSnackbarSuccess('Saved to database');
apiCall('database-connections/sync-model', dbid);
}
},
});
},

View File

@@ -14,6 +14,22 @@
if (syncModel) apiCall('database-connections/sync-model', { conid, database });
}
}
export async function runOperationOnDatabase({ conid, database }, operation, syncModel = true) {
const resp = await apiCall('database-connections/run-operation', {
conid,
database,
operation,
});
const { errorMessage } = resp || {};
if (errorMessage) {
showModal(ErrorMessageModal, { title: 'Error when executing operation', message: errorMessage });
} else {
showSnackbarSuccess('Saved to database');
if (syncModel) apiCall('database-connections/sync-model', { conid, database });
}
}
</script>
<script>

View File

@@ -99,6 +99,14 @@
{/key}
{/if}
{#if driver?.showConnectionField('endpoint', $values, showConnectionFieldArgs)}
<FormTextField label="Endpoint" name="endpoint" disabled={isConnected} />
{/if}
{#if driver?.showConnectionField('endpointKey', $values, showConnectionFieldArgs)}
<FormTextField label="Key" name="endpointKey" disabled={isConnected} />
{/if}
{#if driver?.showConnectionField('clientLibraryPath', $values, showConnectionFieldArgs)}
<FormTextField label="Client library path" name="clientLibraryPath" disabled={isConnected} />
{/if}

View File

@@ -120,7 +120,7 @@
{/if}
{#if driver?.databaseEngineTypes?.includes('document')}
<div class="m-1" />
<InlineButton on:click={() => runCommand('new.collection')}>New collection</InlineButton>
<InlineButton on:click={() => runCommand('new.collection')}>New collection/container</InlineButton>
{/if}
</WidgetsInnerContainer>
{:else}