mirror of
https://github.com/garraflavatra/rolens.git
synced 2025-01-18 13:07:58 +00:00
Added collection copy feature (fixes #63)
This commit is contained in:
parent
7e5e2127ff
commit
3ca561b4b4
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
* Added log view (#53, #54).
|
* Added log view (#53, #54).
|
||||||
* Added a shell script editor (#37), plus export/import feature.
|
* Added a shell script editor (#37), plus export/import feature.
|
||||||
|
* Added collection duplication feature (#63).
|
||||||
* Find view: paste ID and press Enter (#55).
|
* Find view: paste ID and press Enter (#55).
|
||||||
* Preserve state after switching to another tab (#56).
|
* Preserve state after switching to another tab (#56).
|
||||||
* Find view: ask for confirmation before negligently deleting documents when the user has clicked the '-' button (#58).
|
* Find view: ask for confirmation before negligently deleting documents when the user has clicked the '-' button (#58).
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
"chev-u": "<path d=\"m18 15-6-6-6 6\"/>",
|
"chev-u": "<path d=\"m18 15-6-6-6 6\"/>",
|
||||||
"chevs-l": "<path d=\"m11 17-5-5 5-5M18 17l-5-5 5-5\"/>",
|
"chevs-l": "<path d=\"m11 17-5-5 5-5M18 17l-5-5 5-5\"/>",
|
||||||
"chevs-r": "<path d=\"m13 17 5-5-5-5M6 17l5-5-5-5\"/>",
|
"chevs-r": "<path d=\"m13 17 5-5-5-5M6 17l5-5-5-5\"/>",
|
||||||
"arr-d": " <path d=\"M12 5v14M19 12l-7 7-7-7\"/>",
|
"arr-d": "<path d=\"M12 5v14M19 12l-7 7-7-7\"/>",
|
||||||
|
"arr-r": "<path d=\"M5 12h14M12 5l7 7-7 7\"/>",
|
||||||
"db": "<ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\"></ellipse><path d=\"M21 12c0 1.66-4 3-9 3s-9-1.34-9-3\"></path><path d=\"M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5\"></path>",
|
"db": "<ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\"></ellipse><path d=\"M21 12c0 1.66-4 3-9 3s-9-1.34-9-3\"></path><path d=\"M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5\"></path>",
|
||||||
"x": "<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>",
|
"x": "<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>",
|
||||||
"+": "<line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"></line><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"></line>",
|
"+": "<line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\"></line><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"></line>",
|
||||||
|
@ -10,12 +10,14 @@ import IndexDetailDialog from '$organisms/connection/collection/dialogs/indexdet
|
|||||||
import QueryChooserDialog from '$organisms/connection/collection/dialogs/querychooser.svelte';
|
import QueryChooserDialog from '$organisms/connection/collection/dialogs/querychooser.svelte';
|
||||||
import DumpDialog from '$organisms/connection/database/dialogs/dump.svelte';
|
import DumpDialog from '$organisms/connection/database/dialogs/dump.svelte';
|
||||||
import HostDetailDialog from '$organisms/connection/host/dialogs/hostdetail.svelte';
|
import HostDetailDialog from '$organisms/connection/host/dialogs/hostdetail.svelte';
|
||||||
|
import DuplicateDialog from '$organisms/connection/collection/dialogs/duplicate.svelte';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreateIndex,
|
CreateIndex,
|
||||||
DropCollection,
|
DropCollection,
|
||||||
DropDatabase,
|
DropDatabase,
|
||||||
DropIndex,
|
DropIndex,
|
||||||
|
DuplicateCollection,
|
||||||
ExecuteShellScript,
|
ExecuteShellScript,
|
||||||
GetIndexes,
|
GetIndexes,
|
||||||
HostLogs,
|
HostLogs,
|
||||||
@ -30,6 +32,7 @@ import {
|
|||||||
TruncateCollection
|
TruncateCollection
|
||||||
} from '$wails/go/app/App';
|
} from '$wails/go/app/App';
|
||||||
|
|
||||||
|
|
||||||
const { set, subscribe } = writable({});
|
const { set, subscribe } = writable({});
|
||||||
const getValue = () => get({ subscribe });
|
const getValue = () => get({ subscribe });
|
||||||
let hostTreeInited = false;
|
let hostTreeInited = false;
|
||||||
@ -120,9 +123,30 @@ async function refresh() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
collection.duplicate = async function() {
|
||||||
|
const dialog = dialogs.new(DuplicateDialog, { host, dbKey, collKey });
|
||||||
|
return new Promise(resolve => {
|
||||||
|
dialog.$on('duplicate', async event => {
|
||||||
|
const success = await DuplicateCollection(
|
||||||
|
hostKey,
|
||||||
|
dbKey,
|
||||||
|
collKey,
|
||||||
|
event.detail.newHost,
|
||||||
|
event.detail.newDb,
|
||||||
|
event.detail.newColl
|
||||||
|
);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
await refresh();
|
||||||
|
dialog.$close();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
collection.export = function(query) {
|
collection.export = function(query) {
|
||||||
const dialog = dialogs.new(ExportDialog, { collection, query });
|
const dialog = dialogs.new(ExportDialog, { collection, query });
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
dialog.$on('export', async event => {
|
dialog.$on('export', async event => {
|
||||||
const success = await PerformFindExport(hostKey, dbKey, collKey, JSON.stringify(event.detail.exportInfo));
|
const success = await PerformFindExport(hostKey, dbKey, collKey, JSON.stringify(event.detail.exportInfo));
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
<script>
|
||||||
|
import Icon from '$components/icon.svelte';
|
||||||
|
import Modal from '$components/modal.svelte';
|
||||||
|
import input from '$lib/actions/input';
|
||||||
|
import hostTree from '$lib/stores/hosttree';
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
|
export let host = {};
|
||||||
|
export let dbKey = '';
|
||||||
|
export let collKey = '';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
let newHost = host.key;
|
||||||
|
let newDb = dbKey;
|
||||||
|
let newColl = `${collKey}-duplicate`;
|
||||||
|
|
||||||
|
function duplicate() {
|
||||||
|
dispatch('duplicate', { newHost, newDb, newColl });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal title="Duplicate collection" width="500px" on:close>
|
||||||
|
<div class="duplicate">
|
||||||
|
<div class="origin">
|
||||||
|
<div class="field">
|
||||||
|
<span class="label">{host.name || host.uri}</span>
|
||||||
|
<!-- <input type="text" readonly value={host.name || host.uri} /> -->
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<span class="label">{dbKey}</span>
|
||||||
|
<!-- <input type="text" readonly value={dbKey} /> -->
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<span class="label">{collKey}</span>
|
||||||
|
<!-- <input type="text" readonly value={collKey} /> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="arrow">
|
||||||
|
<Icon name="arr-r" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="destination">
|
||||||
|
<label class="field">
|
||||||
|
<span class="label">Host</span>
|
||||||
|
<select bind:value={newHost}>
|
||||||
|
{#each Object.values($hostTree) as { key, name }}
|
||||||
|
<option value={key} selected={key === host.key}>{name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span class="label">Database</span>
|
||||||
|
<input type="text" bind:value={newDb} use:input />
|
||||||
|
</label>
|
||||||
|
<label class="field">
|
||||||
|
<span class="label">Collection</span>
|
||||||
|
<input type="text" bind:value={newColl} use:input />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<svelte:fragment slot="footer">
|
||||||
|
<button class="button" on:click={duplicate}>
|
||||||
|
<Icon name="play" /> Duplicate
|
||||||
|
</button>
|
||||||
|
</svelte:fragment>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.duplicate {
|
||||||
|
display: grid;
|
||||||
|
grid-template: auto / 1fr auto 1fr;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field:not(:last-child) {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.origin .field .label {
|
||||||
|
width: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
@ -33,6 +33,8 @@
|
|||||||
{ label: 'Dump collection (BSON via mongodump)…', fn: collection.dump },
|
{ label: 'Dump collection (BSON via mongodump)…', fn: collection.dump },
|
||||||
{ separator: true },
|
{ separator: true },
|
||||||
{ label: 'Rename collection…', fn: collection.rename },
|
{ label: 'Rename collection…', fn: collection.rename },
|
||||||
|
{ label: 'Duplicate collection…', fn: collection.duplicate },
|
||||||
|
{ separator: true },
|
||||||
{ label: 'Truncate collection…', fn: collection.truncate },
|
{ label: 'Truncate collection…', fn: collection.truncate },
|
||||||
{ label: 'Drop collection…', fn: collection.drop },
|
{ label: 'Drop collection…', fn: collection.drop },
|
||||||
{ separator: true },
|
{ separator: true },
|
||||||
|
2
frontend/wailsjs/go/app/App.d.ts
generated
vendored
2
frontend/wailsjs/go/app/App.d.ts
generated
vendored
@ -20,6 +20,8 @@ export function DropDatabase(arg1:string,arg2:string):Promise<boolean>;
|
|||||||
|
|
||||||
export function DropIndex(arg1:string,arg2:string,arg3:string,arg4:string):Promise<boolean>;
|
export function DropIndex(arg1:string,arg2:string,arg3:string,arg4:string):Promise<boolean>;
|
||||||
|
|
||||||
|
export function DuplicateCollection(arg1:string,arg2:string,arg3:string,arg4:string,arg5:string,arg6:string):Promise<boolean>;
|
||||||
|
|
||||||
export function Environment():Promise<app.EnvironmentInfo>;
|
export function Environment():Promise<app.EnvironmentInfo>;
|
||||||
|
|
||||||
export function ExecuteShellScript(arg1:string,arg2:string,arg3:string,arg4:string):Promise<app.ExecuteShellScriptResult>;
|
export function ExecuteShellScript(arg1:string,arg2:string,arg3:string,arg4:string):Promise<app.ExecuteShellScriptResult>;
|
||||||
|
4
frontend/wailsjs/go/app/App.js
generated
4
frontend/wailsjs/go/app/App.js
generated
@ -30,6 +30,10 @@ export function DropIndex(arg1, arg2, arg3, arg4) {
|
|||||||
return window['go']['app']['App']['DropIndex'](arg1, arg2, arg3, arg4);
|
return window['go']['app']['App']['DropIndex'](arg1, arg2, arg3, arg4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function DuplicateCollection(arg1, arg2, arg3, arg4, arg5, arg6) {
|
||||||
|
return window['go']['app']['App']['DuplicateCollection'](arg1, arg2, arg3, arg4, arg5, arg6);
|
||||||
|
}
|
||||||
|
|
||||||
export function Environment() {
|
export function Environment() {
|
||||||
return window['go']['app']['App']['Environment']();
|
return window['go']['app']['App']['Environment']();
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OpenCollectionResult struct {
|
type OpenCollectionResult struct {
|
||||||
@ -55,6 +57,77 @@ func (a *App) RenameCollection(hostKey, dbKey, collKey, newCollKey string) bool
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) DuplicateCollection(origHostKey, origDbKey, origCollKey, destHostKey, destDbKey, destCollKey string) bool {
|
||||||
|
runtime.LogDebugf(a.ctx, "Duplicating collection %s:%s.%s to %s:%s.%s", origHostKey, origDbKey, origCollKey, destHostKey, destDbKey, destCollKey)
|
||||||
|
|
||||||
|
if (origHostKey == destHostKey) && (origDbKey == destDbKey) && (origCollKey == destCollKey) {
|
||||||
|
runtime.LogInfof(a.ctx, "Duplicating collection: cannot duplicate to the same collection")
|
||||||
|
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||||
|
Type: runtime.WarningDialog,
|
||||||
|
Title: "Error while duplicating collection",
|
||||||
|
Message: "The new collection name cannot equal the old one!",
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
origHost, origCtx, origClose, err := a.connectToHost(origHostKey)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer origClose()
|
||||||
|
|
||||||
|
var destHost *mongo.Client
|
||||||
|
var destCtx context.Context
|
||||||
|
|
||||||
|
if origHostKey != destHostKey {
|
||||||
|
var destClose func()
|
||||||
|
destHost, destCtx, destClose, err = a.connectToHost(destHostKey)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
defer destClose()
|
||||||
|
} else {
|
||||||
|
destHost = origHost
|
||||||
|
destCtx = origCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
destColl := destHost.Database(destDbKey).Collection(destCollKey)
|
||||||
|
origCur, err := origHost.Database(origDbKey).Collection(origCollKey).Find(origCtx, bson.D{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
runtime.LogInfof(a.ctx, "Duplicating collection: could not create origin cursor: %s", err.Error())
|
||||||
|
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||||
|
Type: runtime.WarningDialog,
|
||||||
|
Title: "Error while duplicating collection",
|
||||||
|
Message: "Could not create origin cursor: " + err.Error(),
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var n int64 = 0
|
||||||
|
var ok = true
|
||||||
|
|
||||||
|
for origCur.Next(origCtx) {
|
||||||
|
_, err := destColl.InsertOne(destCtx, origCur.Current)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ok = false
|
||||||
|
runtime.LogInfof(a.ctx, "Duplicating collection: could not insert item %d: %s", n, err.Error())
|
||||||
|
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||||
|
Type: runtime.WarningDialog,
|
||||||
|
Title: "Error while duplicating collection",
|
||||||
|
Message: fmt.Sprintf("Could not insert item %d: %s", n, err.Error()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
n += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) TruncateCollection(hostKey, dbKey, collKey string) bool {
|
func (a *App) TruncateCollection(hostKey, dbKey, collKey string) bool {
|
||||||
choice, _ := runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
choice, _ := runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
|
||||||
Title: "Confirm",
|
Title: "Confirm",
|
||||||
|
Loading…
Reference in New Issue
Block a user