dbviewer 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +92 -0
  3. data/app/controllers/concerns/dbviewer/database_operations.rb +11 -19
  4. data/app/controllers/dbviewer/api/entity_relationship_diagrams_controller.rb +84 -0
  5. data/app/controllers/dbviewer/api/queries_controller.rb +1 -1
  6. data/app/controllers/dbviewer/entity_relationship_diagrams_controller.rb +5 -6
  7. data/app/controllers/dbviewer/logs_controller.rb +1 -1
  8. data/app/controllers/dbviewer/tables_controller.rb +2 -8
  9. data/app/helpers/dbviewer/application_helper.rb +1 -1
  10. data/app/views/dbviewer/entity_relationship_diagrams/index.html.erb +232 -141
  11. data/app/views/dbviewer/tables/show.html.erb +278 -404
  12. data/config/routes.rb +7 -0
  13. data/lib/dbviewer/database/cache_manager.rb +78 -0
  14. data/lib/dbviewer/database/dynamic_model_factory.rb +62 -0
  15. data/lib/dbviewer/database/manager.rb +204 -0
  16. data/lib/dbviewer/database/metadata_manager.rb +129 -0
  17. data/lib/dbviewer/datatable/query_operations.rb +330 -0
  18. data/lib/dbviewer/datatable/query_params.rb +41 -0
  19. data/lib/dbviewer/engine.rb +1 -1
  20. data/lib/dbviewer/query/analyzer.rb +250 -0
  21. data/lib/dbviewer/query/collection.rb +39 -0
  22. data/lib/dbviewer/query/executor.rb +93 -0
  23. data/lib/dbviewer/query/logger.rb +108 -0
  24. data/lib/dbviewer/query/parser.rb +56 -0
  25. data/lib/dbviewer/storage/file_storage.rb +0 -3
  26. data/lib/dbviewer/version.rb +1 -1
  27. data/lib/dbviewer.rb +24 -7
  28. metadata +14 -14
  29. data/lib/dbviewer/cache_manager.rb +0 -78
  30. data/lib/dbviewer/database_manager.rb +0 -249
  31. data/lib/dbviewer/dynamic_model_factory.rb +0 -60
  32. data/lib/dbviewer/error_handler.rb +0 -18
  33. data/lib/dbviewer/logger.rb +0 -77
  34. data/lib/dbviewer/query_analyzer.rb +0 -239
  35. data/lib/dbviewer/query_collection.rb +0 -37
  36. data/lib/dbviewer/query_executor.rb +0 -91
  37. data/lib/dbviewer/query_parser.rb +0 -53
  38. data/lib/dbviewer/table_metadata_manager.rb +0 -136
  39. data/lib/dbviewer/table_query_operations.rb +0 -621
  40. data/lib/dbviewer/table_query_params.rb +0 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fff65e39ec4f823de37b99cf4b60c49d998e56bf2ff10cde22e9d664ea193fdc
4
- data.tar.gz: 3ad5a045ab31b37c8f8d7ae410499f9f16a9a10fe8a52a8b3161e331cb3f0f6a
3
+ metadata.gz: 4d0b4fd21e1b0a48e17e9301cc629947f80a40e66b4e3f2120848eaffd50fa53
4
+ data.tar.gz: ab3fc815ced156f36e0640334c2ed304fd7c77b8c2b50d780b7c1a2d95ce2fd8
5
5
  SHA512:
6
- metadata.gz: 72f824b283bd82504319fe5002d48d1f8a7cd29f4038773d4e75ff7c8e9ba250e27de2239eee36adac6ff10740e5779a5272f0c202f2bb1ccb32c586f94ea093
7
- data.tar.gz: 655d0bd7d9ea5fc5c70e338b8bdea8dd000b8fd25984c267243598a02afcaff50ba185d80d123b9e2203148b82945f13ebc47d470ef900252887cdfe4134d3c6
6
+ metadata.gz: 658597f5db20651fd6fbd40078adfe9b7dec956a2c1933b87e76523a0f33bca278fbcbf02f5c822f92202419bb0c80ec4b9af6b24f9e0a02ea092bed4894b7e0
7
+ data.tar.gz: 96fe5ec2d5f105fd6f041fb393092f39012b8708dcf18f915c2b2fe606e587280d286b766a1f211e01f40f7ed8a3be616826782bede4e28a676ee49b16146e75
data/README.md CHANGED
@@ -333,6 +333,98 @@ gem build dbviewer.gemspec
333
333
  2. Visit `http://localhost:3000/dbviewer` to test your changes
334
334
  3. The dummy app includes sample data across multiple tables to test various DBViewer features
335
335
 
336
+ ### Architecture Diagram
337
+ ```mermaid
338
+ graph TB
339
+ subgraph "DBViewer Engine"
340
+ Engine[Engine<br/>Rails::Engine]
341
+ Config[Configuration<br/>Settings & Defaults]
342
+ SqlValidator[SqlValidator<br/>Query Validation]
343
+ end
344
+
345
+ subgraph "Controllers Layer"
346
+ HomeController[HomeController<br/>Dashboard & Overview]
347
+ TablesController[TablesController<br/>Table Operations]
348
+ LogsController[LogsController<br/>Query Logs]
349
+ ERDController[ERDController<br/>Entity Relationships]
350
+ APIController[API Controllers<br/>JSON Endpoints]
351
+ end
352
+
353
+ subgraph "Database Namespace"
354
+ Manager[Manager<br/>Database Operations]
355
+ CacheManager[CacheManager<br/>Caching Layer]
356
+ MetadataManager[MetadataManager<br/>Schema Information]
357
+ DynamicModelFactory[DynamicModelFactory<br/>ActiveRecord Models]
358
+ end
359
+
360
+ subgraph "Query Namespace"
361
+ QueryExecutor[Executor<br/>SQL Execution]
362
+ QueryLogger[Logger<br/>Query Logging]
363
+ QueryAnalyzer[Analyzer<br/>Performance Analysis]
364
+ QueryParser[Parser<br/>SQL Parsing]
365
+ end
366
+
367
+ subgraph "Datatable Namespace"
368
+ QueryOperations[QueryOperations<br/>Table Queries]
369
+ QueryParams[QueryParams<br/>Parameter Handling]
370
+ end
371
+
372
+ subgraph "Storage Namespace"
373
+ StorageBase[Base<br/>Storage Interface]
374
+ InMemoryStorage[InMemoryStorage<br/>Memory Storage]
375
+ FileStorage[FileStorage<br/>File Storage]
376
+ end
377
+
378
+ %% Configuration Dependencies (Decoupled)
379
+ Config -.->|"Dependency Injection"| Manager
380
+ Manager -->|"cache_expiry"| CacheManager
381
+ Manager -->|"config object"| QueryExecutor
382
+
383
+ %% Main Dependencies
384
+ Engine --> HomeController
385
+ Engine --> TablesController
386
+ Engine --> LogsController
387
+ Engine --> ERDController
388
+
389
+ Manager --> CacheManager
390
+ Manager --> MetadataManager
391
+ Manager --> DynamicModelFactory
392
+ Manager --> QueryOperations
393
+
394
+ CacheManager --> DynamicModelFactory
395
+ CacheManager --> MetadataManager
396
+
397
+ QueryOperations --> DynamicModelFactory
398
+ QueryOperations --> QueryExecutor
399
+ QueryOperations --> MetadataManager
400
+
401
+ QueryLogger --> StorageBase
402
+ StorageBase --> InMemoryStorage
403
+ StorageBase --> FileStorage
404
+
405
+ TablesController --> Manager
406
+ HomeController --> Manager
407
+ LogsController --> QueryLogger
408
+ APIController --> Manager
409
+ APIController --> QueryLogger
410
+
411
+ %% Decoupled Configuration Flow
412
+ Engine -.->|"setup()"| QueryLogger
413
+ Config -.->|"logging settings"| QueryLogger
414
+
415
+ classDef decoupled fill:#e1f5fe,stroke:#01579b,stroke-width:2px
416
+ classDef controller fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
417
+ classDef database fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px
418
+ classDef query fill:#fff3e0,stroke:#e65100,stroke-width:2px
419
+ classDef storage fill:#fce4ec,stroke:#880e4f,stroke-width:2px
420
+
421
+ class CacheManager,QueryLogger decoupled
422
+ class HomeController,TablesController,LogsController,ERDController,APIController controller
423
+ class Manager,MetadataManager,DynamicModelFactory database
424
+ class QueryExecutor,QueryAnalyzer,QueryParser query
425
+ class StorageBase,InMemoryStorage,FileStorage storage
426
+ ```
427
+
336
428
  ## 🤌🏻 Contributing
337
429
 
338
430
  Bug reports and pull requests are welcome.
@@ -10,7 +10,7 @@ module Dbviewer
10
10
 
11
11
  # Initialize the database manager
12
12
  def database_manager
13
- @database_manager ||= ::Dbviewer::DatabaseManager.new
13
+ @database_manager ||= ::Dbviewer::Database::Manager.new
14
14
  end
15
15
 
16
16
  # Initialize the table query operations manager
@@ -46,23 +46,15 @@ module Dbviewer
46
46
 
47
47
  # Get the name of the current database adapter
48
48
  def get_adapter_name
49
- adapter = database_manager.connection.adapter_name
50
-
51
- # Format the adapter name for better display
52
- case adapter.downcase
53
- when /mysql/
54
- "MySQL"
55
- when /postgres/
56
- "PostgreSQL"
57
- when /sqlite/
58
- "SQLite"
59
- when /oracle/
60
- "Oracle"
61
- when /sqlserver/, /mssql/
62
- "SQL Server"
63
- else
64
- adapter.titleize # Fallback to titleized version
65
- end
49
+ adapter_name = database_manager.connection.adapter_name.downcase
50
+ adapter_mappings = {
51
+ /mysql/i => "MySQL",
52
+ /postgres/i => "PostgreSQL",
53
+ /sqlite/i => "SQLite",
54
+ /oracle/i => "Oracle",
55
+ /sqlserver|mssql/i => "SQL Server"
56
+ }
57
+ adapter_mappings.find { |pattern, _| adapter_name =~ pattern }&.last || adapter_name.titleize
66
58
  rescue => e
67
59
  Rails.logger.error("Error retrieving adapter name: #{e.message}")
68
60
  "Unknown"
@@ -328,7 +320,7 @@ module Dbviewer
328
320
 
329
321
  # Export table data to CSV
330
322
  def export_table_to_csv(table_name, query_params = nil, include_headers = true)
331
- records = database_manager.query_operations.table_records(table_name, query_params)
323
+ records = database_manager.table_query_operations.table_records(table_name, query_params)
332
324
 
333
325
  csv_data = CSV.generate do |csv|
334
326
  # Add headers if requested
@@ -0,0 +1,84 @@
1
+ module Dbviewer
2
+ module Api
3
+ class EntityRelationshipDiagramsController < BaseController
4
+ before_action :set_tables
5
+
6
+ def relationships
7
+ # Fetch all relationships asynchronously
8
+ begin
9
+ @table_relationships = fetch_table_relationships
10
+ render_success({
11
+ relationships: @table_relationships,
12
+ status: "success"
13
+ })
14
+ rescue => e
15
+ Rails.logger.error("[DBViewer] Error fetching relationships: #{e.message}")
16
+ render json: {
17
+ relationships: [],
18
+ status: "error",
19
+ error: e.message
20
+ }, status: :internal_server_error
21
+ end
22
+ end
23
+
24
+ def table_relationships
25
+ # Fetch relationships for specific tables
26
+ table_names = params[:tables]&.split(",") || []
27
+
28
+ if table_names.blank?
29
+ render json: {
30
+ relationships: [],
31
+ status: "error",
32
+ error: "No tables specified"
33
+ }, status: :bad_request
34
+ return
35
+ end
36
+
37
+ begin
38
+ relationships = []
39
+
40
+ table_names.each do |table_name|
41
+ next unless @tables.any? { |t| t[:name] == table_name }
42
+
43
+ begin
44
+ metadata = fetch_table_metadata(table_name)
45
+ if metadata && metadata[:foreign_keys].present?
46
+ metadata[:foreign_keys].each do |fk|
47
+ relationships << {
48
+ from_table: table_name,
49
+ to_table: fk[:to_table],
50
+ from_column: fk[:column],
51
+ to_column: fk[:primary_key],
52
+ name: fk[:name]
53
+ }
54
+ end
55
+ end
56
+ rescue => e
57
+ Rails.logger.error("[DBViewer] Error fetching relationships for #{table_name}: #{e.message}")
58
+ # Continue with other tables even if one fails
59
+ end
60
+ end
61
+
62
+ render_success({
63
+ relationships: relationships,
64
+ status: "success",
65
+ processed_tables: table_names
66
+ })
67
+ rescue => e
68
+ Rails.logger.error("[DBViewer] Error in table_relationships: #{e.message}")
69
+ render json: {
70
+ relationships: [],
71
+ status: "error",
72
+ error: e.message
73
+ }, status: :internal_server_error
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def set_tables
80
+ @tables = fetch_tables
81
+ end
82
+ end
83
+ end
84
+ end
@@ -13,7 +13,7 @@ module Dbviewer
13
13
  def fetch_recent_queries
14
14
  return [] unless query_logging_enabled?
15
15
 
16
- Dbviewer::Logger.instance.recent_queries(limit: queries_limit)
16
+ Dbviewer::Query::Logger.instance.recent_queries(limit: queries_limit)
17
17
  end
18
18
 
19
19
  def query_logging_enabled?
@@ -1,19 +1,18 @@
1
1
  module Dbviewer
2
2
  class EntityRelationshipDiagramsController < ApplicationController
3
3
  def index
4
- if @tables.present?
5
- @table_relationships = fetch_table_relationships
6
- else
7
- @table_relationships = []
4
+ # Only show warning if no tables exist, but don't fetch relationships on initial load
5
+ if @tables.blank?
8
6
  flash.now[:warning] = "No tables found in database to generate ERD."
9
7
  end
10
8
 
11
9
  respond_to do |format|
12
- format.html
10
+ format.html # Just render the HTML without relationships
13
11
  format.json do
12
+ # For JSON requests, return just tables initially
14
13
  render json: {
15
14
  tables: @tables,
16
- relationships: @table_relationships
15
+ relationships: []
17
16
  }
18
17
  end
19
18
  end
@@ -45,7 +45,7 @@ module Dbviewer
45
45
  end
46
46
 
47
47
  def dbviewer_logger
48
- @dbviewer_logger ||= Dbviewer::Logger.instance
48
+ @dbviewer_logger ||= Dbviewer::Query::Logger.instance
49
49
  end
50
50
  end
51
51
  end
@@ -11,7 +11,7 @@ module Dbviewer
11
11
  end
12
12
 
13
13
  def show
14
- query_params = Dbviewer::TableQueryParams.new(
14
+ query_params = Dbviewer::Datatable::QueryParams.new(
15
15
  page: @current_page,
16
16
  per_page: @per_page,
17
17
  order_by: @order_by,
@@ -29,12 +29,6 @@ module Dbviewer
29
29
  @records = ActiveRecord::Result.new(column_names, [])
30
30
  end
31
31
 
32
- # Fetch timestamp visualization data if the table has a created_at column
33
- if has_timestamp_column?(@table_name)
34
- @time_grouping = params[:time_group] || "daily"
35
- @timestamp_data = fetch_timestamp_data(@table_name, @time_grouping)
36
- end
37
-
38
32
  respond_to do |format|
39
33
  format.html # Default HTML response
40
34
  format.json do
@@ -76,7 +70,7 @@ module Dbviewer
76
70
  end
77
71
 
78
72
  include_headers = params[:include_headers] != "0"
79
- query_params = Dbviewer::TableQueryParams.new(
73
+ query_params = Dbviewer::Datatable::QueryParams.new(
80
74
  page: @current_page,
81
75
  per_page: (params[:limit] || 10000).to_i,
82
76
  order_by: @order_by,
@@ -11,7 +11,7 @@ module Dbviewer
11
11
 
12
12
  # Helper to access the database manager
13
13
  def get_database_manager
14
- @database_manager ||= ::Dbviewer::DatabaseManager.new
14
+ @database_manager ||= ::Dbviewer::Database::Manager.new
15
15
  end
16
16
 
17
17
  # Extract column type from columns info