mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-24 09:38:29 +02:00
Add migrate for base environment to sync logic
This commit is contained in:
@@ -66,7 +66,22 @@ export function migrateImport(contents: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { resources: parsed.resources }; // Should already be in the correct format
|
// Migrate v4 to v5
|
||||||
|
for (const environment of parsed.resources.environments ?? []) {
|
||||||
|
if ('base' in environment && environment.base) {
|
||||||
|
environment.parentId = environment.workspaceId;
|
||||||
|
environment.parentType = 'workspace';
|
||||||
|
delete environment.environmentId;
|
||||||
|
} else if ('base' in environment && !environment.base) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const baseEnvironment = parsed.resources.environments.find((e: any) => e.base);
|
||||||
|
environment.parentId = baseEnvironment?.id ?? null;
|
||||||
|
environment.parentType = 'environment';
|
||||||
|
delete environment.environmentId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { resources: parsed.resources };
|
||||||
}
|
}
|
||||||
|
|
||||||
function isJSObject(obj: unknown) {
|
function isJSObject(obj: unknown) {
|
||||||
|
|||||||
32
src-tauri/Cargo.lock
generated
32
src-tauri/Cargo.lock
generated
@@ -5163,10 +5163,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.219"
|
version = "1.0.226"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5203,10 +5204,19 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_core"
|
||||||
version = "1.0.219"
|
version = "1.0.226"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.226"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -5236,6 +5246,17 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_path_to_error"
|
||||||
|
version = "0.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_repr"
|
name = "serde_repr"
|
||||||
version = "0.1.20"
|
version = "0.1.20"
|
||||||
@@ -8162,6 +8183,7 @@ dependencies = [
|
|||||||
"notify",
|
"notify",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_path_to_error",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"sha1",
|
"sha1",
|
||||||
"tauri",
|
"tauri",
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ impl<'a> DbContext<'a> {
|
|||||||
let environments = self.list_environments_ensure_base(workspace_id)?;
|
let environments = self.list_environments_ensure_base(workspace_id)?;
|
||||||
let base_environments = environments
|
let base_environments = environments
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|e| e.parent_id.is_none())
|
.filter(|e| e.parent_model == "workspace")
|
||||||
.collect::<Vec<Environment>>();
|
.collect::<Vec<Environment>>();
|
||||||
|
|
||||||
if base_environments.len() > 1 {
|
if base_environments.len() > 1 {
|
||||||
@@ -44,7 +44,7 @@ impl<'a> DbContext<'a> {
|
|||||||
let mut environments =
|
let mut environments =
|
||||||
self.find_many::<Environment>(EnvironmentIden::WorkspaceId, workspace_id, None)?;
|
self.find_many::<Environment>(EnvironmentIden::WorkspaceId, workspace_id, None)?;
|
||||||
|
|
||||||
let base_environment = environments.iter().find(|e| e.parent_id.is_none());
|
let base_environment = environments.iter().find(|e| e.parent_model == "workspace");
|
||||||
|
|
||||||
if let None = base_environment {
|
if let None = base_environment {
|
||||||
let e = self.upsert_environment(
|
let e = self.upsert_environment(
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ thiserror = { workspace = true }
|
|||||||
tokio = { workspace = true, features = ["fs", "sync", "macros"] }
|
tokio = { workspace = true, features = ["fs", "sync", "macros"] }
|
||||||
ts-rs = { workspace = true, features = ["chrono-impl", "serde-json-impl"] }
|
ts-rs = { workspace = true, features = ["chrono-impl", "serde-json-impl"] }
|
||||||
yaak-models = { workspace = true }
|
yaak-models = { workspace = true }
|
||||||
|
serde_path_to_error = "0.1.20"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-plugin = { workspace = true, features = ["build"] }
|
tauri-plugin = { workspace = true, features = ["build"] }
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use crate::error::Error::UnknownModel;
|
|||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
use serde_yaml::{Mapping, Value};
|
||||||
use sha1::{Digest, Sha1};
|
use sha1::{Digest, Sha1};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -11,7 +12,7 @@ use yaak_models::models::{
|
|||||||
AnyModel, Environment, Folder, GrpcRequest, HttpRequest, WebsocketRequest, Workspace,
|
AnyModel, Environment, Folder, GrpcRequest, HttpRequest, WebsocketRequest, Workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, PartialEq, Serialize, TS)]
|
||||||
#[serde(rename_all = "snake_case", tag = "type")]
|
#[serde(rename_all = "snake_case", tag = "type")]
|
||||||
#[ts(export, export_to = "gen_models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub enum SyncModel {
|
pub enum SyncModel {
|
||||||
@@ -23,6 +24,78 @@ pub enum SyncModel {
|
|||||||
WebsocketRequest(WebsocketRequest),
|
WebsocketRequest(WebsocketRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for SyncModel {
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
use serde_path_to_error as spte;
|
||||||
|
let mut v = Value::deserialize(deserializer)?;
|
||||||
|
let model = match v.get("model") {
|
||||||
|
Some(Value::String(model)) => model.clone(),
|
||||||
|
_ => "".to_string(),
|
||||||
|
};
|
||||||
|
let model = model.as_str();
|
||||||
|
|
||||||
|
let obj = v
|
||||||
|
.as_mapping_mut()
|
||||||
|
.ok_or_else(|| serde::de::Error::custom("expected object for SyncModel"))?;
|
||||||
|
|
||||||
|
// Dispatch to CHILD types (no recursion)
|
||||||
|
match model {
|
||||||
|
"workspace" => {
|
||||||
|
let x: Workspace = spte::deserialize(v).map_err(serde::de::Error::custom)?;
|
||||||
|
Ok(SyncModel::Workspace(x))
|
||||||
|
}
|
||||||
|
"environment" => {
|
||||||
|
migrate_environment(obj);
|
||||||
|
let x: Environment = spte::deserialize(v).map_err(serde::de::Error::custom)?;
|
||||||
|
Ok(SyncModel::Environment(x))
|
||||||
|
}
|
||||||
|
"folder" => {
|
||||||
|
let x: Folder = spte::deserialize(v).map_err(serde::de::Error::custom)?;
|
||||||
|
Ok(SyncModel::Folder(x))
|
||||||
|
}
|
||||||
|
"http_request" => {
|
||||||
|
let x: HttpRequest = spte::deserialize(v).map_err(serde::de::Error::custom)?;
|
||||||
|
Ok(SyncModel::HttpRequest(x))
|
||||||
|
}
|
||||||
|
"grpc_request" => {
|
||||||
|
let x: GrpcRequest = spte::deserialize(v).map_err(serde::de::Error::custom)?;
|
||||||
|
Ok(SyncModel::GrpcRequest(x))
|
||||||
|
}
|
||||||
|
"websocket_request" => {
|
||||||
|
let x: WebsocketRequest = spte::deserialize(v).map_err(serde::de::Error::custom)?;
|
||||||
|
Ok(SyncModel::WebsocketRequest(x))
|
||||||
|
}
|
||||||
|
other => Err(serde::de::Error::unknown_variant(
|
||||||
|
other,
|
||||||
|
&[
|
||||||
|
"workspace",
|
||||||
|
"environment",
|
||||||
|
"folder",
|
||||||
|
"http_request",
|
||||||
|
"grpc_request",
|
||||||
|
"websocket_request",
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn migrate_environment(obj: &mut Mapping) {
|
||||||
|
match obj.get("base") {
|
||||||
|
Some(Value::Bool(base)) => {
|
||||||
|
if *base {
|
||||||
|
obj.insert("parentModel".into(), "workspace".into());
|
||||||
|
} else {
|
||||||
|
obj.insert("parentModel".into(), "environment".into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SyncModel {
|
impl SyncModel {
|
||||||
pub fn from_bytes(content: Vec<u8>, file_path: &Path) -> Result<Option<(SyncModel, String)>> {
|
pub fn from_bytes(content: Vec<u8>, file_path: &Path) -> Result<Option<(SyncModel, String)>> {
|
||||||
let mut hasher = Sha1::new();
|
let mut hasher = Sha1::new();
|
||||||
@@ -145,3 +218,59 @@ impl TryFrom<AnyModel> for SyncModel {
|
|||||||
Ok(m)
|
Ok(m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod placeholder_tests {
|
||||||
|
use crate::error::Result;
|
||||||
|
use crate::models::SyncModel;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserializes_environment_via_syncmodel_with_fixups() -> Result<()> {
|
||||||
|
let raw = r#"
|
||||||
|
type: environment
|
||||||
|
model: environment
|
||||||
|
id: ev_fAUS49FUN2
|
||||||
|
workspaceId: wk_kfSI3JDHd7
|
||||||
|
createdAt: 2025-01-11T17:02:58.012792
|
||||||
|
updatedAt: 2025-07-23T20:00:46.049649
|
||||||
|
name: Global Variables
|
||||||
|
public: true
|
||||||
|
base: true
|
||||||
|
variables: []
|
||||||
|
color: null
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let m: SyncModel = serde_yaml::from_str(raw)?;
|
||||||
|
match m {
|
||||||
|
SyncModel::Environment(env) => {
|
||||||
|
assert_eq!(env.parent_model, "workspace".to_string());
|
||||||
|
assert_eq!(env.parent_id, None);
|
||||||
|
}
|
||||||
|
_ => panic!("expected base environment"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let raw = r#"
|
||||||
|
type: environment
|
||||||
|
model: environment
|
||||||
|
id: ev_fAUS49FUN2
|
||||||
|
workspaceId: wk_kfSI3JDHd7
|
||||||
|
createdAt: 2025-01-11T17:02:58.012792
|
||||||
|
updatedAt: 2025-07-23T20:00:46.049649
|
||||||
|
name: Global Variables
|
||||||
|
public: true
|
||||||
|
base: false
|
||||||
|
variables: []
|
||||||
|
color: null
|
||||||
|
"#;
|
||||||
|
let m: SyncModel = serde_yaml::from_str(raw)?;
|
||||||
|
match m {
|
||||||
|
SyncModel::Environment(env) => {
|
||||||
|
assert_eq!(env.parent_model, "environment".to_string());
|
||||||
|
assert_eq!(env.parent_id, None);
|
||||||
|
}
|
||||||
|
_ => panic!("expected sub environment"),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ export function EnvironmentEditor({
|
|||||||
{
|
{
|
||||||
label: 'Encrypt Variables',
|
label: 'Encrypt Variables',
|
||||||
onClick: () => encryptEnvironment(environment),
|
onClick: () => encryptEnvironment(environment),
|
||||||
color: 'primary',
|
color: 'success',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { activeEnvironmentIdAtom } from '../../../hooks/useActiveEnvironment';
|
|
||||||
import type { WrappedEnvironmentVariable } from '../../../hooks/useEnvironmentVariables';
|
import type { WrappedEnvironmentVariable } from '../../../hooks/useEnvironmentVariables';
|
||||||
import { useEnvironmentVariables } from '../../../hooks/useEnvironmentVariables';
|
import { useEnvironmentVariables } from '../../../hooks/useEnvironmentVariables';
|
||||||
import { useRequestEditor } from '../../../hooks/useRequestEditor';
|
import { useRequestEditor } from '../../../hooks/useRequestEditor';
|
||||||
@@ -138,9 +137,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
) {
|
) {
|
||||||
const settings = useAtomValue(settingsAtom);
|
const settings = useAtomValue(settingsAtom);
|
||||||
|
|
||||||
const activeEnvironmentId = useAtomValue(activeEnvironmentIdAtom);
|
const allEnvironmentVariables = useEnvironmentVariables(forcedEnvironmentId ?? null);
|
||||||
const environmentId = forcedEnvironmentId ?? activeEnvironmentId ?? null;
|
|
||||||
const allEnvironmentVariables = useEnvironmentVariables(environmentId);
|
|
||||||
const environmentVariables = autocompleteVariables ? allEnvironmentVariables : emptyVariables;
|
const environmentVariables = autocompleteVariables ? allEnvironmentVariables : emptyVariables;
|
||||||
const useTemplating = !!(autocompleteFunctions || autocompleteVariables || autocomplete);
|
const useTemplating = !!(autocompleteFunctions || autocompleteVariables || autocomplete);
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function useEnvironmentVariables(targetEnvironmentId: string | null) {
|
|||||||
|
|
||||||
// Folder environments also can auto-complete from the active environment
|
// Folder environments also can auto-complete from the active environment
|
||||||
const activeEnvironmentVariables =
|
const activeEnvironmentVariables =
|
||||||
targetEnvironment != null && isFolderEnvironment(targetEnvironment)
|
targetEnvironment == null || isFolderEnvironment(targetEnvironment)
|
||||||
? wrapVariables(activeEnvironment)
|
? wrapVariables(activeEnvironment)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import { useMemo } from 'react';
|
|||||||
export function useEnvironmentsBreakdown() {
|
export function useEnvironmentsBreakdown() {
|
||||||
const allEnvironments = useAtomValue(environmentsAtom);
|
const allEnvironments = useAtomValue(environmentsAtom);
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const baseEnvironments = allEnvironments.filter((e) => e.parentId == null) ?? [];
|
const baseEnvironments = allEnvironments.filter((e) => e.parentModel == 'workspace') ?? [];
|
||||||
const subEnvironments =
|
const subEnvironments =
|
||||||
allEnvironments.filter((e) => e.parentModel === 'environment' && e.parentId != null) ?? [];
|
allEnvironments.filter((e) => e.parentModel === 'environment') ?? [];
|
||||||
const folderEnvironments =
|
const folderEnvironments =
|
||||||
allEnvironments.filter((e) => e.parentModel === 'folder' && e.parentId != null) ?? [];
|
allEnvironments.filter((e) => e.parentModel === 'folder' && e.parentId != null) ?? [];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user