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 templating capabilities #3

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft

add templating capabilities #3

wants to merge 2 commits into from

Conversation

elg0nz
Copy link
Contributor

@elg0nz elg0nz commented Sep 27, 2024

Summary by CodeRabbit

  • New Features

    • Introduced a Unix socket server for dynamic request template handling.
    • Added methods to set and remove request templates via a command-line interface.
    • Enhanced command logging to provide additional feedback on execution results.
  • Bug Fixes

    • Improved error handling for file access when setting templates.
  • Documentation

    • Updated logging methods to reflect changes in request handling.

@elg0nz elg0nz requested review from fitzk and jonathankap September 27, 2024 20:48
Copy link

coderabbitai bot commented Sep 27, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

The changes introduce a Unix socket server to the RequestInterceptor class, enabling dynamic handling of request templates. New methods are added to manage templates through socket communication, including setting and removing templates. Additionally, the logging and request handling mechanisms are updated to accommodate a simpler request key. In the UploadsCli class, methods for setting and removing templates are implemented, allowing users to interact with the socket server effectively.

Changes

File Change Summary
lib/request_interceptor.rb Introduced Unix socket server, methods for handling templates, and updated logging mechanisms. Added start_unix_socket_server, handle_client, and process_message methods. Updated method signatures for logging to use request keys instead of hashes. Removed generate_request_hash method.
lib/uploads_cli.rb Added set_template and remove_template methods to manage templates via the Unix socket. Error handling for file operations included in set_template.
lib/command_executor.rb Modified log_command_result method to output the response value from the parsed JSON result to the console.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant UploadsCli
    participant RequestInterceptor

    Client->>UploadsCli: set_template(key, filename)
    UploadsCli->>RequestInterceptor: send message (set, key, template content)
    RequestInterceptor->>RequestInterceptor: store template
    Client->>UploadsCli: remove_template(key)
    UploadsCli->>RequestInterceptor: send message (remove, key)
    RequestInterceptor->>RequestInterceptor: remove template
Loading

Poem

🐇 In the burrow, changes brew,
A socket server, fresh and new.
Templates dance, requests align,
With every hop, the code will shine.
Set and remove, a simple task,
In this code, we joyfully bask! 🌟


🪧 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>, please review it.
    • 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 gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @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

@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: 6

🧹 Outside diff range and nitpick comments (3)
lib/uploads_cli.rb (1)

Line range hint 1-103: Consider adding documentation and separating socket communication concerns.

The new template management functionality aligns well with the PR objective. However, there are a few areas that could be improved:

  1. Add documentation for the new methods set_template and remove_template. This will help other developers understand their purpose and usage.
  2. Consider extracting the socket communication logic into a separate module. This would improve the separation of concerns and make the UploadsCLI class more focused on its primary responsibilities.

Here's a suggestion for documenting the new methods:

# Set a template for the given key using the content of the specified file
#
# @param key [String] The key to associate with the template
# @param filename [String] The path to the file containing the template content
# @raise [Errno::ENOENT] If the specified file does not exist
def set_template(key, filename)
  # ... implementation ...
end

# Remove the template associated with the given key
#
# @param key [String] The key of the template to remove
def remove_template(key)
  # ... implementation ...
end

For socket communication, consider creating a SocketCommunicator module:

module SocketCommunicator
  def send_socket_message(message)
    # ... implementation ...
  end

  # ... other socket-related methods ...
end

class UploadsCLI < Thor
  include SocketCommunicator
  # ... rest of the class ...
end

This approach would make the code more modular and easier to maintain.

🧰 Tools
🪛 rubocop

[convention] 69-88: Method has too many lines. [14/10]

(Metrics/MethodLength)

lib/request_interceptor.rb (2)

83-83: Use safe navigation operator

Instead of checking if @server exists before calling close, you can use the safe navigation operator &. for cleaner code.

Apply this diff:

-@server.close if @server
+@server&.close
🧰 Tools
🪛 rubocop

[convention] 83-83: Use safe navigation (&.) instead of checking if an object exists before calling the method.

(Style/SafeNavigation)


88-88: Clarify assignment in condition by adding parentheses

In the while loop, the assignment within the condition can be misread as a comparison. Wrapping the assignment in parentheses clarifies the intent and avoids potential warnings.

Apply this diff:

-while message = conn.gets
+while (message = conn.gets)
🧰 Tools
🪛 rubocop

[warning] 88-88: Use == if you meant to do a comparison or wrap the expression in parentheses to indicate you meant to assign in a condition.

(Lint/AssignmentInCondition)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 29d3c7e and a3b71ad.

📒 Files selected for processing (2)
  • lib/request_interceptor.rb (3 hunks)
  • lib/uploads_cli.rb (2 hunks)
🧰 Additional context used
🪛 rubocop
lib/request_interceptor.rb

[convention] 28-58: Assignment Branch Condition size for call is too high. [<10, 20, 5> 22.91/17]

(Metrics/AbcSize)


[convention] 28-58: Method has too many lines. [19/10]

(Metrics/MethodLength)


[convention] 62-85: Assignment Branch Condition size for start_unix_socket_server is too high. [<5, 16, 4> 17.23/17]

(Metrics/AbcSize)


[convention] 62-85: Method has too many lines. [19/10]

(Metrics/MethodLength)


[convention] 83-83: Use safe navigation (&.) instead of checking if an object exists before calling the method.

(Style/SafeNavigation)


[warning] 88-88: Use == if you meant to do a comparison or wrap the expression in parentheses to indicate you meant to assign in a condition.

(Lint/AssignmentInCondition)


[convention] 98-120: Assignment Branch Condition size for process_message is too high. [<6, 17, 3> 18.28/17]

(Metrics/AbcSize)


[convention] 98-120: Method has too many lines. [21/10]

(Metrics/MethodLength)

lib/uploads_cli.rb

[convention] 69-88: Method has too many lines. [14/10]

(Metrics/MethodLength)

@@ -26,6 +26,7 @@ def initialize(*args)
@client_uuid = current_client_uuid
@file_uploader = FileUploader.new(@logger, @client_uuid)
@command_executor = CommandExecutor.new(@logger, @client_uuid)
@socket_path = '/tmp/hawksi.sock'
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider improving socket path configuration and error handling.

The socket path is currently hardcoded, which might limit flexibility across different environments. Consider the following improvements:

  1. Make the socket path configurable, possibly through an environment variable or a configuration file.
  2. Add a check to ensure the socket file exists and is accessible when initializing the class.

Here's a suggested implementation:

def initialize(*args)
  super
  # ... existing code ...
  @socket_path = ENV['HAWKSI_SOCKET_PATH'] || '/tmp/hawksi.sock'
  validate_socket_path
end

private

def validate_socket_path
  unless File.exist?(@socket_path) && File.socket?(@socket_path)
    raise "Socket file not found or invalid: #{@socket_path}"
  end
end

This change allows for configuration via an environment variable and includes a basic validation of the socket file.

Comment on lines +66 to +88
desc 'set_template KEY FILENAME', 'Set a template for the given key using the content of the specified file'
option :base_dir, type: :string,
desc: 'Base directory for storing intercepted data. Defaults to ./tmp/intercepted_data'
def set_template(key, filename)
set_base_dir
unless File.exist?(filename)
@logger.error "File not found: #{filename}"
return
end

# Read the template content from the file
template_content = File.read(filename)

message = {
action: 'set_template',
key: key,
template: template_content
}
# Send the message to the /tmp/hawksi.sock socket
socket = UNIXSocket.new(@socket_path)
socket.write(message.to_json)
socket.close
end
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor set_template method for improved error handling and readability.

The set_template method could benefit from some refactoring to improve error handling, reduce method length, and ensure proper socket communication. Consider the following suggestions:

  1. Wrap the socket communication in a begin-rescue block to handle potential errors.
  2. Ensure the socket is always closed, even in case of an error.
  3. Extract the file reading and socket communication into separate private methods to reduce the main method's length.

Here's a suggested refactoring:

def set_template(key, filename)
  set_base_dir
  template_content = read_template_file(filename)
  send_template_message(key, template_content) if template_content
end

private

def read_template_file(filename)
  File.read(filename)
rescue Errno::ENOENT
  @logger.error "File not found: #{filename}"
  nil
end

def send_template_message(key, template_content)
  message = {
    action: 'set_template',
    key: key,
    template: template_content
  }
  send_socket_message(message)
end

def send_socket_message(message)
  socket = UNIXSocket.new(@socket_path)
  socket.write(message.to_json)
rescue StandardError => e
  @logger.error "Error sending message to socket: #{e.message}"
ensure
  socket&.close
end

This refactoring improves error handling, ensures the socket is always closed, and reduces the main method's length by extracting functionality into separate methods.

🧰 Tools
🪛 rubocop

[convention] 69-88: Method has too many lines. [14/10]

(Metrics/MethodLength)

Comment on lines +90 to +102
desc 'remove_template KEY', 'Remove the template for the given key'
option :base_dir, type: :string,
desc: 'Base directory for storing intercepted data. Defaults to ./tmp/intercepted_data'
def remove_template(key)
set_base_dir
message = {
action: 'remove_template',
key: key
}
socket = UNIXSocket.new(@socket_path)
socket.write(message.to_json)
socket.close
end
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve remove_template method with error handling and code reuse.

The remove_template method could benefit from improved error handling and code reuse. Consider the following suggestions:

  1. Reuse the send_socket_message method suggested in the previous comment for consistent error handling and socket management.
  2. Keep the method focused on its specific task by extracting the message creation.

Here's a suggested refactoring:

def remove_template(key)
  set_base_dir
  message = create_remove_template_message(key)
  send_socket_message(message)
end

private

def create_remove_template_message(key)
  {
    action: 'remove_template',
    key: key
  }
end

# Use the send_socket_message method from the previous comment

This refactoring improves error handling, ensures consistent socket management, and promotes code reuse between set_template and remove_template methods.

Comment on lines +122 to +124
def generate_request_key(request)
"#{request.request_method}_#{request.path}"
end
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Include query parameters in request key to ensure uniqueness

The generate_request_key method uses request.request_method and request.path, which might not be unique if the same path is used with different query parameters. To avoid key collisions, consider including the query string in the request key.

Apply this diff to include query parameters:

 def generate_request_key(request)
-  "#{request.request_method}_#{request.path}"
+  "#{request.request_method}_#{request.fullpath}"
 end
📝 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
def generate_request_key(request)
"#{request.request_method}_#{request.path}"
end
def generate_request_key(request)
"#{request.request_method}_#{request.fullpath}"
end

Comment on lines +24 to +25
@socket_path = '/tmp/hawksi.sock'
start_unix_socket_server
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Make the Unix socket path configurable

Currently, the socket path @socket_path = '/tmp/hawksi.sock' is hardcoded, which may lead to conflicts or reduce flexibility in different environments. Consider making it configurable through an environment variable or an initialization parameter.

Apply this diff to make the socket path configurable:

 def initialize(app, logger: Logger.new('hawksi.log'), storage: FileStorage)
   @app = app
   @logger = logger
   @storage = storage
   @templates = {}
   @templates_mutex = Mutex.new
-  @socket_path = '/tmp/hawksi.sock'
+  @socket_path = ENV['HAWKSI_SOCKET_PATH'] || '/tmp/hawksi.sock'
   start_unix_socket_server
 end
📝 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
@socket_path = '/tmp/hawksi.sock'
start_unix_socket_server
@socket_path = ENV['HAWKSI_SOCKET_PATH'] || '/tmp/hawksi.sock'
start_unix_socket_server

Comment on lines +38 to +42
status, headers, _temp = @app.call(env)

# Serve the template
headers['Etag'] = Digest::MD5.hexdigest(template)
return [status, headers, [template]]
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid unnecessary application calls when serving templates

When a template is found, the code still calls @app.call(env) to obtain the status and headers, which can be inefficient if the application is resource-intensive. Consider defining default status and headers to avoid unnecessary application calls.

Apply this diff to bypass calling the application when serving a template:

-        status, headers, _temp = @app.call(env)
-
+        status = 200
+        headers = { 'Content-Type' => 'application/json' }
         # Serve the template
         headers['Etag'] = Digest::MD5.hexdigest(template)
         return [status, headers, [template]]
📝 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
status, headers, _temp = @app.call(env)
# Serve the template
headers['Etag'] = Digest::MD5.hexdigest(template)
return [status, headers, [template]]
status = 200
headers = { 'Content-Type' => 'application/json' }
# Serve the template
headers['Etag'] = Digest::MD5.hexdigest(template)
return [status, headers, [template]]

Copy link

@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 (1)
lib/command_executor.rb (1)

64-64: Improved feedback, but consider enhancing clarity and flexibility.

The addition of this line improves user experience by providing immediate feedback in the console. Good use of fetch with a default value to prevent errors.

However, consider the following suggestions:

  1. Add a prefix to the output to distinguish it from other console messages, e.g., puts "Response: #{result.fetch('response', '')}".
  2. Consider making this console output configurable, allowing users to enable/disable it based on their needs, especially for scripted usage.

Here's a potential refactor to address these suggestions:

def log_command_result(result)
  if result['result'] == 'error'
    logger.error "Error during command execution: #{result['message']}"
  else
    logger.info "Command executed successfully. #{result}"
    puts "Response: #{result.fetch('response', '')}" if Hawksi.configuration.show_console_output
  end
end

This assumes you add a show_console_output option to your configuration. Don't forget to update the configuration class if you implement this suggestion.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a3b71ad and 9658031.

📒 Files selected for processing (1)
  • lib/command_executor.rb (1 hunks)

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.

1 participant