mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-31 14:33:18 +02:00
Add ability to exclude environments from data export
This commit is contained in:
@@ -1002,8 +1002,11 @@ async fn cmd_export_data(
|
|||||||
window: WebviewWindow,
|
window: WebviewWindow,
|
||||||
export_path: &str,
|
export_path: &str,
|
||||||
workspace_ids: Vec<&str>,
|
workspace_ids: Vec<&str>,
|
||||||
|
include_environments: bool,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let export_data = get_workspace_export_resources(window.app_handle(), workspace_ids).await;
|
let export_data = get_workspace_export_resources(&window, workspace_ids, include_environments)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
let f = File::options()
|
let f = File::options()
|
||||||
.create(true)
|
.create(true)
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
|
|||||||
@@ -2175,7 +2175,8 @@ pub async fn batch_upsert<R: Runtime>(
|
|||||||
pub async fn get_workspace_export_resources<R: Runtime>(
|
pub async fn get_workspace_export_resources<R: Runtime>(
|
||||||
mgr: &impl Manager<R>,
|
mgr: &impl Manager<R>,
|
||||||
workspace_ids: Vec<&str>,
|
workspace_ids: Vec<&str>,
|
||||||
) -> WorkspaceExport {
|
include_environments: bool,
|
||||||
|
) -> Result<WorkspaceExport> {
|
||||||
let mut data = WorkspaceExport {
|
let mut data = WorkspaceExport {
|
||||||
yaak_version: mgr.package_info().version.clone().to_string(),
|
yaak_version: mgr.package_info().version.clone().to_string(),
|
||||||
yaak_schema: 2,
|
yaak_schema: 2,
|
||||||
@@ -2190,24 +2191,19 @@ pub async fn get_workspace_export_resources<R: Runtime>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
for workspace_id in workspace_ids {
|
for workspace_id in workspace_ids {
|
||||||
data.resources
|
data.resources.workspaces.push(get_workspace(mgr, workspace_id).await?);
|
||||||
.workspaces
|
data.resources.environments.append(&mut list_environments(mgr, workspace_id).await?);
|
||||||
.push(get_workspace(mgr, workspace_id).await.expect("Failed to get workspace"));
|
data.resources.folders.append(&mut list_folders(mgr, workspace_id).await?);
|
||||||
data.resources.environments.append(
|
data.resources.http_requests.append(&mut list_http_requests(mgr, workspace_id).await?);
|
||||||
&mut list_environments(mgr, workspace_id).await.expect("Failed to get environments"),
|
data.resources.grpc_requests.append(&mut list_grpc_requests(mgr, workspace_id).await?);
|
||||||
);
|
|
||||||
data.resources
|
|
||||||
.folders
|
|
||||||
.append(&mut list_folders(mgr, workspace_id).await.expect("Failed to get folders"));
|
|
||||||
data.resources.http_requests.append(
|
|
||||||
&mut list_http_requests(mgr, workspace_id).await.expect("Failed to get http requests"),
|
|
||||||
);
|
|
||||||
data.resources.grpc_requests.append(
|
|
||||||
&mut list_grpc_requests(mgr, workspace_id).await.expect("Failed to get grpc requests"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data
|
// Nuke environments if we don't want them
|
||||||
|
if !include_environments {
|
||||||
|
data.resources.environments.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the created_at or updated_at timestamps for an upsert operation, depending on the ID
|
// Generate the created_at or updated_at timestamps for an upsert operation, depending on the ID
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ pub enum Error {
|
|||||||
#[error("Unknown model: {0}")]
|
#[error("Unknown model: {0}")]
|
||||||
UnknownModel(String),
|
UnknownModel(String),
|
||||||
|
|
||||||
#[error("Workspace not configured for sync: {0}")]
|
|
||||||
WorkspaceSyncNotConfigured(String),
|
|
||||||
|
|
||||||
#[error("I/o error: {0}")]
|
#[error("I/o error: {0}")]
|
||||||
IoError(#[from] io::Error),
|
IoError(#[from] io::Error),
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ pub(crate) async fn get_db_candidates<R: Runtime>(
|
|||||||
sync_dir: &Path,
|
sync_dir: &Path,
|
||||||
) -> Result<Vec<DbCandidate>> {
|
) -> Result<Vec<DbCandidate>> {
|
||||||
let models: HashMap<_, _> =
|
let models: HashMap<_, _> =
|
||||||
workspace_models(mgr, workspace_id).await.into_iter().map(|m| (m.id(), m)).collect();
|
workspace_models(mgr, workspace_id).await?.into_iter().map(|m| (m.id(), m)).collect();
|
||||||
let sync_states: HashMap<_, _> = list_sync_states_for_workspace(mgr, workspace_id, sync_dir)
|
let sync_states: HashMap<_, _> = list_sync_states_for_workspace(mgr, workspace_id, sync_dir)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -270,12 +270,15 @@ pub(crate) fn compute_sync_ops(
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn workspace_models<R: Runtime>(mgr: &impl Manager<R>, workspace_id: &str) -> Vec<SyncModel> {
|
async fn workspace_models<R: Runtime>(
|
||||||
let resources = get_workspace_export_resources(mgr, vec![workspace_id]).await.resources;
|
mgr: &impl Manager<R>,
|
||||||
|
workspace_id: &str,
|
||||||
|
) -> Result<Vec<SyncModel>> {
|
||||||
|
let resources = get_workspace_export_resources(mgr, vec![workspace_id], true).await?.resources;
|
||||||
let workspace = resources.workspaces.iter().find(|w| w.id == workspace_id);
|
let workspace = resources.workspaces.iter().find(|w| w.id == workspace_id);
|
||||||
|
|
||||||
let workspace = match workspace {
|
let workspace = match workspace {
|
||||||
None => return Vec::new(),
|
None => return Ok(Vec::new()),
|
||||||
Some(w) => w,
|
Some(w) => w,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -294,7 +297,7 @@ async fn workspace_models<R: Runtime>(mgr: &impl Manager<R>, workspace_id: &str)
|
|||||||
sync_models.push(SyncModel::GrpcRequest(m));
|
sync_models.push(SyncModel::GrpcRequest(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
sync_models
|
Ok(sync_models)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn apply_sync_ops<R: Runtime>(
|
pub(crate) async fn apply_sync_ops<R: Runtime>(
|
||||||
|
|||||||
@@ -420,7 +420,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
|||||||
<div className="h-full px-1.5 overflow-y-auto pt-2 pb-1">
|
<div className="h-full px-1.5 overflow-y-auto pt-2 pb-1">
|
||||||
{filteredGroups.map((g) => (
|
{filteredGroups.map((g) => (
|
||||||
<div key={g.key} className="mb-1.5 w-full">
|
<div key={g.key} className="mb-1.5 w-full">
|
||||||
<Heading size={2} className="!text-xs uppercase px-1.5 h-sm flex items-center">
|
<Heading level={2} className="!text-xs uppercase px-1.5 h-sm flex items-center">
|
||||||
{g.label}
|
{g.label}
|
||||||
</Heading>
|
</Heading>
|
||||||
{g.items.map((v) => (
|
{g.items.map((v) => (
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
|||||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||||
import { pluralizeCount } from '../lib/pluralize';
|
import { pluralizeCount } from '../lib/pluralize';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
|
import { Banner } from './core/Banner';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import { Checkbox } from './core/Checkbox';
|
import { Checkbox } from './core/Checkbox';
|
||||||
import { HStack, VStack } from './core/Stacks';
|
import { HStack, VStack } from './core/Stacks';
|
||||||
@@ -39,6 +40,7 @@ function ExportDataDialogContent({
|
|||||||
allWorkspaces: Workspace[];
|
allWorkspaces: Workspace[];
|
||||||
activeWorkspace: Workspace;
|
activeWorkspace: Workspace;
|
||||||
}) {
|
}) {
|
||||||
|
const [includeEnvironments, setIncludeEnvironments] = useState<boolean>(true);
|
||||||
const [selectedWorkspaces, setSelectedWorkspaces] = useState<Record<string, boolean>>({
|
const [selectedWorkspaces, setSelectedWorkspaces] = useState<Record<string, boolean>>({
|
||||||
[activeWorkspace.id]: true,
|
[activeWorkspace.id]: true,
|
||||||
});
|
});
|
||||||
@@ -67,10 +69,14 @@ function ExportDataDialogContent({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await invokeCmd('cmd_export_data', { workspaceIds: ids, exportPath });
|
await invokeCmd('cmd_export_data', {
|
||||||
|
workspaceIds: ids,
|
||||||
|
exportPath,
|
||||||
|
includeEnvironments: includeEnvironments,
|
||||||
|
});
|
||||||
onHide();
|
onHide();
|
||||||
onSuccess(exportPath);
|
onSuccess(exportPath);
|
||||||
}, [onHide, onSuccess, selectedWorkspaces, workspaces]);
|
}, [includeEnvironments, onHide, onSuccess, selectedWorkspaces, workspaces]);
|
||||||
|
|
||||||
const allSelected = workspaces.every((w) => selectedWorkspaces[w.id]);
|
const allSelected = workspaces.every((w) => selectedWorkspaces[w.id]);
|
||||||
const numSelected = Object.values(selectedWorkspaces).filter(Boolean).length;
|
const numSelected = Object.values(selectedWorkspaces).filter(Boolean).length;
|
||||||
@@ -117,6 +123,18 @@ function ExportDataDialogContent({
|
|||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<Banner className="!p-0">
|
||||||
|
<details open>
|
||||||
|
<summary className="px-3 py-2">Extra Settings</summary>
|
||||||
|
<div className="px-3 pb-2">
|
||||||
|
<Checkbox
|
||||||
|
checked={includeEnvironments}
|
||||||
|
onChange={setIncludeEnvironments}
|
||||||
|
title="Include environments"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</Banner>
|
||||||
<HStack space={2} justifyContent="end">
|
<HStack space={2} justifyContent="end">
|
||||||
<Button className="focus" variant="border" onClick={onHide}>
|
<Button className="focus" variant="border" onClick={onHide}>
|
||||||
Cancel
|
Cancel
|
||||||
@@ -128,7 +146,8 @@ function ExportDataDialogContent({
|
|||||||
disabled={noneSelected}
|
disabled={noneSelected}
|
||||||
onClick={() => handleExport()}
|
onClick={() => handleExport()}
|
||||||
>
|
>
|
||||||
Export {pluralizeCount('Workspace', numSelected, { omitSingle: true, noneWord: 'Nothing' })}
|
Export{' '}
|
||||||
|
{pluralizeCount('Workspace', numSelected, { omitSingle: true, noneWord: 'Nothing' })}
|
||||||
</Button>
|
</Button>
|
||||||
</HStack>
|
</HStack>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export function SettingsGeneral() {
|
|||||||
|
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
|
|
||||||
<Heading size={2}>
|
<Heading level={2}>
|
||||||
Workspace{' '}
|
Workspace{' '}
|
||||||
<div className="inline-block ml-1 bg-surface-highlight px-2 py-0.5 rounded text text-shrink">
|
<div className="inline-block ml-1 bg-surface-highlight px-2 py-0.5 rounded text text-shrink">
|
||||||
{workspace.name}
|
{workspace.name}
|
||||||
@@ -134,7 +134,7 @@ export function SettingsGeneral() {
|
|||||||
|
|
||||||
<Separator className="my-4" />
|
<Separator className="my-4" />
|
||||||
|
|
||||||
<Heading size={2}>App Info</Heading>
|
<Heading level={2}>App Info</Heading>
|
||||||
<KeyValueRows>
|
<KeyValueRows>
|
||||||
<KeyValueRow label="Version">{appInfo.version}</KeyValueRow>
|
<KeyValueRow label="Version">{appInfo.version}</KeyValueRow>
|
||||||
<KeyValueRow
|
<KeyValueRow
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export function Dialog({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{title ? (
|
{title ? (
|
||||||
<Heading className="px-6 mt-4 mb-2" size={1} id={titleId}>
|
<Heading className="px-6 mt-4 mb-2" level={1} id={titleId}>
|
||||||
{title}
|
{title}
|
||||||
</Heading>
|
</Heading>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ import classNames from 'classnames';
|
|||||||
import type { HTMLAttributes } from 'react';
|
import type { HTMLAttributes } from 'react';
|
||||||
|
|
||||||
interface Props extends HTMLAttributes<HTMLHeadingElement> {
|
interface Props extends HTMLAttributes<HTMLHeadingElement> {
|
||||||
size?: 1 | 2 | 3;
|
level?: 1 | 2 | 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Heading({ className, size = 1, ...props }: Props) {
|
export function Heading({ className, level = 1, ...props }: Props) {
|
||||||
const Component = size === 1 ? 'h1' : size === 2 ? 'h2' : 'h3';
|
const Component = level === 1 ? 'h1' : level === 2 ? 'h2' : 'h3';
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
className={classNames(
|
className={classNames(
|
||||||
className,
|
className,
|
||||||
'font-semibold text',
|
'font-semibold text',
|
||||||
size === 1 && 'text-2xl',
|
level === 1 && 'text-2xl',
|
||||||
size === 2 && 'text-xl',
|
level === 2 && 'text-xl',
|
||||||
size === 3 && 'text-lg',
|
level === 3 && 'text-lg',
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export function useExportData() {
|
|||||||
|
|
||||||
showDialog({
|
showDialog({
|
||||||
id: 'export-data',
|
id: 'export-data',
|
||||||
title: 'Export App Data',
|
title: 'Export Data',
|
||||||
size: 'md',
|
size: 'md',
|
||||||
noPadding: true,
|
noPadding: true,
|
||||||
render: ({ hide }) => (
|
render: ({ hide }) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user