Skip to content

Commit da64ba4

Browse files
Merge pull request #752 from bruin-data/improve/render-vars-in-ingestr
Improve/render vars in ingestr
2 parents c99dd57 + 9655621 commit da64ba4

9 files changed

Lines changed: 448 additions & 257 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# Changelog
2+
## [0.79.9] - [2026-04-29]
3+
- Improved Ingestr asset display with edit/view mode toggle using "Edit"/"Done" text link for cleaner UI.
4+
25
## [0.79.8] - [2026-04-23]
36
- Fixed Bruin panel not updating on file save in latest VS Code versions on Windows due to path comparison issues.
47

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,11 @@ Bruin is a unified analytics platform that enables data professionals to work en
7070
## Release Notes
7171

7272
### Recent Update
73+
- **0.79.9**: Improved Ingestr asset display with edit/view mode toggle.
7374
- **0.79.8**: Fixed Bruin panel not updating on file save in latest VS Code versions on Windows due to path comparison issues.
7475
- **0.79.7**: Fixed reset date button for continuous pipelines; improved tooltip to clarify behavior.
7576
- **0.79.6**: Fixed "Fill from Query" command failing due to unsupported `--environment` flag.
7677
- **0.79.5**: Added interval modifiers preview tooltip on the checkbox.
7778
- **0.79.4**: Fixed "Fill from DB" to use selected environment; fixed start date input on full refresh.
78-
- **0.79.3**: Added ability to select and run SQL text directly in Query Preview.
7979

8080
For a full changelog, see Bruin Extension [Changelog](https://github.com/bruin-data/bruin-vscode/blob/main/CHANGELOG.md).

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "bruin",
33
"displayName": "Bruin",
44
"description": "Manage your Bruin data assets from within VS Code.",
5-
"version": "0.79.8",
5+
"version": "0.79.9",
66
"engines": {
77
"vscode": "^1.87.0"
88
},

src/bruin/bruinRender.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,12 @@ export class BruinRender extends BruinCommand {
6464
}
6565

6666
const isTaskYml = filePath.endsWith('.task.yml') || filePath.endsWith('.task.yaml');
67-
67+
6868
if ((isBruinAsset || isBruinPipeline || (isBruinYaml && !isTaskYml))) {
6969
BruinPanel?.postMessage("render-message", {
7070
status: "bruin-asset-alert",
7171
message: "-- Python, Yaml, or Pipeline BRUIN asset detected --",
7272
});
73-
console.log("Python, Yaml, or Pipeline BRUIN asset detected");
7473
return;
7574
}
7675

@@ -124,7 +123,7 @@ export class BruinRender extends BruinCommand {
124123
}
125124
return JSON.stringify({ error: String(error) });
126125
}
127-
126+
128127
private async isBruinPipeline(filePath: string): Promise<boolean> {
129128
return await isBruinPipeline(filePath);
130129
}

src/extension/commands/parseAssetCommand.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { BruinLineageInternalParse } from "../../bruin/bruinFlowLineage";
66
import { BruinPanel } from "../../panels/BruinPanel";
77
import { EnhancedPipelineData, PipelineColumnInfo } from "../../types";
88
import { getCurrentPipelinePath } from "../../bruin/bruinUtils";
9+
import { BruinRender } from "../../bruin/bruinRender";
10+
import * as vscode from "vscode";
911
import * as path from "path";
1012
import * as fs from "fs";
1113

@@ -74,10 +76,20 @@ export const patchAssetCommand = async (body: object, lastRenderedDocumentUri: U
7476
""
7577
);
7678
const success = await patched.patchAsset(body, lastRenderedDocumentUri.fsPath);
77-
79+
7880
// After successful patch, re-parse the asset to update the webview
7981
if (success) {
8082
await parseAssetCommand(lastRenderedDocumentUri);
83+
84+
// For ingestr assets, also re-render to get updated rendered parameters
85+
const filePath = lastRenderedDocumentUri.fsPath;
86+
if (filePath.endsWith('.asset.yml') || filePath.endsWith('.asset.yaml')) {
87+
const renderer = new BruinRender(
88+
getBruinExecutablePath(),
89+
vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || ""
90+
);
91+
renderer.render(filePath);
92+
}
8193
}
8294
};
8395

src/ui-test/ingestr-asset-ui-integration.test.ts

Lines changed: 125 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,24 @@ const ensureSectionExpanded = async (driver: WebDriver): Promise<void> => {
7373
const startEditingField = async (driver: WebDriver, fieldId: string): Promise<void> => {
7474
// Ensure section is expanded first
7575
await ensureSectionExpanded(driver);
76-
76+
77+
// Enter edit mode first (new UI flow)
78+
await enterEditMode(driver);
79+
7780
const field = await findElementWithRetry(driver, By.id(fieldId), 10000);
78-
81+
7982
// Ensure field is visible and clickable
8083
assert(await field.isDisplayed(), `Field ${fieldId} should be visible`);
8184
assert(await field.isEnabled(), `Field ${fieldId} should be enabled`);
82-
85+
8386
// Scroll into view if needed (Windows compatibility)
8487
await driver.executeScript("arguments[0].scrollIntoView(true);", field);
8588
await sleep(500);
86-
89+
8790
// Click the field
8891
await field.click();
89-
console.log(`Clicked ${fieldId}, waiting for edit mode...`);
90-
92+
console.log(`Clicked ${fieldId}, waiting for input...`);
93+
9194
// Increased wait time for Windows compatibility - Vue reactivity needs time
9295
await sleep(2000);
9396
};
@@ -146,28 +149,81 @@ const testDropdownEditing = async (driver: WebDriver, fieldId: string, selectId:
146149
await exitEditMode(driver);
147150
};
148151

152+
// Helper function to enter edit mode by clicking the Edit button
153+
const enterEditMode = async (driver: WebDriver): Promise<void> => {
154+
console.log("Entering edit mode...");
155+
156+
// Find and click the Edit button in the header
157+
const editButtons = await driver.findElements(By.css('#ingestr-header button'));
158+
for (const button of editButtons) {
159+
const text = await button.getText();
160+
if (text === 'Edit') {
161+
await button.click();
162+
await sleep(1000);
163+
console.log("✓ Clicked Edit button");
164+
return;
165+
}
166+
}
167+
168+
// Fallback: try finding button by text content
169+
const allButtons = await driver.findElements(By.tagName('button'));
170+
for (const button of allButtons) {
171+
try {
172+
const text = await button.getText();
173+
if (text === 'Edit') {
174+
await button.click();
175+
await sleep(1000);
176+
console.log("✓ Clicked Edit button (fallback)");
177+
return;
178+
}
179+
} catch (e) {
180+
// Button might not be visible
181+
}
182+
}
183+
184+
console.log("⚠️ Edit button not found - might already be in edit mode");
185+
};
186+
187+
// Helper function to exit edit mode by clicking the Done button
188+
const exitEditModeWithButton = async (driver: WebDriver): Promise<void> => {
189+
console.log("Exiting edit mode...");
190+
191+
const buttons = await driver.findElements(By.css('#ingestr-header button'));
192+
for (const button of buttons) {
193+
const text = await button.getText();
194+
if (text === 'Done') {
195+
await button.click();
196+
await sleep(1000);
197+
console.log("✓ Clicked Done button");
198+
return;
199+
}
200+
}
201+
202+
console.log("⚠️ Done button not found");
203+
};
204+
149205
// Helper function to wait for the Ingestr component to be fully loaded
150206
const waitForIngestrComponent = async (driver: WebDriver): Promise<void> => {
151207
console.log("Waiting for Ingestr component to load...");
152-
208+
153209
// Wait for the main container
154210
await driver.wait(until.elementLocated(By.id("ingestr-asset-display")), 15000);
155211
console.log("✓ Ingestr asset display container found");
156-
212+
157213
// Wait for the section
158214
await driver.wait(until.elementLocated(By.id("ingestr-section")), 10000);
159215
console.log("✓ Ingestr section found");
160-
216+
161217
// Wait for the header
162218
await driver.wait(until.elementLocated(By.id("ingestr-header")), 10000);
163219
console.log("✓ Ingestr header found");
164-
220+
165221
// Ensure section is expanded
166222
await ensureSectionExpanded(driver);
167-
168-
// Wait for at least one field to be present
169-
await driver.wait(until.elementLocated(By.id("source-connection-field")), 10000);
170-
console.log("✓ Ingestr fields are loaded");
223+
224+
// Wait for ingestr content to be present (rendered view)
225+
await driver.wait(until.elementLocated(By.id("ingestr-content")), 10000);
226+
console.log("✓ Ingestr content is loaded");
171227
};
172228

173229
describe("Ingestr Asset Display Integration Tests", function () {
@@ -657,8 +713,14 @@ describe("Ingestr Asset Display Integration Tests", function () {
657713

658714
describe("Required Fields Display", function () {
659715
before(async function () {
660-
// Ensure section is expanded once for all tests in this group
716+
// Ensure section is expanded and enter edit mode for field tests
661717
await ensureSectionExpanded(driver);
718+
await enterEditMode(driver);
719+
});
720+
721+
after(async function () {
722+
// Exit edit mode after tests
723+
await exitEditModeWithButton(driver);
662724
});
663725

664726
it("should display source connection field with required indicator", async function () {
@@ -715,8 +777,14 @@ describe("Ingestr Asset Display Integration Tests", function () {
715777

716778
describe("Optional Fields Display", function () {
717779
before(async function () {
718-
// Ensure section is expanded once for all tests in this group
780+
// Ensure section is expanded and enter edit mode for field tests
719781
await ensureSectionExpanded(driver);
782+
await enterEditMode(driver);
783+
});
784+
785+
after(async function () {
786+
// Exit edit mode after tests
787+
await exitEditModeWithButton(driver);
720788
});
721789

722790
it("should display incremental strategy field without required indicator", async function () {
@@ -756,8 +824,14 @@ describe("Ingestr Asset Display Integration Tests", function () {
756824

757825
describe("Interactive Functionality", function () {
758826
before(async function () {
759-
// Ensure section is expanded once for all tests in this group
827+
// Ensure section is expanded and enter edit mode for field tests
760828
await ensureSectionExpanded(driver);
829+
await enterEditMode(driver);
830+
});
831+
832+
after(async function () {
833+
// Exit edit mode after tests
834+
await exitEditModeWithButton(driver);
761835
});
762836

763837
it("should be able to toggle section visibility", async function () {
@@ -817,8 +891,14 @@ describe("Ingestr Asset Display Integration Tests", function () {
817891

818892
describe("Field Editing Functionality", function () {
819893
before(async function () {
820-
// Ensure section is expanded once for all tests in this group
894+
// Ensure section is expanded and enter edit mode for field tests
821895
await ensureSectionExpanded(driver);
896+
await enterEditMode(driver);
897+
});
898+
899+
after(async function () {
900+
// Exit edit mode after tests
901+
await exitEditModeWithButton(driver);
822902
});
823903

824904
it("should allow editing source connection field", async function () {
@@ -998,16 +1078,27 @@ describe("Ingestr Asset Display Integration Tests", function () {
9981078

9991079
describe("Dropdown Functionality", function () {
10001080
before(async function () {
1001-
// Ensure section is expanded once for all tests in this group
1081+
// Ensure section is expanded and enter edit mode for field tests
10021082
await ensureSectionExpanded(driver);
1083+
await enterEditMode(driver);
10031084
});
10041085

1086+
after(async function () {
1087+
// Exit edit mode after tests
1088+
await exitEditModeWithButton(driver);
1089+
});
10051090
});
10061091

10071092
describe("Field Validation States", function () {
10081093
before(async function () {
1009-
// Ensure section is expanded once for all tests in this group
1094+
// Ensure section is expanded and enter edit mode for field tests
10101095
await ensureSectionExpanded(driver);
1096+
await enterEditMode(driver);
1097+
});
1098+
1099+
after(async function () {
1100+
// Exit edit mode after tests
1101+
await exitEditModeWithButton(driver);
10111102
});
10121103

10131104
it("should show error styling for empty required fields", async function () {
@@ -1053,8 +1144,14 @@ describe("Ingestr Asset Display Integration Tests", function () {
10531144

10541145
describe("Keyboard Interaction", function () {
10551146
before(async function () {
1056-
// Ensure section is expanded once for all tests in this group
1147+
// Ensure section is expanded and enter edit mode for field tests
10571148
await ensureSectionExpanded(driver);
1149+
await enterEditMode(driver);
1150+
});
1151+
1152+
after(async function () {
1153+
// Exit edit mode after tests
1154+
await exitEditModeWithButton(driver);
10581155
});
10591156

10601157
it("should handle ESC key to cancel editing", async function () {
@@ -1103,8 +1200,14 @@ describe("Ingestr Asset Display Integration Tests", function () {
11031200

11041201
describe("Field Value Display", function () {
11051202
before(async function () {
1106-
// Ensure section is expanded once for all tests in this group
1203+
// Ensure section is expanded and enter edit mode for field tests
11071204
await ensureSectionExpanded(driver);
1205+
await enterEditMode(driver);
1206+
});
1207+
1208+
after(async function () {
1209+
// Exit edit mode after tests
1210+
await exitEditModeWithButton(driver);
11081211
});
11091212

11101213
it("should handle empty optional fields gracefully", async function () {

webview-ui/src/App.vue

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ const handleMessage = (event: MessageEvent) => {
379379
case "asset-metadata-message":
380380
const metadataResult = updateValue(message, "success");
381381
const metadataError = updateValue(message, "error");
382-
382+
383383
if (metadataError) {
384384
assetMetadataError.value = typeof metadataError === 'string' ? metadataError : String(metadataError);
385385
assetMetadata.value = null;
@@ -411,11 +411,21 @@ const handleMessage = (event: MessageEvent) => {
411411
versionStatus.value = message.versionStatus;
412412
break;
413413
case "file-changed":
414-
lastRenderedDocument.value = message.filePath;
415-
// Clear any existing metadata when file changes to prevent stale data
416-
assetMetadata.value = null;
417-
assetMetadataError.value = null;
418-
console.log("🔍 [App.vue] File changed - cleared existing metadata");
414+
// Only clear metadata when file path actually changes (not just editor refocus)
415+
const previousFilePath = lastRenderedDocument.value;
416+
const newFilePath = message.filePath;
417+
const fileActuallyChanged = normalizePath(previousFilePath) !== normalizePath(newFilePath);
418+
419+
lastRenderedDocument.value = newFilePath;
420+
421+
if (fileActuallyChanged) {
422+
// Clear metadata only when switching to a different file
423+
assetMetadata.value = null;
424+
assetMetadataError.value = null;
425+
console.log("🔍 [App.vue] File changed to different file - cleared existing metadata");
426+
} else {
427+
console.log("🔍 [App.vue] Same file refocused - keeping existing metadata");
428+
}
419429
// Leave current content/tabs as-is for non-relevant files; simply exit settings-only
420430
settingsOnlyMode.value = false;
421431
// If current file is a .bruin.yml config, force Settings-only view (with Connections)

0 commit comments

Comments
 (0)