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

feat: lineage refactor #1212

Merged
merged 55 commits into from
Oct 28, 2024
Merged

feat: lineage refactor #1212

merged 55 commits into from
Oct 28, 2024

Conversation

saravmajestic
Copy link
Collaborator

@saravmajestic saravmajestic commented Jun 13, 2024

Overview

Problem

Extract lineage component for better maintainability

Solution

  • Created separate components for lineage

Screenshot/Demo

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

How to test

  • enable feature flag: dbt.enableLineageV2 as true in settings.json
  • verify lineage panel, sql visualizer
  • all functionalities should as existing

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

Release Notes

  • New Features

    • Introduced new LineageDemo component for rendering a GIF in the icon set.
    • Added a new .modal-content CSS class for improved styling in the webview.
    • New DemoButton component for displaying a demo modal with a close button.
    • Added LineageView component for managing lineage data display and interactions.
  • Improvements

    • Enhanced webview readiness checks and streamlined rendering processes in the NewLineagePanel and SQLLineagePanel.
    • Improved error handling in the QueryResultPanel during query execution.
    • Enhanced Chatbot component for better follow-up request handling.
  • Refactor

    • Refactored NewLineagePanel and SQLLineagePanel classes for better modularity and service integration.
    • Updated handling of SQL lineage details for clarity and consistency.
  • Bug Fixes

    • Fixed issues with command handling and response tracking in the webview components.

@saravmajestic saravmajestic self-assigned this Jun 13, 2024
window.addEventListener(
"message",
(
event: MessageEvent<{ command: string; args: Record<string, unknown> }>,

Check warning

Code scanning / CodeQL

Missing origin verification in `postMessage` handler Medium

Postmessage handler has no origin check.

Copilot Autofix AI about 2 months ago

To fix the problem, we need to verify the origin of the incoming messages in the postMessage handler. This involves checking the event.origin property against a list of trusted origins before processing the message.

  1. Identify the trusted origins for your application.
  2. Modify the postMessage handler to include a check for the event.origin.
  3. If the origin is trusted, proceed with the existing logic; otherwise, ignore the message or log a warning.
Suggested changeset 1
webview_panels/src/modules/lineage/LineageView.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/webview_panels/src/modules/lineage/LineageView.tsx b/webview_panels/src/modules/lineage/LineageView.tsx
--- a/webview_panels/src/modules/lineage/LineageView.tsx
+++ b/webview_panels/src/modules/lineage/LineageView.tsx
@@ -92,2 +92,3 @@
 
+    const trustedOrigins = ['https://www.example.com', 'https://another-trusted-origin.com'];
     window.addEventListener(
@@ -97,2 +98,6 @@
       ) => {
+        if (!trustedOrigins.includes(event.origin)) {
+          panelLogger.warn(`Untrusted origin: ${event.origin}`);
+          return;
+        }
         panelLogger.log("lineage:message -> ", JSON.stringify(event.data));
EOF
@@ -92,2 +92,3 @@

const trustedOrigins = ['https://www.example.com', 'https://another-trusted-origin.com'];
window.addEventListener(
@@ -97,2 +98,6 @@
) => {
if (!trustedOrigins.includes(event.origin)) {
panelLogger.warn(`Untrusted origin: ${event.origin}`);
return;
}
panelLogger.log("lineage:message -> ", JSON.stringify(event.data));
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
@@ -173,14 +173,6 @@ export class LineagePanel implements WebviewViewProvider, Disposable {
return;
}

if (command === "openURL") {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is handled via altimateWebviewProvider

@@ -188,17 +188,6 @@ export class QueryResultPanel extends AltimateWebviewProvider {
);
}

private async checkIfWebviewReady() {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

moved to altimateWebviewProvider

@saravmajestic saravmajestic marked this pull request as ready for review July 9, 2024 07:55
@saravmajestic saravmajestic changed the title feat: lineage v2 feat: lineage refactor Jul 9, 2024
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: 1

Outside diff range and nitpick comments (6)
webview_panels/src/lib/altimate/altimate-components.d.ts (1)

297-298: Remove unnecessary empty export

The empty export statement is unnecessary as there are other exports in the file. It can be safely removed to clean up the code.

Apply this diff to remove the empty export:

 declare type ViewsTypes = keyof typeof VIEWS_TYPE_COLOR;
-export { }
Tools
Biome

[error] 297-298: This empty export is useless because there's another export or import.

This import makes useless the empty export.

Safe fix: Remove this useless empty export.

(lint/complexity/noUselessEmptyExport)

src/webview_provider/newLineagePanel.ts (5)

74-117: LGTM: Class refactor improves modularity and dependency injection.

The NewLineagePanel class has been refactored to extend AltimateWebviewProvider and implement LineagePanelView. This change improves the class's modularity and allows for better separation of concerns. The constructor now uses dependency injection for various services, which is a good practice for maintainability and testability.

A few minor suggestions:

  1. Consider using TypeScript's parameter properties to reduce boilerplate in the constructor.
  2. The _disposables property is not visible in this snippet. Ensure it's properly initialized.

Consider refactoring the constructor to use parameter properties:

constructor(
  protected readonly dbtProjectContainer: DBTProjectContainer,
  private readonly altimate: AltimateRequest,
  protected readonly telemetry: TelemetryService,
  private readonly terminal: DBTTerminal,
  private readonly eventEmitterService: SharedStateService,
  protected readonly queryManifestService: QueryManifestService,
  protected readonly usersService: UsersService,
) {
  super(
    dbtProjectContainer,
    altimate,
    telemetry,
    eventEmitterService,
    terminal,
    queryManifestService,
    usersService,
  );

  // ... rest of the constructor code
}

Line range hint 165-327: Consider refactoring handleCommand for improved maintainability.

The handleCommand method has grown quite large and handles multiple responsibilities. While it's organized by command type, its length makes it harder to maintain and understand at a glance.

Suggestions for improvement:

  1. Consider splitting this method into smaller, more focused methods for each command type.
  2. Implement a command pattern or use a map of command handlers to reduce the number of if-else statements.
  3. Use early returns to reduce nesting and improve readability.

Here's a high-level example of how you might refactor this method:

private commandHandlers = {
  openProblemsTab: () => commands.executeCommand("workbench.action.problems.focus"),
  upstreamTables: async (args) => {
    const body = await this.getUpstreamTables(args.params);
    return { body, status: true };
  },
  // ... other command handlers
};

async handleCommand(message: { command: string; args: any; syncRequestId?: string }): Promise<void> {
  const { command, args = {}, syncRequestId } = message;
  const { id = syncRequestId, params } = args;

  const handler = this.commandHandlers[command];
  if (handler) {
    const result = await handler(args);
    this._panel?.webview.postMessage({
      command: "response",
      args: { id, syncRequestId, ...result },
    });
    return;
  }

  this.terminal.debug("newLineagePanel:handleCommand", "Unsupported command", message);
  super.handleCommand(message);
}

This refactoring would make the handleCommand method more concise and easier to maintain, as each command's logic would be encapsulated in its own function.


Line range hint 541-757: Refactor getConnectedColumns for improved readability and maintainability.

The getConnectedColumns method is complex and handles multiple responsibilities. While it seems to work as intended, its length and complexity make it difficult to understand and maintain.

Suggestions for improvement:

  1. Break down the method into smaller, more focused helper methods.
  2. Consider moving some of the logic (e.g., model fetching, SQL compilation) into separate services.
  3. Use more descriptive variable names to improve readability.
  4. Add more inline comments explaining the purpose of each section.

Here's a high-level example of how you might start refactoring this method:

private async getConnectedColumns(params: ConnectedColumnsParams) {
  const { event, project } = await this.validateAndGetEventAndProject();
  if (!event || !project) return;

  const modelInfos = await this.prepareModelInfos(params, event, project);
  if (this.cancellationTokenSource?.token.isCancellationRequested) {
    return { column_lineage: [] };
  }

  const request = this.buildColumnLevelLineageRequest(params, modelInfos, project);
  const result = await this.fetchColumnLevelLineage(request);

  return this.processColumnLevelLineageResult(result);
}

private async validateAndGetEventAndProject() {
  // ... implementation
}

private async prepareModelInfos(params: ConnectedColumnsParams, event: Event, project: Project) {
  // ... implementation
}

private buildColumnLevelLineageRequest(params: ConnectedColumnsParams, modelInfos: ModelInfo[], project: Project) {
  // ... implementation
}

private async fetchColumnLevelLineage(request: ColumnLevelLineageRequest) {
  // ... implementation
}

private processColumnLevelLineageResult(result: ColumnLevelLineageResult) {
  // ... implementation
}

This refactoring breaks down the large method into smaller, more focused methods, improving readability and maintainability.


Line range hint 981-994: LGTM: Good approach for gradual feature rollout.

The renderWebviewView method has been updated to support a new version of the lineage panel. This is a good approach for gradually rolling out new features while maintaining backward compatibility.

A minor suggestion for improvement:
Consider extracting the version check into a separate method for better readability and reusability.

Here's a suggested refactor:

private isV2Enabled = () =>
  workspace
    .getConfiguration("dbt")
    .get<boolean>("enableLineagePanelV2", false);

protected renderWebviewView(webview: Webview) {
  if (this.isV2Enabled()) {
    this.renderV2WebviewView(webview);
  } else {
    this.renderV1WebviewView(webview);
  }
}

private renderV2WebviewView(webview: Webview) {
  this._panel!.webview.html = super.getHtml(
    webview,
    this.dbtProjectContainer.extensionUri,
  );
}

private renderV1WebviewView(webview: Webview) {
  this._panel!.webview.options = <WebviewOptions>{ enableScripts: true };
  this._panel!.webview.html = getHtml(
    webview,
    this.dbtProjectContainer.extensionUri,
  );
}

This refactoring improves readability and makes it easier to maintain different versions of the webview rendering logic.


Line range hint 1-1048: Consider further refactoring for improved modularity and consistency.

While the individual methods have been improved, the overall file structure could benefit from further refactoring:

  1. The file is quite large. Consider splitting it into smaller, more focused modules.
  2. There's a mix of async/await and Promise-based code. Standardize on async/await for consistency.
  3. Remove or address TODO comments and commented-out code sections.
  4. Consider creating separate services for some of the complex logic (e.g., lineage calculation, webview rendering).

Here are some high-level suggestions for improving the overall architecture:

  1. Create a LineageService to handle the complex lineage calculation logic.
  2. Move webview-related code to a separate LineageWebviewProvider class.
  3. Use dependency injection consistently throughout the codebase.
  4. Create interfaces for the main classes to improve testability and maintainability.

Example structure:

// lineageService.ts
export class LineageService {
  // ... lineage calculation methods
}

// lineageWebviewProvider.ts
export class LineageWebviewProvider extends AltimateWebviewProvider {
  // ... webview-related methods
}

// newLineagePanel.ts
export class NewLineagePanel implements LineagePanelView {
  constructor(
    private lineageService: LineageService,
    private webviewProvider: LineageWebviewProvider,
    // ... other dependencies
  ) {}

  // ... simplified methods using the new services
}

This structure would improve modularity, testability, and maintainability of the codebase.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between f685076 and 67fd4f8.

Files selected for processing (3)
  • src/webview_provider/newLineagePanel.ts (24 hunks)
  • webview_panels/src/lib/altimate/altimate-components.d.ts (1 hunks)
  • webview_panels/src/lib/altimate/altimate-components.js (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • webview_panels/src/lib/altimate/altimate-components.js
Additional context used
Biome
webview_panels/src/lib/altimate/altimate-components.d.ts

[error] 9-19: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)


[error] 297-298: This empty export is useless because there's another export or import.

This import makes useless the empty export.

Safe fix: Remove this useless empty export.

(lint/complexity/noUselessEmptyExport)

Additional comments not posted (7)
webview_panels/src/lib/altimate/altimate-components.d.ts (6)

21-25: LGTM: CllEvents enum declaration

The CllEvents enum is well-structured and follows TypeScript conventions. It appropriately defines the events related to the CLL lifecycle (CANCEL, END, START).


27-27: LGTM: CodeBlock component props enhancement

The CodeBlock component has been updated with additional props (editorTheme and className) and refined type definitions. These changes improve the component's flexibility and customization options, aligning with modern React component patterns.

Also applies to: 177-186


29-293: LGTM: Comprehensive type definitions with new interfaces

The addition of numerous new interfaces (e.g., CollectColumn, Column, ColumnLineage, Columns, Confidence, Conversation, ConversationGroup, ExposureMetaData, LineageProviderProps, ModalArgs, OpNodeArgs, SelectedColumn, SqlLineage, StaticLineage, StaticLineageDetails) significantly enhances the type safety and readability of the codebase. These well-structured interfaces follow TypeScript best practices and provide clear type definitions for various data structures used in the application.


279-286: LGTM: VIEWS_TYPE_COLOR constant declaration

The VIEWS_TYPE_COLOR constant provides a centralized definition for view type colors, which can improve consistency across the application. The naming convention follows TypeScript standards for constants.


306-321: LGTM: Identifiers namespace declaration

The Identifiers namespace is a well-structured grouping of related constants. This approach effectively organizes and encapsulates values related to various aspects of the application, including themes, debugging, and remote URIs. It follows good TypeScript practices for constant management.


300-303: Verify usage of custom 'selectionend' event

The global HTMLElementEventMap has been extended to include a custom 'selectionend' event. This is a valid approach to declare custom events. However, it's important to ensure that this custom event is actually used and dispatched in the application.

To verify the usage of this custom event, you can run the following script:

This will help confirm that the custom event is being utilized in the application.

Verification successful

To address the issue with the previous search, please run the following script to verify the usage of the custom 'selectionend' event:


'selectionend' event usage verified

The custom selectionend event is actively used within the application as seen in the following file:

  • webview_panels/src/lib/altimate/DbtDocsRenderer.js
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage of 'selectionend' event in the codebase
rg --type typescript --type javascript "selectionend" -C 3

Length of output: 93


Script:

# Searching for 'selectionend' event usage in .ts and .js files
rg -e 'selectionend' --glob '*.ts' --glob '*.js' -C 3

Length of output: 1882

src/webview_provider/newLineagePanel.ts (1)

Line range hint 1-1048: Summary: Significant improvements with opportunities for further refinement.

The NewLineagePanel class has undergone substantial refactoring, resulting in improved modularity and functionality. Key improvements include:

  1. Extension of AltimateWebviewProvider and implementation of LineagePanelView.
  2. Improved dependency injection in the constructor.
  3. Support for a new version of the lineage panel.

While these changes are positive, there are opportunities for further improvement:

  1. Further break down large methods like handleCommand and getConnectedColumns.
  2. Standardize on async/await throughout the file.
  3. Consider splitting the file into smaller, more focused modules.
  4. Implement a more robust error handling strategy.

Overall, the changes represent a significant step forward in the codebase's evolution. Addressing the suggested improvements will further enhance the maintainability and scalability of the lineage functionality.

Comment on lines 9 to 19
export declare class CLL {
static isCancelled: boolean;
static inProgress: boolean;
static linkCount: number;
static onCancel(): void;
static cancel(): void;
static start(): void;
static end(): void;
static addLinks(n: number): void;
static showCllInProgressMsg(): void;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider refactoring the CLL class to a namespace or module

The CLL class contains only static members and methods, which is generally discouraged in TypeScript. To improve maintainability and adhere to best practices, consider refactoring this into a namespace or module with exported functions and variables.

Here's a suggested refactor:

export namespace CLL {
    export let isCancelled: boolean;
    export let inProgress: boolean;
    export let linkCount: number;
    export function onCancel(): void;
    export function cancel(): void;
    export function start(): void;
    export function end(): void;
    export function addLinks(n: number): void;
    export function showCllInProgressMsg(): void;
}

This approach maintains the same functionality while aligning with TypeScript best practices.

Tools
Biome

[error] 9-19: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

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: 3

🧹 Outside diff range and nitpick comments (2)
src/webview_provider/sqlLineagePanel.ts (1)

202-202: Use Consistent Node Type Strings

In assigning the type property for the node mapping, ensure that the string values match expected resource types. For example, "source" should match the resource type used elsewhere in the application to avoid inconsistencies.

src/webview_provider/newLineagePanel.ts (1)

165-171: Standardize the use of 'id' and 'syncRequestId'

In the handleCommand method, both id and syncRequestId are being used interchangeably. This can lead to confusion and potential bugs. Consider standardizing on a single identifier for request correlation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between ce8762d and d27a9b8.

📒 Files selected for processing (3)
  • src/commands/index.ts (1 hunks)
  • src/webview_provider/newLineagePanel.ts (23 hunks)
  • src/webview_provider/sqlLineagePanel.ts (6 hunks)
🧰 Additional context used
🪛 Biome
src/webview_provider/sqlLineagePanel.ts

[error] 220-220: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)


[error] 281-281: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 291-291: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 299-299: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (11)
src/webview_provider/sqlLineagePanel.ts (9)

38-40: Extending AltimateWebviewProvider Enhances Code Reusability

By extending AltimateWebviewProvider, the SQLLineagePanel class now inherits shared functionalities, promoting code reuse and improving maintainability as per the PR objectives.


47-63: Constructor Properly Injects Dependencies

The constructor correctly injects all necessary services and passes them to the superclass, ensuring that both the SQLLineagePanel and its superclass are properly initialized with required dependencies.


250-261: Refactored renderSqlVisualizer Method Improves Clarity

The renderSqlVisualizer method has been refactored for better clarity and efficiency. It streamlines the rendering process by directly posting messages to the webview after ensuring it's ready.


267-272: Enhanced Command Handling in handleCommand Method

The handleCommand method now uses a switch statement for cleaner and more maintainable command handling. The inclusion of syncRequestId supports synchronous communication when necessary.


Line range hint 193-225: Efficient Mapping of Nodes in nodeMapping

The updates to nodeMapping correctly associate node IDs and types, improving the accuracy of the lineage visualization by ensuring that nodes are properly identified and rendered.

🧰 Tools
🪛 Biome

[error] 220-220: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)


31-31: Updated Type Definition for details

The details property in the SQLLineage type has been updated to SqlLineageDetails, ensuring type correctness and better alignment with the data being handled.


15-15: Importing Necessary Types from ../altimate

The import statement now includes SqlLineageDetails, which is necessary for the updated details type, ensuring that all required types are available.


25-27: New Imports for Expanded Functionality

The added imports of AltimateWebviewProvider, SharedStateService, and UsersService indicate an expansion of functionality and integration with additional services, enhancing the capabilities of the SQLLineagePanel.


225-225: Consistent Use of Node Resource Types

Ensure that currNode.resource_type returns a string consistent with the expected values for node types. This consistency is crucial for correct rendering and interaction in the lineage visualization.

src/webview_provider/newLineagePanel.ts (2)

913-915: Good job addressing the previous null check issue

You've successfully added a null check before calling throwDiagnosticsErrorIfAvailable(), preventing potential runtime errors when getProject() returns undefined.


594-594: ⚠️ Potential issue

Ensure 'cancellationTokenSource' is initialized before use

In getConnectedColumns, this.cancellationTokenSource is used without checking if it's initialized. If this.cancellationTokenSource is undefined, accessing its token property will lead to a runtime error. Please ensure that this.cancellationTokenSource is properly initialized before accessing its token.

Apply this diff to add a null check:

-        this.cancellationTokenSource!.token,
+        this.cancellationTokenSource?.token || CancellationToken.None,

Likely invalid or redundant comment.

@@ -700,7 +700,7 @@ export class VSCodeCommands implements Disposable {
ViewColumn.Two,
{ retainContextWhenHidden: true, enableScripts: true },
);
this.sqlLineagePanel.resolveWebviewView(panel, lineage);
this.sqlLineagePanel.renderSqlVisualizer(panel, lineage);
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Incomplete method renaming detected.

The method resolveWebviewView still exists in multiple files, indicating that the renaming to renderSqlVisualizer was not fully propagated. To maintain code consistency and prevent potential issues, please ensure that all instances of resolveWebviewView are updated accordingly.

Affected files:

  • src/webview_provider/lineagePanel.ts
  • src/webview_provider/altimateWebviewProvider.ts
  • src/webview_provider/sqlLineagePanel.ts
  • src/webview_provider/queryResultPanel.ts
  • src/webview_provider/newDocsGenPanel.ts
  • src/webview_provider/docsEditPanel.ts
  • src/webview_provider/modelGraphViewPanel.ts
🔗 Analysis chain

Improved method naming for better clarity.

The renaming of resolveWebviewView to renderSqlVisualizer enhances code readability by more accurately describing the method's purpose. This change is a step in the right direction for maintaining clear and self-documenting code.

To ensure consistency across the codebase, let's verify if this method has been renamed in all relevant files:


Correction needed for file type specification in search scripts.

The initial shell scripts failed because rg does not recognize typescript as a valid file type. To resolve this, we'll use the correct file type identifier ts for TypeScript files.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining instances of 'resolveWebviewView' in TypeScript files
echo "Searching for 'resolveWebviewView':"
rg --type typescript 'resolveWebviewView'

# Search for occurrences of 'renderSqlVisualizer' to ensure consistent usage
echo "Searching for 'renderSqlVisualizer':"
rg --type typescript 'renderSqlVisualizer'

Length of output: 330


Script:

#!/bin/bash
# Search for any remaining instances of 'resolveWebviewView' in TypeScript files
echo "Searching for 'resolveWebviewView':"
rg --type ts 'resolveWebviewView'

# Search for occurrences of 'renderSqlVisualizer' to ensure consistent usage
echo "Searching for 'renderSqlVisualizer':"
rg --type ts 'renderSqlVisualizer'

Length of output: 1138

Comment on lines 980 to 987
protected renderWebviewView(webview: Webview) {
if (this.isV2Enabled()) {
this._panel!.webview.html = super.getHtml(
webview,
this.dbtProjectContainer.extensionUri,
);
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Set 'webview.options' consistently

In the renderWebviewView method, this._panel!.webview.options is only set when isV2Enabled() is false. For consistency and to avoid potential issues, consider setting webview.options regardless of the feature flag.

Apply this diff to set webview.options in both cases:

protected renderWebviewView(webview: Webview) {
+  this._panel!.webview.options = { enableScripts: true };
   if (this.isV2Enabled()) {
     this._panel!.webview.html = super.getHtml(
       webview,
       this.dbtProjectContainer.extensionUri,
     );
     return;
   }
-  this._panel!.webview.options = { enableScripts: true };
   this._panel!.webview.html = getHtml(
     webview,
     this.dbtProjectContainer.extensionUri,
   );
}
📝 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
protected renderWebviewView(webview: Webview) {
if (this.isV2Enabled()) {
this._panel!.webview.html = super.getHtml(
webview,
this.dbtProjectContainer.extensionUri,
);
return;
}
protected renderWebviewView(webview: Webview) {
this._panel!.webview.options = { enableScripts: true };
if (this.isV2Enabled()) {
this._panel!.webview.html = super.getHtml(
webview,
this.dbtProjectContainer.extensionUri,
);
return;
}
this._panel!.webview.html = getHtml(
webview,
this.dbtProjectContainer.extensionUri,
);
}

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: 2

🧹 Outside diff range and nitpick comments (2)
src/webview_provider/altimateWebviewProvider.ts (1)

641-648: LGTM: HTML template updates look good

The changes to the HTML template are well-implemented:

  1. Adding a class to the body based on viewPath allows for view-specific styling.
  2. The new "modal" div can be used for modal dialogs.
  3. The lineageGif variable makes the new asset available to the webview's JavaScript.

Minor suggestion: Consider using template literals for the class attribute to improve readability:

<body class="${this.viewPath.replace(/\//g, "")}">
src/altimate.ts (1)

Line range hint 936-940: LGTM! Consistent return type update.

The update of the return type from StaticLineageResponse to SqlLineageResponse is consistent with the earlier type renaming, maintaining type coherence throughout the file.

For complete consistency, consider updating the method name from sqlLineage to getSqlLineage or fetchSqlLineage to better align with other method names in the class (e.g., getColumnLevelLineage, fetchArtifactUrl).

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between d27a9b8 and 747de74.

📒 Files selected for processing (6)
  • src/altimate.ts (3 hunks)
  • src/webview_provider/altimateWebviewProvider.ts (3 hunks)
  • src/webview_provider/queryResultPanel.ts (0 hunks)
  • webview_panels/package.json (1 hunks)
  • webview_panels/src/assets/icons/index.tsx (2 hunks)
  • webview_panels/src/uiCore/theme.scss (1 hunks)
💤 Files with no reviewable changes (1)
  • src/webview_provider/queryResultPanel.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • webview_panels/package.json
🧰 Additional context used
🔇 Additional comments (7)
webview_panels/src/uiCore/theme.scss (3)

178-178: LGTM: Proper closure of CSS rule

The addition of the closing brace for the .offcanvas class is a good fix. It ensures that the CSS rule is properly structured and closed.


178-182: Summary: Minor styling updates

The changes in this file include properly closing the .offcanvas rule and adding a new .modal-content class. While these changes are consistent with good coding practices and the existing style, it would be helpful to understand how they specifically relate to the lineage refactoring mentioned in the PR objectives.

Could you provide more context on how these styling changes contribute to the overall goal of enhancing the maintainability of the lineage component?


180-182: LGTM: New modal content styling

The addition of the .modal-content class with a background color is consistent with the existing code style and use of CSS variables. This change aligns with the modifications mentioned in the AI-generated summary.

Could you please clarify the specific use case for this new modal content styling? It would be helpful to understand how it relates to the lineage refactoring mentioned in the PR objectives.

✅ Verification successful

To verify the connection between the new modal content styling and the lineage components mentioned in the PR objectives, we need to examine the contents of some key files. Let's run the following scripts to gather more information:


To further verify the connection between the new modal content styling and the lineage components, let's examine a few more files:


LGTM: Modal styling consistent with lineage components

The new .modal-content class, while not directly visible in the custom components, is likely part of the reactstrap library used in the Modals.tsx file. The modal components (Modal, SidebarModal, LineageModal) are being used in the context of lineage functionality, as seen in the Lineage.tsx file. This confirms that the new modal content styling is related to the lineage refactoring mentioned in the PR objectives.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for modal-related changes in other files

# Test: Look for modal-related changes in HTML templates
rg -i 'modal' --type html

# Test: Look for modal-related changes in TypeScript/JavaScript files
rg -i 'modal' --type ts --type js

Length of output: 257594


Script:

#!/bin/bash
# View contents of main.tsx
cat new_lineage_panel/src/main.tsx

# View contents of Modals.tsx
cat new_lineage_panel/src/Modals.tsx

# View contents of PageModal.tsx
cat new_lineage_panel/src/components/Modal/PageModal.tsx

Length of output: 5866


Script:

#!/bin/bash
# Check contents of PageModal.css
cat new_lineage_panel/src/components/Modal/PageModal.css

# Examine Lineage.tsx
cat new_lineage_panel/src/Lineage.tsx

# Search for other CSS files containing .modal-content
grep -r "\.modal-content" new_lineage_panel/src --include="*.css" --include="*.scss"

Length of output: 15878

webview_panels/src/assets/icons/index.tsx (1)

31-31: LGTM: New import statement for LineageGif.

The import statement for LineageGif is correctly placed and follows the existing pattern in the file.

src/webview_provider/altimateWebviewProvider.ts (1)

601-611: LGTM: New asset added correctly

The addition of the LineageGif constant follows the existing pattern for other assets and is implemented correctly.

src/altimate.ts (2)

Line range hint 82-93: LGTM! Improved type naming for better clarity.

The renaming of Details to SqlLineageDetails enhances code readability by making the type name more specific and descriptive of its purpose in the context of SQL lineage.


93-97: LGTM! Consistent type renaming and usage.

The renaming of StaticLineageResponse to SqlLineageResponse and the update of the details property to use SqlLineageDetails maintain consistency with the previous change. This improves the overall clarity and coherence of the type system related to SQL lineage.

Comment on lines +137 to +143
export const LineageDemo = (): JSX.Element => (
<img
// @ts-expect-error added in altimateWebViewProvider
src={(window.lineageGif as string) ?? LineageGif}
alt="Lineage demo"
/>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Review the LineageDemo component implementation.

The LineageDemo component is well-structured and consistent with other components in the file. However, there are a couple of points that need attention:

  1. The TypeScript error suppression comment (@ts-expect-error) might hide underlying type issues. Consider addressing the root cause instead of suppressing the error.
  2. The use of window.lineageGif assumes it's defined somewhere else, which could lead to runtime errors if not properly managed. Ensure that this global variable is consistently set where needed.

To address these issues, consider refactoring the component as follows:

export const LineageDemo = (): JSX.Element => {
  const gifSrc = typeof window !== 'undefined' && 'lineageGif' in window
    ? (window as any).lineageGif as string
    : LineageGif;

  return <img src={gifSrc} alt="Lineage demo" />;
};

This approach type-checks the window object and uses a type assertion only when necessary, reducing the risk of runtime errors.

Note: While a similar comment was made in a previous review, this new comment is necessary as the issues haven't been fully addressed in the current implementation.

Comment on lines +527 to +536
protected async checkIfWebviewReady() {
return new Promise<void>((resolve) => {
const interval = setInterval(() => {
if (this.isWebviewReady) {
clearInterval(interval);
resolve();
}
}, 500);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider optimizing the webview ready check

The checkIfWebviewReady method provides a way to asynchronously wait for the webview to be ready, which is good. However, there are a few potential improvements:

  1. Instead of using setInterval, consider using setTimeout with a recursive approach. This would be more efficient, especially if the webview takes a while to be ready.
  2. Add a timeout mechanism to prevent an infinite loop if isWebviewReady never becomes true.

Here's a suggested implementation:

protected async checkIfWebviewReady(timeout = 30000): Promise<void> {
  return new Promise<void>((resolve, reject) => {
    const startTime = Date.now();
    const check = () => {
      if (this.isWebviewReady) {
        resolve();
      } else if (Date.now() - startTime > timeout) {
        reject(new Error('Timeout waiting for webview to be ready'));
      } else {
        setTimeout(check, 500);
      }
    };
    check();
  });
}

This implementation will reject the promise if the webview isn't ready within the specified timeout (default 30 seconds).

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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 747de74 and f2cdd96.

📒 Files selected for processing (1)
  • webview_panels/src/modules/lineage/components/demo/DemoButton.tsx (1 hunks)
🧰 Additional context used
🔇 Additional comments (5)
webview_panels/src/modules/lineage/components/demo/DemoButton.tsx (5)

1-5: LGTM: Import statements are well-organized and necessary.

The import statements are logically ordered and include all the necessary dependencies for the component's functionality.


7-46: LGTM: Component structure follows React best practices.

The DemoButton component is well-structured as a functional component. It effectively uses React hooks for state management and implements conditional rendering correctly.


11-16: Implement the suggested timeout cleanup to prevent memory leaks.

As mentioned in a previous review comment, it's important to clear the timeout when the component unmounts to prevent potential memory leaks. Please implement the suggested change:

useEffect(() => {
  // hide demo button after 10s
  const timer = setTimeout(() => {
    setShowDemoButton(false);
  }, 10000);

  // Clear the timeout when the component unmounts
  return () => clearTimeout(timer);
}, []);

This ensures proper cleanup if the component unmounts before the 10-second delay elapses.


31-43: LGTM: Button implementation is correct and follows good practices.

The conditional rendering of the Button component is well-implemented. The use of stopPropagation() in the onClick handler is appropriate to prevent event bubbling. The button's content and styling are clear and consistent with the component's purpose.


1-48: Overall, well-implemented component with minor suggestions for improvement.

The DemoButton component is well-structured and follows React best practices. The suggested improvements (timeout cleanup and close button accessibility) are minor and once addressed, will further enhance the component's quality and robustness.

Comment on lines +20 to +30
<Modal
isOpen={showDemoModal}
close={() => setShowDemoModal(false)}
fullscreen
className={styles.demoModal}
>
<Demo />
<div className="close-btn" onClick={() => setShowDemoModal(false)}>
<CloseIcon />
</div>
</Modal>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance accessibility of the close button in the Modal.

While the Modal implementation is generally correct, the close button could be improved for better accessibility. Consider replacing the div with a proper button element:

<button
  className="close-btn"
  onClick={() => setShowDemoModal(false)}
  aria-label="Close demo modal"
>
  <CloseIcon />
</button>

This change improves keyboard accessibility and provides better semantic meaning to screen readers.

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: 0

🧹 Outside diff range and nitpick comments (2)
src/webview_provider/newLineagePanel.ts (2)

594-596: Proper error handling for cancellation

The code now checks for cancellation using this.cancellationTokenSource!.token, which is a good practice. However, the use of the non-null assertion operator (!) might be risky if cancellationTokenSource could be undefined.

Consider adding a null check before using cancellationTokenSource to avoid potential runtime errors:

if (this.cancellationTokenSource?.token) {
  // Use the token
} else {
  // Handle the case where cancellationTokenSource is undefined
}

913-915: Improved error handling in getMissingLineageMessage

The code now uses optional chaining (?.) when calling throwDiagnosticsErrorIfAvailable(), which is safer than the previous implementation. However, there's still room for improvement.

Consider using nullish coalescing to provide a default error message:

try {
  this.queryManifestService.getProject()?.throwDiagnosticsErrorIfAvailable();
} catch (err) {
  return { message: (err as Error).message ?? "Unknown error occurred", type: "error" };
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f2cdd96 and a28d423.

📒 Files selected for processing (1)
  • src/webview_provider/newLineagePanel.ts (24 hunks)
🧰 Additional context used
🔇 Additional comments (9)
src/webview_provider/newLineagePanel.ts (9)

74-117: Improved class structure and dependency injection

The NewLineagePanel class has been refactored to extend AltimateWebviewProvider and implement LineagePanelView. The constructor now accepts more dependencies, improving modularity and testability. The addition of a configuration change listener is a good practice for keeping the panel up-to-date with user settings.


165-171: Enhanced command handling with sync request support

The handleCommand method signature has been updated to include an optional syncRequestId. This allows for better tracking and correlation of asynchronous operations, improving the overall reliability of the command handling system.


181-181: Consistent use of syncRequestId in response messages

The syncRequestId is now consistently included in the response messages sent back to the webview. This improvement ensures that the frontend can accurately match responses to their corresponding requests, enhancing the reliability of the communication between the extension and the webview.

Also applies to: 190-190, 199-199, 208-208, 218-218, 247-247, 252-252


327-327: Proper delegation to parent class

The handleCommand method now correctly delegates to the parent class implementation using super.handleCommand(message) for unsupported commands. This ensures that any common command handling logic in the parent class is not bypassed.


576-579: Improved usage of event object

The code now correctly uses event.event when calling DBTProject.getNonEphemeralParents, ensuring that the correct data is passed to the method. This change aligns with the updated structure of the event object.

Also applies to: 582-583


619-621: Efficient bulk compilation of SQL

The code now uses project.getBulkCompiledSql to compile multiple SQL models at once, which is likely more efficient than compiling them individually. This is a good optimization for performance.


786-788: Improved null handling in createTable method

The code now properly checks if _node is undefined before attempting to use it, preventing potential runtime errors. This is a good defensive programming practice.


975-978: New method for checking if V2 is enabled

The addition of the isV2Enabled method is a good practice for encapsulating the configuration check. This makes it easier to update the condition in the future if needed.


Line range hint 980-992: Improved renderWebviewView method

The renderWebviewView method now checks if V2 is enabled and renders the appropriate HTML. This is a good way to handle feature flags and gradually roll out new features.

To ensure that the V2 rendering is working as expected, we should verify that the configuration is being read correctly. Run the following script to check the configuration:

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: 1

🧹 Outside diff range and nitpick comments (2)
src/webview_provider/newLineagePanel.ts (2)

Line range hint 149-311: Improved command handling with suggestion for further refactoring

The updates to the handleCommand method, including the addition of syncRequestId for better response tracking, are good improvements. However, the method is handling many different commands, which could make it harder to maintain as the number of commands grows.

Consider refactoring this method to use a command pattern or a map of command handlers to improve maintainability and make it easier to add new commands in the future.


Line range hint 524-740: Consider refactoring getConnectedColumns for improved maintainability

The getConnectedColumns method is quite long and complex, handling multiple responsibilities. Consider breaking it down into smaller, more focused methods to improve readability and maintainability. This would make the code easier to understand, test, and modify in the future.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a28d423 and bbd65e4.

📒 Files selected for processing (1)
  • src/webview_provider/newLineagePanel.ts (23 hunks)
🧰 Additional context used
🔇 Additional comments (2)
src/webview_provider/newLineagePanel.ts (2)

73-101: Improved class structure and dependency injection

The refactoring of NewLineagePanel to extend AltimateWebviewProvider and the addition of new dependencies in the constructor enhance the modularity and maintainability of the code. This change aligns with best practices for dependency injection and separation of concerns.


959-963: Simplified webview rendering

The new renderWebviewView method effectively leverages the superclass implementation, promoting code reuse and maintaining a clear separation of concerns. This change simplifies the rendering process and makes the code more maintainable.

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: 2

🧹 Outside diff range and nitpick comments (3)
src/webview_provider/newLineagePanel.ts (3)

84-101: LGTM! Improved dependency injection. Consider using parameter properties for conciseness.

The updated constructor properly injects the required services, which is excellent for managing dependencies and improving testability.

For improved readability and conciseness, consider using TypeScript's parameter properties. This can reduce boilerplate code. For example:

constructor(
  protected readonly dbtProjectContainer: DBTProjectContainer,
  private readonly altimate: AltimateRequest,
  protected readonly telemetry: TelemetryService,
  private readonly terminal: DBTTerminal,
  protected readonly eventEmitterService: SharedStateService,
  protected readonly queryManifestService: QueryManifestService,
  protected readonly usersService: UsersService
) {
  super(
    dbtProjectContainer,
    altimate,
    telemetry,
    eventEmitterService,
    terminal,
    queryManifestService,
    usersService
  );
}

This change would eliminate the need for separate property declarations.


149-155: LGTM! Improved command handling with sync request tracking. Consider adding a default error handler.

The changes to the handleCommand method are good:

  1. The addition of syncRequestId improves request-response tracking.
  2. The call to super.handleCommand(message) ensures proper inheritance behavior.

Consider adding a default error handler for unhandled commands before calling the super method. This could improve error reporting and debugging. For example:

if (!handled) {
  console.warn(`Unhandled command: ${command}`);
  // Optionally, send an error response to the webview
  this._panel?.webview.postMessage({
    command: "response",
    args: { id, syncRequestId, error: "Unhandled command", status: false },
  });
}
super.handleCommand(message);

This addition would help catch and report any commands that aren't explicitly handled in this class or its superclass.

Also applies to: 165-165, 174-174, 183-183, 192-192, 202-202, 231-231, 236-236, 277-277, 298-298, 311-311


897-899: LGTM! Improved null safety. Consider enhancing error handling.

The changes in getMissingLineageMessage are good:

  1. The use of queryManifestService is consistent with the overall refactoring.
  2. The null-safe call operator ?. prevents potential null reference errors.

Consider enhancing the error handling to provide more specific error messages. For example:

try {
  const project = this.queryManifestService.getProject();
  if (!project) {
    return { message: "No active dbt project found", type: "error" };
  }
  project.throwDiagnosticsErrorIfAvailable();
} catch (err) {
  return { message: `Error in dbt project: ${(err as Error).message}`, type: "error" };
}

This change would provide more context in the error message, making it easier for users to understand and troubleshoot issues.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between bbd65e4 and 3bcb5ae.

📒 Files selected for processing (1)
  • src/webview_provider/newLineagePanel.ts (24 hunks)
🧰 Additional context used
🔇 Additional comments (3)
src/webview_provider/newLineagePanel.ts (3)

32-35: LGTM! Improved modularity and service-oriented architecture.

The changes to the import statements and class declaration show a shift towards a more modular and service-oriented architecture. This is a positive change as it:

  1. Improves maintainability by separating concerns.
  2. Enhances testability by allowing easier mocking of dependencies.
  3. Promotes code reuse through the use of shared services.

Also applies to: 73-76


Line range hint 1-963: Overall, great improvements to modularity and maintainability!

The refactoring of NewLineagePanel represents a significant improvement to the codebase:

  1. The shift towards a service-oriented architecture enhances modularity and testability.
  2. Improved error handling and null checks increase robustness.
  3. Consistent use of queryManifestService centralizes data retrieval logic.

While there are some areas for potential further improvement (as noted in previous comments), these changes lay a solid foundation for easier maintenance and future enhancements.


959-963: LGTM! Simplified rendering. Verify webview script execution.

The simplification of renderWebviewView is good:

  1. It reduces code duplication by using the superclass's getHtml method.
  2. It makes the code more maintainable and consistent with the base class implementation.

Please verify that removing the webview.options = { enableScripts: true } setting doesn't affect the functionality of the webview. If scripts are required in the webview, ensure that this setting is properly handled in the superclass or add it back if necessary.

Run the following script to check if enableScripts is set in the superclass:

If the script doesn't find any results, consider adding the enableScripts option back to this method.

✅ Verification successful

Verified: enableScripts is enabled in the superclass.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if enableScripts is set in AltimateWebviewProvider

# Test: Search for enableScripts in AltimateWebviewProvider
rg -A 5 'enableScripts' src/webview_provider/altimateWebviewProvider.ts

Length of output: 253

Comment on lines +378 to +379
const event = this.queryManifestService.getEventByCurrentProject();
if (!event?.event) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider abstracting repeated null checks into a helper method.

The use of queryManifestService to retrieve project and event data is a good practice. However, the repeated null checks for event and project in multiple methods introduce code duplication.

Consider creating a helper method to perform these checks, which would reduce code duplication and improve maintainability. For example:

private getEventAndProject(): { event: ManifestCacheProjectAddedEvent['event'], project: DBTProject } | undefined {
  const event = this.queryManifestService.getEventByCurrentProject();
  if (!event?.event) {
    return undefined;
  }
  const project = this.queryManifestService.getProject();
  if (!project) {
    return undefined;
  }
  return { event: event.event, project };
}

You can then use this helper method in place of the repeated checks throughout the class:

const eventAndProject = this.getEventAndProject();
if (!eventAndProject) {
  return;
}
const { event, project } = eventAndProject;

This refactoring would make the code more DRY and easier to maintain.

Also applies to: 382-382, 412-413, 416-416, 541-542, 545-545

Comment on lines +758 to +759
const event = this.queryManifestService.getEventByCurrentProject();
if (!event?.event) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider further simplification of table-related methods.

The changes to use queryManifestService in getConnectedTables and createTable are consistent with the overall refactoring. However, these methods still contain complex logic that could potentially be simplified.

Consider the following improvements:

  1. Extract the logic for creating different types of tables (source, metric, exposure, etc.) into separate methods or a factory class. This would make the createTable method more readable and easier to maintain.

  2. Use early returns in getConnectedTables to reduce nesting and improve readability. For example:

private getConnectedTables(key: keyof GraphMetaMap, table: string): Table[] | undefined {
  const event = this.queryManifestService.getEventByCurrentProject();
  if (!event?.event) return;

  const { graphMetaMap } = event.event;
  const dependencyNodes = graphMetaMap[key];
  const node = dependencyNodes.get(table);
  if (!node) return;

  const tables = new Map<string, Table>();
  node.nodes.forEach(({ url, key }) => {
    const _node = this.createTable(event.event!, url, key);
    if (_node && !tables.has(_node.table)) {
      tables.set(_node.table, _node);
    }
  });

  return Array.from(tables.values()).sort((a, b) => a.table.localeCompare(b.table));
}

These changes would improve the overall structure and readability of the code.

Also applies to: 762-762, 770-772

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: 4

🧹 Outside diff range and nitpick comments (1)
webview_panels/src/modules/lineage/LineageView.tsx (1)

38-38: Remove debugging log statements

There are log statements with the message "thisisiit" that appear to be left over from debugging. To maintain clean and readable code, consider removing or updating these log statements.

Apply this diff to clean up the log statements:

- panelLogger.log("thisisiit", "render");
- panelLogger.log("thisisiit", "ApiHelper.get", url, data);
- panelLogger.log("thisisiit", renderNode);

Also applies to: 48-48, 122-122

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 3bcb5ae and f182728.

📒 Files selected for processing (1)
  • webview_panels/src/modules/lineage/LineageView.tsx (1 hunks)
🧰 Additional context used
🪛 GitHub Check: CodeQL
webview_panels/src/modules/lineage/LineageView.tsx

[warning] 98-98: Missing origin verification in postMessage handler
Postmessage handler has no origin check.

}

// const lineageType = renderNode.details ? "sql" : "dynamic";
const lineageType = "dynamic" as "dynamic" | "sql";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Determine lineage type dynamically instead of hardcoding

Currently, lineageType is hardcoded to "dynamic". To ensure the component behaves correctly in all scenarios, determine the lineage type based on the available data.

Apply this diff to set lineageType dynamically:

- const lineageType = "dynamic" as "dynamic" | "sql";
+ const lineageType = renderNode.details ? "sql" : "dynamic";

This change uses the presence of renderNode.details to decide the lineage type.

Committable suggestion was skipped due to low confidence.

Comment on lines 100 to 103
if (!event.origin.startsWith("vscode-webview://")) {
panelLogger.debug("invalid message ", event);
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Improve origin verification in message event handler

The current origin check using event.origin.startsWith("vscode-webview://") may not be reliable, as event.origin can be an empty string in VSCode webviews. Consider verifying the message source by checking event.source or implementing a more robust message validation mechanism.

Apply this diff to enhance security:

- if (!event.origin.startsWith("vscode-webview://")) {
+ if (event.source !== window.parent) {
    panelLogger.debug("invalid message ", event);
    return;
}

Ensure that event.source correctly identifies messages from the expected source.

📝 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
if (!event.origin.startsWith("vscode-webview://")) {
panelLogger.debug("invalid message ", event);
return;
}
if (event.source !== window.parent) {
panelLogger.debug("invalid message ", event);
return;
}

Comment on lines 46 to 77
// @ts-expect-error TODO: add type generic for executeRequestInSync
ApiHelper.get = async (url: string, data?: Record<string, unknown>) => {
panelLogger.log("thisisiit", "ApiHelper.get", url, data);
switch (url) {
case "init": {
const event = new CustomEvent("renderStartNode", {
detail: _data,
});
document.dispatchEvent(event);
return;
}
case "upstreamTables":
case "downstreamTables":
case "getExposureDetails":
case "getColumns":
case "getConnectedColumns":
case "sendFeedback":
case "getLineageSettings":
case "persistLineageSettings":
case "openFile":
case "openChat":
case "showInfoNotification":
case "previewFeature":
case "telemetryEvents":
return executeRequestInSync(url, { args: { params: data ?? {} } });
case "columnLineage":
return executeRequestInSync(url, { args: data });

default:
break;
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid overwriting imported module methods directly

Overwriting ApiHelper.get directly can lead to unexpected behavior and makes the code harder to maintain and test. Consider using dependency injection or a wrapper function to achieve the desired behavior without modifying the imported module.

Apply this diff to refactor the code:

- // @ts-expect-error TODO: add type generic for executeRequestInSync
- ApiHelper.get = async (url: string, data?: Record<string, unknown>) => {
-   // Implementation
- };
+ // Consider creating a custom API helper or passing necessary functions as props

Committable suggestion was skipped due to low confidence.

Comment on lines 87 to 121
useEffect(() => {
const commandMap = {
render,
columnLineage: (data: { event: CllEvents }) => {
columnLineage(data);
},
};

window.addEventListener(
"message",
(
event: MessageEvent<{ command: string; args: Record<string, unknown> }>,
) => {
if (!event.origin.startsWith("vscode-webview://")) {
panelLogger.debug("invalid message ", event);
return;
}

panelLogger.log("lineage:message -> ", JSON.stringify(event.data));
const { command, args } = event.data;

if (command in commandMap) {
(
commandMap[command as keyof typeof commandMap] as (
args: Record<string, unknown>,
) => void
)(args);
}
},
);

panelLogger.info("lineage:onload");
document.documentElement.classList.add(styles.lineageBody);
executeRequestInAsync("init", {});
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add cleanup for event listeners in useEffect

The useEffect hook adds an event listener to the window but does not remove it upon component unmounting, which can lead to memory leaks or unwanted behavior. Return a cleanup function to remove the event listener when the component unmounts.

Apply this diff to ensure proper cleanup:

  useEffect(() => {
    const commandMap = {
      render,
      columnLineage: (data: { event: CllEvents }) => {
        columnLineage(data);
      },
    };

+   const messageHandler = (
+     event: MessageEvent<{ command: string; args: Record<string, unknown> }>,
+   ) => {
+     // Existing event handler code
+   };
+
-   window.addEventListener(
-     "message",
-     (
-       event: MessageEvent<{ command: string; args: Record<string, unknown> }>,
-     ) => {
-       // Existing event handler code
-     },
-   );
+   window.addEventListener("message", messageHandler);

    panelLogger.info("lineage:onload");
    document.documentElement.classList.add(styles.lineageBody);
    executeRequestInAsync("init", {});

+   return () => {
+     window.removeEventListener("message", messageHandler);
+   };
  }, []);
📝 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
useEffect(() => {
const commandMap = {
render,
columnLineage: (data: { event: CllEvents }) => {
columnLineage(data);
},
};
window.addEventListener(
"message",
(
event: MessageEvent<{ command: string; args: Record<string, unknown> }>,
) => {
if (!event.origin.startsWith("vscode-webview://")) {
panelLogger.debug("invalid message ", event);
return;
}
panelLogger.log("lineage:message -> ", JSON.stringify(event.data));
const { command, args } = event.data;
if (command in commandMap) {
(
commandMap[command as keyof typeof commandMap] as (
args: Record<string, unknown>,
) => void
)(args);
}
},
);
panelLogger.info("lineage:onload");
document.documentElement.classList.add(styles.lineageBody);
executeRequestInAsync("init", {});
}, []);
useEffect(() => {
const commandMap = {
render,
columnLineage: (data: { event: CllEvents }) => {
columnLineage(data);
},
};
const messageHandler = (
event: MessageEvent<{ command: string; args: Record<string, unknown> }>,
) => {
if (!event.origin.startsWith("vscode-webview://")) {
panelLogger.debug("invalid message ", event);
return;
}
panelLogger.log("lineage:message -> ", JSON.stringify(event.data));
const { command, args } = event.data;
if (command in commandMap) {
(
commandMap[command as keyof typeof commandMap] as (
args: Record<string, unknown>,
) => void
)(args);
}
};
window.addEventListener("message", messageHandler);
panelLogger.info("lineage:onload");
document.documentElement.classList.add(styles.lineageBody);
executeRequestInAsync("init", {});
return () => {
window.removeEventListener("message", messageHandler);
};
}, []);
🧰 Tools
🪛 GitHub Check: CodeQL

[warning] 98-98: Missing origin verification in postMessage handler
Postmessage handler has no origin check.

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: 3

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f182728 and 8519f62.

📒 Files selected for processing (1)
  • webview_panels/src/modules/lineage/LineageView.tsx (1 hunks)
🧰 Additional context used
🪛 GitHub Check: CodeQL
webview_panels/src/modules/lineage/LineageView.tsx

[warning] 96-96: Missing origin verification in postMessage handler
Postmessage handler has no origin check.

Comment on lines +36 to +37
// @ts-expect-error TODO: add type generic for executeRequestInSync
ApiHelper.get = async (url: string, data?: Record<string, unknown>) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Resolve TypeScript errors instead of suppressing them

The @ts-expect-error directive suppresses a TypeScript error. It's better to properly type executeRequestInSync to ensure type safety and prevent potential runtime issues.

Update the type definitions to include generic types for executeRequestInSync, eliminating the need for the @ts-expect-error comment.

(
event: MessageEvent<{ command: string; args: Record<string, unknown> }>,
) => {
panelLogger.log("lineage:message -> ", JSON.stringify(event.data));
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Sanitize user-provided data before logging

Logging user-provided data directly can introduce security risks like log injection. Consider sanitizing or validating event.data before logging.

Update the logging statement to ensure safe logging practices:

- panelLogger.log("lineage:message -> ", JSON.stringify(event.data));
+ const sanitizedData = sanitizeEventData(event.data);
+ panelLogger.log("lineage:message -> ", JSON.stringify(sanitizedData));

Implement sanitizeEventData to properly sanitize the data before logging.

Committable suggestion was skipped due to low confidence.

Comment on lines +36 to +60
// @ts-expect-error TODO: add type generic for executeRequestInSync
ApiHelper.get = async (url: string, data?: Record<string, unknown>) => {
switch (url) {
case "upstreamTables":
case "downstreamTables":
case "getExposureDetails":
case "getColumns":
case "getConnectedColumns":
case "sendFeedback":
case "getLineageSettings":
case "persistLineageSettings":
case "init":
case "openFile":
case "openChat":
case "showInfoNotification":
case "previewFeature":
case "telemetryEvents":
return executeRequestInSync(url, { args: { params: data ?? {} } });
case "columnLineage":
return executeRequestInSync(url, { args: data });

default:
break;
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid overwriting imported module methods directly

Overwriting ApiHelper.get can lead to unexpected behavior and make the code harder to maintain and test. Consider using a custom API helper or dependency injection instead of modifying the imported module.

Create a custom API helper instance:

- // @ts-expect-error TODO: add type generic for executeRequestInSync
- ApiHelper.get = async (url: string, data?: Record<string, unknown>) => {
-   // Implementation
- };
+ const customApiHelper = {
+   get: async (url: string, data?: Record<string, unknown>) => {
+     // Implementation
+   },
+ };

Then use customApiHelper.get instead of ApiHelper.get in your component.

📝 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
// @ts-expect-error TODO: add type generic for executeRequestInSync
ApiHelper.get = async (url: string, data?: Record<string, unknown>) => {
switch (url) {
case "upstreamTables":
case "downstreamTables":
case "getExposureDetails":
case "getColumns":
case "getConnectedColumns":
case "sendFeedback":
case "getLineageSettings":
case "persistLineageSettings":
case "init":
case "openFile":
case "openChat":
case "showInfoNotification":
case "previewFeature":
case "telemetryEvents":
return executeRequestInSync(url, { args: { params: data ?? {} } });
case "columnLineage":
return executeRequestInSync(url, { args: data });
default:
break;
}
};
const customApiHelper = {
get: async (url: string, data?: Record<string, unknown>) => {
switch (url) {
case "upstreamTables":
case "downstreamTables":
case "getExposureDetails":
case "getColumns":
case "getConnectedColumns":
case "sendFeedback":
case "getLineageSettings":
case "persistLineageSettings":
case "init":
case "openFile":
case "openChat":
case "showInfoNotification":
case "previewFeature":
case "telemetryEvents":
return executeRequestInSync(url, { args: { params: data ?? {} } });
case "columnLineage":
return executeRequestInSync(url, { args: data });
default:
break;
}
},
};

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: 0

🧹 Outside diff range and nitpick comments (3)
src/webview_provider/altimateWebviewProvider.ts (3)

640-640: Add validation for viewPath

While the implementation works, it would be safer to validate the viewPath format before using it as a class name.

Consider sanitizing the viewPath:

-<body class="${this.viewPath.replace(/\//g, "")}">
+<body class="${this.viewPath.replace(/[^a-zA-Z0-9-_]/g, "")}">

This ensures only alphanumeric characters, hyphens, and underscores are used in class names, preventing potential CSS injection or invalid class names.


643-643: Enhance modal accessibility

The modal div should include ARIA attributes for better accessibility.

Consider adding ARIA attributes:

-<div id="modal"></div>
+<div id="modal" role="dialog" aria-modal="true"></div>

647-647: Improve type safety for window object extension

The lineageGif variable is added to the window object without proper TypeScript type definitions.

Consider adding type definitions:

// Add to a separate .d.ts file or at the top of this file
declare global {
  interface Window {
    viewPath: string;
    spinnerUrl: string;
    lineageGif: string;
  }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 8519f62 and ebdafe9.

📒 Files selected for processing (1)
  • src/webview_provider/altimateWebviewProvider.ts (3 hunks)
🔇 Additional comments (1)
src/webview_provider/altimateWebviewProvider.ts (1)

526-535: Optimize webview ready check implementation

The current implementation has potential issues:

  1. Uses setInterval which is less efficient than a recursive setTimeout approach
  2. No timeout mechanism to prevent infinite polling

The previous review comment suggesting improvements to this implementation is still valid. Please refer to the existing comment for the recommended implementation using setTimeout with a timeout mechanism.

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: 1

🧹 Outside diff range and nitpick comments (3)
webview_panels/src/lib/altimate/altimate-components.js (2)

1-1: Consider improving import naming for better maintainability.

The extensive use of single-letter aliases (o, t, n, etc.) in imports makes the code harder to maintain and understand. Consider using more descriptive names that reflect the actual component or utility being imported.

Example of a more maintainable import:

- import { A as o, B as t, m as n, p as i, o as r, n as m, r as C, t as T, C as l, E as c, k as p, i as g, h as v, D as L, I as u, x as h, l as B, L as M, P as d, F as A, J as b, H as k, q as y, y as F, z as P, w as x, T as D, G as I, v as S } from "./main.js";
+ import {
+   ApiHelperComponent as ApiHelper,
+   BadgeComponent as Badge,
+   CLLComponent as CLL,
+   // ... other imports with meaningful names
+ } from "./main.js";

1-32: Update README.md with component changes.

As mentioned in the PR checklist, the README.md hasn't been updated. Given the significant component renaming and restructuring, documentation should be updated to reflect these changes.

Would you like me to help create a documentation section that covers:

  1. The new component naming scheme
  2. Any breaking changes in the exports
  3. Migration guide for existing code
webview_panels/src/lib/altimate/altimate-components.d.ts (1)

353-356: Consider documenting the status types for onStatusUpdate

The Props_9 interface changes look good, adding proper type safety for status updates and follow-up requests. Consider documenting the possible values for the status type string to help implementers.

Add JSDoc comments to document the possible status types:

/** Props for the Chatbot component */
interface Props_9 extends ProChatProps<any> {
  /** Callback for handling chat requests
   * @param messages The chat messages to process
   * @param sessionId The current session identifier
   * @param onStatusUpdate Callback for status updates
   * @param onStatusUpdate.type The type of status update (e.g., 'processing', 'error', 'complete')
   * @param onStatusUpdate.message A human-readable status message
   */
  onRequest: (messages: ChatMessage[], sessionId: string, onStatusUpdate: (info: {
    type: string;
    message: string;
  }) => void) => any;
  
  /** Optional callback for handling follow-up requests
   * @param sessionId The current session identifier
   * @returns Promise resolving to an array of follow-up suggestions or undefined
   */
  onFollowupRequest?: (sessionId: string) => Promise<string[] | undefined>;
}

Also applies to: 358-358

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between ebdafe9 and c1c03fe.

📒 Files selected for processing (2)
  • webview_panels/src/lib/altimate/altimate-components.d.ts (2 hunks)
  • webview_panels/src/lib/altimate/altimate-components.js (1 hunks)
🔇 Additional comments (3)
webview_panels/src/lib/altimate/altimate-components.js (1)

3-32: Verify the impact of renamed exports on dependent code.

The extensive renaming of exports (e.g., Chatbot to ChatTriggerLink, CllEvents to Chatbot) could potentially break existing code that imports these components.

Let's verify the usage of these renamed exports:

webview_panels/src/lib/altimate/altimate-components.d.ts (2)

22-22: LGTM: Well-structured Chatbot component declaration

The updated Chatbot component declaration with the optional onFollowupRequest parameter follows TypeScript best practices.


24-26: Verify the integration of ChatTriggerLink with lineage components

The ChatTriggerLink component declaration looks good. Since this is part of the lineage refactoring, let's verify its integration.

Comment on lines +1 to +32
import { A as o, B as t, m as n, p as i, o as r, n as m, r as C, t as T, C as l, E as c, k as p, i as g, h as v, D as L, I as u, x as h, l as B, L as M, P as d, F as A, J as b, H as k, q as y, y as F, z as P, w as x, T as D, G as I, v as S } from "./main.js";
import "reactstrap";
export {
o as ApiHelper,
t as Badge,
n as CLL,
i as Chatbot,
r as CllEvents,
m as CoachForm,
C as CoachFormButton,
i as ChatTriggerLink,
r as Chatbot,
m as CllEvents,
C as CoachForm,
T as CoachFormButton,
l as CodeBlock,
T as ContentCategory,
c as ConversationGroupProvider,
p as ConversationInputForm,
c as ContentCategory,
p as ConversationGroupProvider,
g as ConversationInputForm,
v as ConversationSources,
g as DbtDocs,
L as DbtDocs,
u as IconButton,
L as Learnings,
h as Learnings,
B as Lineage,
M as LoadingButton,
d as PersonalizationScope,
h as TaskLabels,
A as TeamMateActionType,
b as TeamMateAvailability,
A as TaskLabels,
b as TeamMateActionType,
k as TeamMateAvailability,
y as TeamMateProvider,
F as TeamMates,
P as TeamMatesConfig,
k as TeammateActions,
x as Tooltip,
D as learningSchema,
I as useTeamMateContext
x as TeammateActions,
D as Tooltip,
I as learningSchema,
S as useTeamMateContext
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider grouping related exports for better organization.

The exports appear to be alphabetically ordered but could benefit from logical grouping based on functionality (e.g., Chat-related, Team-related, UI components).

Example organization:

// Chat & Communication
export { ChatTriggerLink, Chatbot, CllEvents };

// Coaching & Learning
export { CoachForm, CoachFormButton, DbtDocs, Learnings, learningSchema };

// Team Management
export { 
  TeamMateActionType,
  TeamMateAvailability,
  TeamMateProvider,
  TeamMates,
  TeamMatesConfig,
  TeammateActions,
  useTeamMateContext
};

// UI Components
export { Badge, CodeBlock, IconButton, LoadingButton, Tooltip };

// Utilities
export { ApiHelper, ConversationGroupProvider };

@anandgupta42 anandgupta42 merged commit e6c36ef into master Oct 28, 2024
10 checks passed
This was referenced Oct 30, 2024
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.

3 participants