Skip to content

Commit 661664e

Browse files
mdesmetclaude
andauthored
feat: add DBT_LOOM_CONFIG_PATH environment variable support (#1761)
Co-authored-by: Claude <[email protected]>
1 parent 158fab7 commit 661664e

File tree

3 files changed

+160
-13
lines changed

3 files changed

+160
-13
lines changed

src/dbt_client/dbtCoreIntegration.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -280,18 +280,14 @@ export class DBTCoreProjectIntegration
280280
"DBTCoreProjectIntegration",
281281
`Registering dbt core project at ${this.projectRoot}`,
282282
);
283-
this.python = this.executionInfrastructure.createPythonBridge(
284-
this.projectRoot.fsPath,
285-
);
283+
this.python = this.createPythonBridge();
286284
this.executionInfrastructure.createQueue(
287285
DBTCoreProjectIntegration.QUEUE_ALL,
288286
);
289287

290288
this.disposables.push(
291289
this.pythonEnvironment.onPythonEnvironmentChanged(() => {
292-
this.python = this.executionInfrastructure.createPythonBridge(
293-
this.projectRoot.fsPath,
294-
);
290+
this.python = this.createPythonBridge();
295291
}),
296292
this.rebuildManifestDiagnostics,
297293
this.pythonBridgeDiagnostics,
@@ -409,6 +405,24 @@ export class DBTCoreProjectIntegration
409405
);
410406
}
411407

408+
private createPythonBridge(): PythonBridge {
409+
const env: { [key: string]: string | undefined } = {};
410+
411+
// Check if dbt_loom.config.yml exists and add it to environment
412+
const dbtLoomConfigPath = path.join(
413+
this.projectRoot.fsPath,
414+
"dbt_loom.config.yml",
415+
);
416+
if (existsSync(dbtLoomConfigPath)) {
417+
env.DBT_LOOM_CONFIG_PATH = dbtLoomConfigPath;
418+
}
419+
420+
return this.executionInfrastructure.createPythonBridge(
421+
this.projectRoot.fsPath,
422+
env,
423+
);
424+
}
425+
412426
async executeSQL(
413427
query: string,
414428
limit: number,
@@ -417,9 +431,7 @@ export class DBTCoreProjectIntegration
417431
this.throwBridgeErrorIfAvailable();
418432
const { limitQuery } = await this.getQuery(query, limit);
419433

420-
const queryThread = this.executionInfrastructure.createPythonBridge(
421-
this.projectRoot.fsPath,
422-
);
434+
const queryThread = this.createPythonBridge();
423435
return new QueryExecution(
424436
async () => {
425437
queryThread.kill(2);
@@ -1185,9 +1197,7 @@ export class DBTCoreProjectIntegration
11851197
configPath,
11861198
}: HealthcheckArgs): Promise<ProjectHealthcheck> {
11871199
this.throwBridgeErrorIfAvailable();
1188-
const healthCheckThread = this.executionInfrastructure.createPythonBridge(
1189-
this.projectRoot.fsPath,
1190-
);
1200+
const healthCheckThread = this.createPythonBridge();
11911201
try {
11921202
await this.createPythonDbtProject(healthCheckThread);
11931203
await healthCheckThread.ex`from dbt_healthcheck import *`;

src/dbt_client/dbtIntegration.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,10 @@ export class DBTCommandExecutionInfrastructure {
413413
private terminal: DBTTerminal,
414414
) {}
415415

416-
createPythonBridge(cwd: string): PythonBridge {
416+
createPythonBridge(
417+
cwd: string,
418+
env?: { [key: string]: string | undefined },
419+
): PythonBridge {
417420
let pythonPath = this.pythonEnvironment.pythonPath;
418421
const envVars = this.pythonEnvironment.environmentVariables;
419422

@@ -440,6 +443,7 @@ export class DBTCommandExecutionInfrastructure {
440443
python: pythonPath,
441444
cwd: cwd,
442445
env: {
446+
...env,
443447
...envVars,
444448
PYTHONPATH: __dirname,
445449
},

src/test/suite/dbtCoreIntegration.test.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Container } from "inversify";
77
import {
88
CLIDBTCommandExecutionStrategy,
99
PythonDBTCommandExecutionStrategy,
10+
DBTCommandExecutionInfrastructure,
1011
} from "../../dbt_client/dbtIntegration";
1112
import { TelemetryService } from "../../telemetry";
1213
import { DBTProjectContainer } from "../../manifest/dbtProjectContainer";
@@ -18,6 +19,9 @@ import {
1819
CommandProcessExecutionFactory,
1920
CommandProcessExecution,
2021
} from "../../commandProcessExecution";
22+
import * as fs from "fs";
23+
import * as path from "path";
24+
import * as os from "os";
2125

2226
// Mock workspace folders
2327
jest.mock("vscode", () => ({
@@ -256,3 +260,132 @@ describe("DBTCoreDetection Tests", () => {
256260
});
257261
});
258262
});
263+
264+
describe("DBTCoreProjectIntegration - dbt-loom config", () => {
265+
let mockExecutionInfrastructure: jest.Mocked<DBTCommandExecutionInfrastructure>;
266+
let mockTelemetry: jest.Mocked<TelemetryService>;
267+
let mockPythonBridge: jest.Mocked<any>;
268+
let tempDir: string;
269+
270+
beforeEach(() => {
271+
// Create a temporary directory for testing
272+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "dbt-test-"));
273+
274+
// Create mock dependencies
275+
mockPythonBridge = {
276+
lock: jest.fn(),
277+
ex: jest.fn(),
278+
connected: true,
279+
};
280+
281+
mockTelemetry = {
282+
sendTelemetryEvent: jest.fn(),
283+
sendTelemetryError: jest.fn(),
284+
setTelemetryCustomAttribute: jest.fn(),
285+
} as any;
286+
287+
mockExecutionInfrastructure = {
288+
createPythonBridge: jest.fn().mockReturnValue(mockPythonBridge),
289+
createQueue: jest.fn(),
290+
} as any;
291+
});
292+
293+
afterEach(() => {
294+
// Clean up temporary directory
295+
if (tempDir && fs.existsSync(tempDir)) {
296+
fs.rmSync(tempDir, { recursive: true, force: true });
297+
}
298+
jest.clearAllMocks();
299+
});
300+
301+
it("should set DBT_LOOM_CONFIG_PATH when dbt_loom.config.yml exists", () => {
302+
// Arrange
303+
const projectRoot = Uri.file(tempDir);
304+
const dbtLoomConfigPath = path.join(tempDir, "dbt_loom.config.yml");
305+
306+
// Create the dbt_loom.config.yml file
307+
fs.writeFileSync(dbtLoomConfigPath, "# Test config file");
308+
309+
const mockDiagnosticCollection =
310+
languages.createDiagnosticCollection("dbt-project-config");
311+
const mockPythonEnvChangeEmitter = new EventEmitter<Uri | undefined>();
312+
313+
// Act
314+
new DBTCoreProjectIntegration(
315+
mockExecutionInfrastructure as any,
316+
{
317+
onPythonEnvironmentChanged: mockPythonEnvChangeEmitter.event,
318+
pythonPath: "/usr/bin/python3",
319+
environmentVariables: {},
320+
} as any,
321+
mockTelemetry,
322+
{} as PythonDBTCommandExecutionStrategy,
323+
{} as (
324+
projectRoot: Uri,
325+
dbtPath: string,
326+
) => CLIDBTCommandExecutionStrategy,
327+
{} as DBTProjectContainer,
328+
{} as AltimateRequest,
329+
{
330+
debug: jest.fn(),
331+
error: jest.fn(),
332+
log: jest.fn(),
333+
} as any,
334+
{} as ValidationProvider,
335+
{} as DeferToProdService,
336+
projectRoot,
337+
mockDiagnosticCollection,
338+
);
339+
340+
// Assert
341+
expect(mockExecutionInfrastructure.createPythonBridge).toHaveBeenCalledWith(
342+
tempDir,
343+
{
344+
DBT_LOOM_CONFIG_PATH: dbtLoomConfigPath,
345+
},
346+
);
347+
});
348+
349+
it("should not set DBT_LOOM_CONFIG_PATH when dbt_loom.config.yml does not exist", () => {
350+
// Arrange
351+
const projectRoot = Uri.file(tempDir);
352+
// Explicitly NOT creating the dbt_loom.config.yml file
353+
354+
const mockDiagnosticCollection =
355+
languages.createDiagnosticCollection("dbt-project-config");
356+
const mockPythonEnvChangeEmitter = new EventEmitter<Uri | undefined>();
357+
358+
// Act
359+
new DBTCoreProjectIntegration(
360+
mockExecutionInfrastructure as any,
361+
{
362+
onPythonEnvironmentChanged: mockPythonEnvChangeEmitter.event,
363+
pythonPath: "/usr/bin/python3",
364+
environmentVariables: {},
365+
} as any,
366+
mockTelemetry,
367+
{} as PythonDBTCommandExecutionStrategy,
368+
{} as (
369+
projectRoot: Uri,
370+
dbtPath: string,
371+
) => CLIDBTCommandExecutionStrategy,
372+
{} as DBTProjectContainer,
373+
{} as AltimateRequest,
374+
{
375+
debug: jest.fn(),
376+
error: jest.fn(),
377+
log: jest.fn(),
378+
} as any,
379+
{} as ValidationProvider,
380+
{} as DeferToProdService,
381+
projectRoot,
382+
mockDiagnosticCollection,
383+
);
384+
385+
// Assert
386+
expect(mockExecutionInfrastructure.createPythonBridge).toHaveBeenCalledWith(
387+
tempDir,
388+
{}, // Empty env object when file doesn't exist
389+
);
390+
});
391+
});

0 commit comments

Comments
 (0)