fix: add relative imports to the dependency list in deploymentUI (#8548)

* prepare sqlx

* Add relative imports to getDependencies of deployUI

* nit

* fix: correct get_imports doc comment, add tracing, use Set for dedup

- Fix copy-pasted doc comment on get_imports (said "get dependents")
- Add tracing::debug to get_imports handler to match get_dependents
- Use Set for O(1) duplicate detection in deploy dependency traversal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
wendrul
2026-03-26 19:28:18 +01:00
committed by GitHub
parent 8866bd44cf
commit d760ea5eaf
5 changed files with 106 additions and 1 deletions
@@ -0,0 +1,23 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT DISTINCT imported_path as \"imported_path!\"\n FROM dependency_map\n WHERE workspace_id = $1\n AND importer_path = $2\n AND imported_path NOT LIKE 'dependencies/%'\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "imported_path!",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Text",
"Text"
]
},
"nullable": [
false
]
},
"hash": "52d765c87cb8da0ca71fb53156820e383a998a54c95355bb85fe7e762a0d9765"
}
@@ -79,6 +79,7 @@ pub fn workspaced_service() -> Router {
.route("/rebuild_dependency_map", post(rebuild_dependency_map))
.route("/get_dependency_map", get(get_dependency_map))
.route("/get_dependents/*imported_path", get(get_dependents))
.route("/get_imports/*importer_path", get(get_imports))
.route("/get_dependents_amounts", post(get_dependents_amounts))
.route("/get_settings", get(get_settings))
.route(
@@ -4358,6 +4359,30 @@ async fn get_dependents(
Ok(Json(dependents))
}
async fn get_imports(
Extension(db): Extension<DB>,
Path((w_id, importer_path)): Path<(String, String)>,
_authed: ApiAuthed,
) -> JsonResult<Vec<String>> {
tracing::debug!(
workspace_id = %w_id,
importer_path = %importer_path,
"API: Getting imports for importer path"
);
let imports = ScopedDependencyMap::get_imports(&importer_path, &w_id, &db).await?;
tracing::debug!(
workspace_id = %w_id,
importer_path = %importer_path,
imports_count = imports.len(),
"API: Found imports: {:?}",
imports
);
Ok(Json(imports))
}
#[derive(Serialize, Debug)]
struct DependentsAmount {
imported_path: String,
+24
View File
@@ -2714,6 +2714,30 @@ paths:
items:
$ref: "#/components/schemas/DependencyDependent"
/w/{workspace}/workspaces/get_imports/{importer_path}:
get:
summary: get script imports for an importer path
operationId: getImports
tags:
- workspace
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- name: importer_path
in: path
required: true
schema:
type: string
description: The script path to get imports for
responses:
"200":
description: list of imported script paths
content:
application/json:
schema:
type: array
items:
type: string
/w/{workspace}/workspaces/get_dependents_amounts:
post:
summary: get dependents amounts for multiple imported paths
@@ -445,7 +445,28 @@ SELECT importer_node_id, imported_path, imported_lockfile_hash
}
}
/// Get dependents of any imported path - returns scripts/flows/apps that depend on it
/// Get imports of a given importer path - returns paths that the importer depends on
pub async fn get_imports<'c>(
importer_path: &str,
workspace_id: &str,
e: impl PgExecutor<'c>,
) -> Result<Vec<String>> {
sqlx::query_scalar!(
r#"
SELECT DISTINCT imported_path as "imported_path!"
FROM dependency_map
WHERE workspace_id = $1
AND importer_path = $2
AND imported_path NOT LIKE 'dependencies/%'
"#,
workspace_id,
importer_path
)
.fetch_all(e)
.await
.map_err(Error::from)
}
pub async fn get_dependents<'c>(
imported_path: &str,
workspace_id: &str,
@@ -262,13 +262,25 @@
return getTriggerDependency(additionalInformation.triggers.kind, path, $workspaceStore!)
}
throw new Error('Missing trigger information')
} else if (kind == 'script') {
const imports = await WorkspaceService.getImports({
workspace: $workspaceStore!,
importerPath: path
})
return imports.map((importedPath) => ({ kind: 'script' as Kind, path: importedPath }))
}
return []
}
let toProcess = [{ kind, path }]
let processedSet = new Set<string>()
let processed: { kind: Kind; path: string }[] = []
while (toProcess.length > 0) {
const { kind, path } = toProcess.pop()!
const key = `${kind}:${path}`
if (processedSet.has(key)) {
continue
}
processedSet.add(key)
toProcess.push(...(await rec(kind, path)))
processed.push({ kind, path })
}