Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dbtCoreCommandIntegation #1337

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft

Add dbtCoreCommandIntegation #1337

wants to merge 4 commits into from

Conversation

mdesmet
Copy link
Contributor

@mdesmet mdesmet commented Jul 28, 2024

Overview

Problem

Describe the problem you are solving. Mention the ticket/issue if applicable.

Solution

Describe the implemented solution. Add external references if needed.

Screenshot/Demo

A picture is worth a thousand words. Please highlight the changes if applicable.

How to test

  • Steps to be followed to verify the solution or code changes
  • Mention if there is any settings configuration added/changed/deleted

Checklist

  • I have run this code and it appears to resolve the stated issue
  • README.md updated and added information about my change

Summary by CodeRabbit

  • New Features

    • Enhanced DBT integration capabilities with new classes for core command detection and project integration.
    • Introduced a factory method for creating instances of the new DBT core command project integration.
  • Improvements

    • Expanded criteria for DBT integration modes for more flexible command execution.
    • Improved logic for retrieving configuration settings, enhancing clarity and usability.
  • Access Modifications

    • Elevated access levels for several methods in the DBT core project integration to enhance extensibility.
  • Dependency Injection

    • Added new bindings for the DBT core command project integration in the dependency injection configuration.

@anandgupta42
Copy link
Contributor

@coderabbitai full review

Copy link
Contributor

coderabbitai bot commented Sep 5, 2024

Actions performed

Full review triggered.

Copy link
Contributor

coderabbitai bot commented Sep 5, 2024

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes across various files enhance the functionality and integration capabilities of the DBT-related classes. Key modifications include adjustments to conditional logic for better matching, the introduction of new classes for DBT core command integration, changes in method visibility to improve extensibility, and updates to dependency injection configurations. Additionally, specific command execution methods were refined for improved flexibility.

Changes

File Path Change Summary
src/content_provider/sqlPreviewContentProvider.ts Modified conditional logic for dbtIntegrationMode to use startsWith.
src/dbtPowerUserExtension.ts Expanded criteria for newDbtIntegration to include "corecommand".
src/dbt_client/dbtCloudIntegration.ts Changed getDBTPath function visibility from private to public.
src/dbt_client/dbtCoreCommandIntegration.ts Introduced new classes for DBT core command integration with multiple asynchronous methods.
src/dbt_client/dbtCoreIntegration.ts Elevated access modifiers for several methods in DBTCoreProjectIntegration from private to protected.
src/dbt_client/dbtIntegration.ts Modified internal logic of dbtCommand method for better command-line argument formatting.
src/inversify.config.ts Added a new factory method for DBTCoreCommandProjectIntegration in the dependency injection configuration.
src/manifest/dbtProject.ts Introduced a factory method for DBTCoreCommandProjectIntegration and updated switch statement.
src/services/deferToProdService.ts Streamlined logic for retrieving configuration settings based on dbtIntegrationMode.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DBTCoreCommandIntegration
    participant DBTProject

    User->>DBTProject: Request DBT integration
    DBTProject->>DBTCoreCommandIntegration: Create instance using factory
    DBTCoreCommandIntegration->>DBTCoreCommandIntegration: Execute SQL queries
    DBTCoreCommandIntegration-->>User: Return results
Loading

Poem

🐰 In the garden where changes bloom,
New commands dance, dispelling gloom.
With each hop, the logic's bright,
DBT's magic takes flight!
A sprinkle of code, a dash of cheer,
Integration's here, let’s give a cheer! 🎉


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Outside diff range, codebase verification and nitpick comments (5)
src/content_provider/sqlPreviewContentProvider.ts (1)

Line range hint 49-66: Consider implementing additional event handling.

The provideTextDocumentContent method sets up a file system watcher and handles changes, but there is a TODO comment about potentially handling onDelete and onCreate events. It's important to consider implementing these to handle all aspects of file lifecycle management.

Would you like me to help implement the handling for onDelete and onCreate events, or should I open a GitHub issue to track this task?

src/dbt_client/dbtCloudIntegration.ts (4)

Line range hint 45-58: Visibility Change Approved for getDBTPath

The change in visibility from private to public for the getDBTPath function is approved as it enhances modularity by allowing access from other modules. However, it is recommended to add unit tests to ensure that the function behaves as expected with the new visibility.

Would you like me to help by writing some unit tests for this function?


Line range hint 60-117: Class Implementation Approved for DBTCloudDetection

The implementation of the DBTCloudDetection class is approved. It is well-structured and makes appropriate use of the getDBTPath function. However, consider improving the clarity and helpfulness of error messages to enhance user experience.


Line range hint 119-145: Class Implementation Approved for DBTCloudProjectDetection

The implementation of the DBTCloudProjectDetection class is approved. The logic for discovering and filtering projects is clear and efficient. Consider enhancing the warning message about performance to include more specific consequences and possible mitigations.


Line range hint 147-999: Class Implementation Approved for DBTCloudProjectIntegration

The implementation of the DBTCloudProjectIntegration class is approved. The class handles a wide range of functionalities effectively and demonstrates good resource management and error handling. Consider adding more inline documentation for complex methods to aid future maintainability.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 9a3c784 and a3c14ad.

Files selected for processing (9)
  • src/content_provider/sqlPreviewContentProvider.ts (1 hunks)
  • src/dbtPowerUserExtension.ts (1 hunks)
  • src/dbt_client/dbtCloudIntegration.ts (1 hunks)
  • src/dbt_client/dbtCoreCommandIntegration.ts (1 hunks)
  • src/dbt_client/dbtCoreIntegration.ts (3 hunks)
  • src/dbt_client/dbtIntegration.ts (1 hunks)
  • src/inversify.config.ts (3 hunks)
  • src/manifest/dbtProject.ts (3 hunks)
  • src/services/deferToProdService.ts (1 hunks)
Additional context used
Biome
src/dbt_client/dbtCoreCommandIntegration.ts

[error] 68-69: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 69-69: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 70-70: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 74-75: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 75-75: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 76-76: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 123-124: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 124-124: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 125-125: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 158-159: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 159-159: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 160-160: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 193-194: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 194-194: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 195-195: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 231-232: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 232-232: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 233-233: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 266-267: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 267-267: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

Additional comments not posted (14)
src/content_provider/sqlPreviewContentProvider.ts (3)

Line range hint 33-41: Proper resource management in dispose method.

The implementation of the dispose method correctly handles the disposal of resources, ensuring that all event emitters, subscriptions, and file system watchers are cleaned up. This is crucial for preventing memory leaks and ensuring the stability of the extension.


Line range hint 68-100: Robust error handling and telemetry in requestCompilation method.

The requestCompilation method is well-implemented with robust error handling and telemetry reporting. It handles different scenarios effectively, such as project loading delays and compilation errors, and ensures that these are reported back to the user clearly.


100-100: Review the updated conditional logic for dbtIntegrationMode.

The change from a strict equality check to startsWith allows for more flexible matching of dbtIntegrationMode values that begin with "core". This is a sensible update if there are multiple modes that share this prefix and need similar handling. However, it's important to ensure that this broader condition doesn't unintentionally match unwanted strings.

Verification successful

Verification successful: Isolated use of startsWith("core")

The change to use startsWith("core") for dbtIntegrationMode is isolated to src/content_provider/sqlPreviewContentProvider.ts. Other parts of the codebase use strict equality checks or switch statements, ensuring no unintended matches occur elsewhere. The update is specific to the logic in this file and does not impact other usages.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the new conditional logic does not unintentionally match unwanted strings.

# Test: Search for the configuration setting usage. Expect: Only valid configurations that start with 'core'.
rg --type typescript -A 5 $'dbtIntegrationMode.startsWith("core")'

Length of output: 102


Script:

#!/bin/bash
# Description: Verify that the new conditional logic does not unintentionally match unwanted strings.

# Test: Search for the configuration setting usage without specifying file type.
rg -A 5 'dbtIntegrationMode.startsWith("core")'

Length of output: 49


Script:

#!/bin/bash
# Description: Search for all occurrences of `dbtIntegrationMode` to understand its usage context.

rg 'dbtIntegrationMode'

Length of output: 2449

src/dbtPowerUserExtension.ts (1)

114-114: Review the conditional logic change for newDbtIntegration.

The addition of "corecommand" to the array of acceptable values for newDbtIntegration expands the scenarios under which the VS Code window will reload. This change seems to align with the PR's objective to enhance DBT integration capabilities. However, it's crucial to ensure that this new integration type is fully supported elsewhere in the application to avoid unexpected behavior.

  • Correctness: The logic correctly checks if the dbtIntegration value has changed and if the new value is among the specified acceptable values before triggering a reload.
  • Maintainability: The use of an array for checking acceptable values is a good practice as it simplifies adding or removing integration types in the future.
  • Performance: This change should not have a significant impact on performance since it only extends an existing condition.

However, it's recommended to verify that all parts of the application that interact with dbtIntegration are aware of and can handle the "corecommand" value appropriately.

Run the following script to verify the handling of the newDbtIntegration value throughout the codebase:

Verification successful

Verification Successful: Handling of newDbtIntegration is consistent.

The newDbtIntegration variable is only used within the src/dbtPowerUserExtension.ts file, and the addition of "corecommand" to the list of acceptable values is handled correctly in the conditional logic. There are no other occurrences of this variable in the codebase, indicating that the change is localized and does not affect other parts of the application.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify handling of the `newDbtIntegration` value throughout the codebase.

# Test: Search for the handling of `newDbtIntegration`. Expect: Consistent handling across the codebase.
rg --type ts -A 5 $'newDbtIntegration'

Length of output: 703

src/inversify.config.ts (3)

105-132: Well-implemented dependency injection for DBTCoreCommandProjectIntegration.

The new factory binding for DBTCoreCommandProjectIntegration is correctly set up and uses dependency injection effectively. This enhances modularity and testability by allowing dynamic resolution of dependencies at runtime.


201-201: Correct integration of the new factory into existing bindings.

The addition of DBTCoreCommandProjectIntegration to the DBTProject factory ensures that all components requiring this integration can access it seamlessly. This maintains consistency and functionality across the application.


Line range hint 1-201: Commendable structure and consistency in dependency management.

The file is well-organized and maintains a consistent approach to defining and using Inversify bindings. This structure enhances the maintainability and readability of the code, adhering to best practices in dependency management.

src/dbt_client/dbtCoreCommandIntegration.ts (1)

15-16: Review of Class Definitions

The classes DBTCoreCommandDetection, DBTCoreCommandProjectDetection, and DBTCoreCommandProjectIntegration are defined as singletons and extend their respective base classes. This design pattern is consistent and ensures that these classes can be easily managed and instantiated within the application.

Also applies to: 18-19, 21-22

src/dbt_client/dbtCoreIntegration.ts (3)

249-249: Review of access modifier change for python property.

The change from private to protected for the python property allows subclasses to access the PythonBridge instance directly. This change supports the intention to make the class more extensible, particularly for subclasses that might need direct access to Python functionalities.


794-794: Review of access modifier change for dbtCoreCommand method.

Changing the access modifier from private to protected for the dbtCoreCommand method enables subclasses to modify or extend the command execution logic. This is a positive change towards enhancing flexibility and extensibility in handling DBT commands in derived classes.


1034-1034: Review of access modifier change for throwBridgeErrorIfAvailable method.

The modification of the access modifier from private to protected for the throwBridgeErrorIfAvailable method facilitates enhanced error handling customization in subclasses. This change aligns with the goal of making the class more adaptable and robust by allowing derived classes to implement their own specific error handling mechanisms.

src/manifest/dbtProject.ts (3)

65-65: Approved: Import statement for DBTCoreCommandProjectIntegration.

The import statement correctly adds the new DBTCoreCommandProjectIntegration class from the appropriate module, which is necessary for the new integration functionality.


122-125: Approved: Declaration of dbtCoreCommandIntegrationFactory.

The factory method dbtCoreCommandIntegrationFactory is correctly declared as a private member of the DBTProject class. This method is crucial for creating instances of DBTCoreCommandProjectIntegration based on the provided path and diagnostics collection.


156-161: Approved: New case in switch statement for corecommand integration.

The addition of the "corecommand" case in the switch statement is well-implemented. It ensures that the new dbtCoreCommandIntegrationFactory is used to instantiate the DBTProjectIntegration when the dbtIntegration configuration is set to "corecommand".

Comment on lines +23 to +24
if (currentConfig[relativePath]) {
return currentConfig[relativePath];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Streamlined Configuration Retrieval Logic

The changes in the getDeferConfigByProjectRoot method simplify the retrieval process by removing the initial check for dbtIntegrationMode being "core". This makes the code more straightforward and easier to maintain. The new logic directly checks for the existence of a configuration for relativePath and then checks for the dbtIntegrationMode being "cloud". This approach is cleaner and reduces the number of conditional branches, which enhances readability and maintainability.

However, consider adding a comment explaining why the deferToProduction is set to true by default when dbtIntegrationMode is "cloud". This would help maintain clarity for future maintainers or developers unfamiliar with the business logic.

Also applies to: 28-32

Comment on lines +23 to +96
async executeSQL(
query: string,
limit: number,
modelName: string,
): Promise<QueryExecution> {
this.throwBridgeErrorIfAvailable();
const showCommand = this.dbtCoreCommand(
new DBTCommand("Running sql...", [
"show",
"--log-level",
"debug",
"--inline",
query,
"--limit",
limit.toString(),
"--output",
"json",
"--log-format",
"json",
]),
);
const cancellationTokenSource = new CancellationTokenSource();
showCommand.setToken(cancellationTokenSource.token);
return new QueryExecution(
async () => {
cancellationTokenSource.cancel();
},
async () => {
const { stdout, stderr } = await showCommand.execute(
cancellationTokenSource.token,
);
const exception = this.processJSONErrors(stderr);
if (exception) {
throw exception;
}
const parsedLines = stdout
.trim()
.split("\n")
.map((line) => {
try {
return JSON.parse(line.trim());
} catch (err) {}
});
const previewLine = parsedLines.filter(
(line) =>
line &&
line.hasOwnProperty("data") &&
line.data.hasOwnProperty("preview"),
);
const compiledSqlLines = parsedLines.filter(
(line) =>
line &&
line.hasOwnProperty("data") &&
line.data.hasOwnProperty("sql"),
);
const preview = JSON.parse(previewLine[0].data.preview);
const compiledSql =
compiledSqlLines[compiledSqlLines.length - 1].data.sql;
return {
table: {
column_names: preview.length > 0 ? Object.keys(preview[0]) : [],
column_types:
preview.length > 0
? Object.keys(preview[0]).map((obj: any) => "string")
: [],
rows: preview.map((obj: any) => Object.values(obj)),
},
compiled_sql: compiledSql,
raw_sql: query,
modelName,
};
},
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complex Method Review: executeSQL

This method is well-structured and handles SQL execution with detailed error handling and JSON parsing. However, there are several areas that could be improved for better readability and maintainability:

  1. Error Handling: The method uses a custom error processing function which could be more robust.
  2. JSON Parsing: The JSON parsing does not handle errors gracefully in all cases, which might lead to unhandled exceptions if the JSON is malformed.

Consider adding more comprehensive error handling around JSON parsing to ensure that all potential issues are gracefully managed.

Tools
Biome

[error] 68-69: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 69-69: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 70-70: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 74-75: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 75-75: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 76-76: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

Comment on lines +98 to +132
// internal commands
async unsafeCompileNode(modelName: string): Promise<string> {
this.throwBridgeErrorIfAvailable();
const compileQueryCommand = this.dbtCoreCommand(
new DBTCommand("Compiling model...", [
"compile",
"--model",
modelName,
"--output",
"json",
"--log-format",
"json",
]),
);
const { stdout, stderr } = await compileQueryCommand.execute();
const compiledLine = stdout
.trim()
.split("\n")
.map((line) => {
try {
return JSON.parse(line.trim());
} catch (err) {}
})
.filter(
(line) =>
line &&
line.hasOwnProperty("data") &&
line.data.hasOwnProperty("compiled"),
);
const exception = this.processJSONErrors(stderr);
if (exception) {
throw exception;
}
return compiledLine[0].data.compiled;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of Utility Methods

The methods unsafeCompileNode, unsafeCompileQuery, validateSQLDryRun, getColumnsOfSource, and getColumnsOfModel share a similar structure and functionality. They all execute DBT commands and parse the output. Here are some suggestions:

  1. Refactoring Opportunity: These methods have repeated code, especially in command execution and JSON parsing. Consider creating a helper function to handle these operations.
  2. Error Handling: Similar to executeSQL, the error handling around JSON parsing could be improved.

Refactoring these methods to use a shared utility function could reduce code duplication and improve error handling consistency across the board.

Also applies to: 134-167, 169-202, 204-240, 242-275

Tools
Biome

[error] 123-124: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 124-124: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 125-125: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

Comment on lines +277 to +310
private processJSONErrors(jsonErrors: string) {
if (!jsonErrors) {
return;
}
try {
const errorLines: string[] = [];
// eslint-disable-next-line prefer-spread
errorLines.push.apply(
errorLines,
jsonErrors
.trim()
.split("\n")
.map((line) => {
try {
return JSON.parse(line.trim());
} catch (err) {}
})
.filter(
(line) =>
line &&
line.hasOwnProperty("info") &&
line.info.hasOwnProperty("level") &&
(line.info.level === "error" || line.info.level === "fatal"),
)
.map((line) => line.info.msg),
);
if (errorLines.length) {
return new Error(errorLines.join(", "));
}
} catch (error) {
// ideally we never come here, this is a bug in our code
return new Error("Could not process " + jsonErrors + ": " + error);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error Processing Function: processJSONErrors

This function is crucial for handling JSON errors across various methods. It is well-implemented with considerations for different error levels. However, the use of push.apply is unconventional and could be simplified using modern JavaScript features like spread syntax.

Consider refactoring this to use simpler and more readable constructs.

Comment on lines +312 to +380
async getCatalog(): Promise<Catalog> {
this.throwBridgeErrorIfAvailable();
const bulkModelQuery = `
{% set result = [] %}
{% for n in graph.nodes.values() %}
{% if n.resource_type == "test" or
n.resource_type == "analysis" or
n.resource_type == "sql_operation" or
n.config.materialized == "ephemeral" %}
{% continue %}
{% endif %}
{% set columns = adapter.get_columns_in_relation(ref(n["name"])) %}
{% for column in columns %}
{% do result.append({
"table_database": n.database,
"table_schema": n.schema,
"table_name": n.name,
"column_name": column.name,
"column_type": column.dtype,
}) %}
{% endfor %}
{% endfor %}
{% for n in graph.sources.values() %}
{% set columns = adapter.get_columns_in_relation(source(n["source_name"], n["identifier"])) %}
{% for column in columns %}
{% do result.append({
"table_database": n.database,
"table_schema": n.schema,
"table_name": n.name,
"column_name": column.name,
"column_type": column.dtype,
}) %}
{% endfor %}
{% endfor %}
{{ tojson(result) }}`;

const compileQueryCommand = this.dbtCoreCommand(
new DBTCommand("Getting catalog...", [
"compile",
"--inline",
bulkModelQuery.trim().split("\n").join(""),
"--output",
"json",
"--log-format",
"json",
]),
);
const { stdout, stderr } = await compileQueryCommand.execute();
const compiledLine = stdout
.trim()
.split("\n")
.map((line) => {
try {
return JSON.parse(line.trim());
} catch (err) {}
})
.filter(
(line) =>
line &&
line.hasOwnProperty("data") &&
line.data.hasOwnProperty("compiled"),
);
const exception = this.processJSONErrors(stderr);
if (exception) {
throw exception;
}
const result: Catalog = JSON.parse(compiledLine[0].data.compiled);
return result;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method Review: getCatalog

The getCatalog method is complex and performs multiple operations including SQL compilation and JSON parsing. The method is critical for fetching catalog data but could benefit from the following enhancements:

  1. Code Clarity: The method's bulk SQL query is complex and spans multiple lines, which could be simplified for better readability.
  2. Error Handling: Enhance error handling around JSON parsing to prevent potential runtime errors.

Improving the readability and robustness of this method would greatly benefit the maintainability of the code.

Comment on lines +68 to +70
line &&
line.hasOwnProperty("data") &&
line.data.hasOwnProperty("preview"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static Analysis Tool Recommendations

The static analysis tool has identified several lines where the use of hasOwnProperty could be replaced with Object.hasOwn for better safety and modern practices. Additionally, it suggests using optional chaining to simplify the code.

Here are the recommended changes:

- line.hasOwnProperty("data") && line.data.hasOwnProperty("preview")
+ line?.data?.hasOwn("preview")

- line.hasOwnProperty("data") && line.data.hasOwnProperty("sql")
+ line?.data?.hasOwn("sql")

- line.hasOwnProperty("data") && line.data.hasOwnProperty("compiled")
+ line?.data?.hasOwn("compiled")

These changes will make the code safer and more in line with modern JavaScript practices.

Also applies to: 74-76, 123-125, 158-160, 193-195, 231-233, 266-268

Tools
Biome

[error] 68-69: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 69-69: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 70-70: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

@@ -169,7 +169,7 @@ export class PythonDBTCommandExecutionStrategy
}

private dbtCommand(args: string[]): string {
args = args.map((arg) => `r'${arg}'`);
args = args.map((arg) => `r"""${arg}"""`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential Security Risk: Ensure Proper Sanitization of Input

The change to use triple double quotes for argument formatting in the dbtCommand method enhances flexibility but also increases the risk of injection attacks. It's crucial to ensure that all inputs are properly sanitized before being used in this context.

Consider conducting a security review or audit to verify that the new argument handling does not introduce vulnerabilities. Here's a suggestion for improving input sanitization:

- args = args.map((arg) => `r"""${arg}"""`);
+ args = args.map((arg) => `r"""${arg.replace(/"/g, '\\"')}"""`);

This change ensures that double quotes within the arguments are escaped, reducing the risk of injection.

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
args = args.map((arg) => `r"""${arg}"""`);
args = args.map((arg) => `r"""${arg.replace(/"/g, '\\"')}"""`);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants