QueryWise 0.2.1 → 0.2.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1e2af8e4e39aaa70b25e76cc31d1d9b86471852662b14b7fe35ed95411973d6
4
- data.tar.gz: 14548bdd8fcc6c6ee17e8c6669720b437987f92754cb30737ee044bbc8619374
3
+ metadata.gz: 675e9af8542844fa7945366477cc652486a47e4279c6345cb9f89ea0dfd4db1c
4
+ data.tar.gz: 239b2d8ca7a00b8f8285d3d13711eb2b634aafb91ed9a749ffbe1b886791549b
5
5
  SHA512:
6
- metadata.gz: 348175008dbd2db11aae172c42ef8806a6c4898489325252fd5a315b8606dd733539df0c045f93fd0b3112cf340136e6ccf98764c9043c649b6b3803c78b916a
7
- data.tar.gz: 4a35a2bcce3a0a7142b7db844ab9e1bc575efa5dedb582f054b27fa80045a884923e42559595888c60723447b1129f12c0cceb87f21954a685315446db28c3ea
6
+ metadata.gz: 6847d97425bea4c34d5f6ba4850c09d11def44263eda5d83e38ec3b0f8a020159f866abd4231cdc75230bcce07dd3dbda803446a81bd385278a0206f16d12e2b
7
+ data.tar.gz: 609cd7a899723f32607f7eeb6f75479faa49d0d9d8e84eeb7a4b7a7f0eceafefc36d6da85f4e34478d9e92c5677cd417bedd14332bee74fae67e426894c91e67
data/Dockerfile CHANGED
@@ -65,5 +65,5 @@ USER 1000:1000
65
65
  ENTRYPOINT ["/rails/bin/docker-entrypoint"]
66
66
 
67
67
  # Start server via Thruster by default, this can be overwritten at runtime
68
- EXPOSE 80
68
+ EXPOSE 8080
69
69
  CMD ["./bin/thrust", "./bin/rails", "server"]
data/Dockerfile.cloudrun CHANGED
@@ -46,6 +46,15 @@ COPY . .
46
46
  # Precompile bootsnap code for faster boot times
47
47
  RUN bundle exec bootsnap precompile app/ lib/
48
48
 
49
+ # Create a script to run migrations and start the server
50
+ RUN echo '#!/bin/bash\n\
51
+ set -e\n\
52
+ echo "Running database migrations..."\n\
53
+ bundle exec rails db:migrate RAILS_ENV=production\n\
54
+ echo "Starting Rails server..."\n\
55
+ exec bundle exec puma -C config/puma.rb' > /rails/start.sh && \
56
+ chmod +x /rails/start.sh
57
+
49
58
  # Final stage for app image
50
59
  FROM base
51
60
 
@@ -73,4 +82,4 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
73
82
  ENTRYPOINT ["/rails/bin/docker-entrypoint"]
74
83
 
75
84
  # Start server optimized for Cloud Run
76
- CMD ["bundle", "exec", "puma", "-C", "config/puma.rb", "-p", "$PORT", "-e", "production"]
85
+ CMD ["/rails/start.sh"]
@@ -1,59 +1,65 @@
1
1
  class Api::V1::AnalysisController < Api::V1::BaseController
2
2
  def analyze
3
- if params[:queries].nil?
4
- render_error('Missing required parameter: queries')
5
- return
6
- end
3
+ begin
4
+ if params[:queries].nil?
5
+ render_error('Missing required parameter: queries')
6
+ return
7
+ end
7
8
 
8
- queries_data = params[:queries]
9
+ queries_data = params[:queries]
9
10
 
10
- # Validate queries using the validator
11
- validation_errors = QueryDataValidator.validate_queries(queries_data)
12
- if validation_errors.any?
13
- render_error('Validation failed', :bad_request, errors: validation_errors)
14
- return
15
- end
16
-
17
- # Create QueryAnalysis records
18
- query_analyses = []
19
- queries_data.each do |query_data|
11
+ # Validate queries using the validator
12
+ validation_errors = QueryDataValidator.validate_queries(queries_data)
13
+ if validation_errors.any?
14
+ render_error('Validation failed', :bad_request, errors: validation_errors)
15
+ return
16
+ end
17
+
18
+ # Create QueryAnalysis records
19
+ query_analyses = []
20
+ queries_data.each do |query_data|
21
+ begin
22
+ analysis = create_query_analysis(query_data)
23
+ query_analyses << analysis if analysis
24
+ rescue => e
25
+ Rails.logger.error "Error creating query analysis: #{e.message}"
26
+ Rails.logger.error e.backtrace.join("\n")
27
+ end
28
+ end
29
+
30
+ if query_analyses.empty?
31
+ render_error('No valid queries provided')
32
+ return
33
+ end
34
+
35
+ # Run analysis
20
36
  begin
21
- analysis = create_query_analysis(query_data)
22
- query_analyses << analysis if analysis
37
+ analysis_results = perform_analysis(query_analyses)
23
38
  rescue => e
24
- Rails.logger.error "Error creating query analysis: #{e.message}"
39
+ Rails.logger.error "Error performing analysis: #{e.message}"
25
40
  Rails.logger.error e.backtrace.join("\n")
41
+ render_error('Analysis failed')
42
+ return
26
43
  end
27
- end
28
44
 
29
- if query_analyses.empty?
30
- render_error('No valid queries provided')
31
- return
32
- end
45
+ # Store optimization suggestions
46
+ begin
47
+ store_suggestions(query_analyses, analysis_results)
48
+ rescue => e
49
+ Rails.logger.error "Failed to store suggestions: #{e.message}"
50
+ Rails.logger.error e.backtrace.join("\n")
51
+ # Continue without storing suggestions for now
52
+ end
33
53
 
34
- # Run analysis
35
- begin
36
- analysis_results = perform_analysis(query_analyses)
37
- rescue => e
38
- Rails.logger.error "Error performing analysis: #{e.message}"
39
- Rails.logger.error e.backtrace.join("\n")
40
- render_error('Analysis failed')
41
- return
42
- end
43
-
44
- # Store optimization suggestions
45
- begin
46
- store_suggestions(query_analyses, analysis_results)
54
+ # Format response
55
+ response_data = format_analysis_response(analysis_results)
56
+
57
+ render_success(response_data, message: "Analyzed #{query_analyses.length} queries")
47
58
  rescue => e
48
- Rails.logger.error "Failed to store suggestions: #{e.message}"
59
+ Rails.logger.error "API Error: #{e.message}"
49
60
  Rails.logger.error e.backtrace.join("\n")
50
- # Continue without storing suggestions for now
61
+ render_error('Internal server error')
51
62
  end
52
-
53
- # Format response
54
- response_data = format_analysis_response(analysis_results)
55
-
56
- render_success(response_data, message: "Analyzed #{query_analyses.length} queries")
57
63
  end
58
64
 
59
65
  def analyze_ci
@@ -331,10 +337,17 @@ class Api::V1::AnalysisController < Api::V1::BaseController
331
337
 
332
338
  def calculate_severity_breakdown(results)
333
339
  breakdown = { low: 0, medium: 0, high: 0, critical: 0 }
334
-
335
- results[:n_plus_one].each { |p| breakdown[p[:severity].to_sym] += 1 }
336
- results[:slow_queries].each { |i| breakdown[i[:severity].to_sym] += 1 }
337
-
340
+
341
+ results[:n_plus_one].each do |p|
342
+ severity_key = p[:severity]&.to_sym
343
+ breakdown[severity_key] += 1 if breakdown.key?(severity_key)
344
+ end
345
+
346
+ results[:slow_queries].each do |i|
347
+ severity_key = i[:severity]&.to_sym
348
+ breakdown[severity_key] += 1 if breakdown.key?(severity_key)
349
+ end
350
+
338
351
  breakdown
339
352
  end
340
353
  end
@@ -18,12 +18,23 @@ class Api::V1::BaseController < ApplicationController
18
18
  return
19
19
  end
20
20
 
21
- @current_app_profile = AppProfile.find_by(api_key_digest: BCrypt::Password.create(api_key))
21
+ # Find app profile by checking the BCrypt hash
22
+ @current_app_profile = AppProfile.all.find do |profile|
23
+ begin
24
+ BCrypt::Password.new(profile.api_key_digest) == api_key
25
+ rescue BCrypt::Errors::InvalidHash
26
+ false
27
+ end
28
+ end
22
29
 
23
30
  unless @current_app_profile
24
31
  render_error('Invalid API key', :unauthorized)
25
32
  return
26
33
  end
34
+ rescue => e
35
+ Rails.logger.error "Authentication error: #{e.message}"
36
+ Rails.logger.error e.backtrace.join("\n")
37
+ render_error('Authentication failed', :unauthorized)
27
38
  end
28
39
 
29
40
  def set_default_response_format
@@ -6,7 +6,9 @@ class AppProfile < ApplicationRecord
6
6
  validates :api_key_digest, presence: true, uniqueness: true
7
7
 
8
8
  def self.authenticate_with_api_key(api_key)
9
- find_by(api_key_digest: BCrypt::Password.create(api_key))
9
+ all.find do |profile|
10
+ BCrypt::Password.new(profile.api_key_digest) == api_key
11
+ end
10
12
  end
11
13
 
12
14
  def generate_api_key!
data/cloudbuild.yaml CHANGED
@@ -4,14 +4,15 @@ steps:
4
4
  - name: 'gcr.io/cloud-builders/docker'
5
5
  args: [
6
6
  'build',
7
- '-t', 'gcr.io/$PROJECT_ID/query-optimizer-api:$COMMIT_SHA',
7
+ '-f', 'Dockerfile.cloudrun',
8
+ '-t', 'gcr.io/$PROJECT_ID/query-optimizer-api:$BUILD_ID',
8
9
  '-t', 'gcr.io/$PROJECT_ID/query-optimizer-api:latest',
9
10
  '.'
10
11
  ]
11
12
 
12
13
  # Push the container image to Container Registry
13
14
  - name: 'gcr.io/cloud-builders/docker'
14
- args: ['push', 'gcr.io/$PROJECT_ID/query-optimizer-api:$COMMIT_SHA']
15
+ args: ['push', 'gcr.io/$PROJECT_ID/query-optimizer-api:$BUILD_ID']
15
16
 
16
17
  - name: 'gcr.io/cloud-builders/docker'
17
18
  args: ['push', 'gcr.io/$PROJECT_ID/query-optimizer-api:latest']
@@ -21,21 +22,22 @@ steps:
21
22
  entrypoint: gcloud
22
23
  args: [
23
24
  'run', 'deploy', 'query-optimizer-api',
24
- '--image', 'gcr.io/$PROJECT_ID/query-optimizer-api:$COMMIT_SHA',
25
+ '--image', 'gcr.io/$PROJECT_ID/query-optimizer-api:latest',
25
26
  '--region', 'us-central1',
26
27
  '--platform', 'managed',
27
28
  '--allow-unauthenticated',
28
- '--port', '80',
29
+ '--port', '8080',
29
30
  '--memory', '512Mi',
30
31
  '--cpu', '1',
31
32
  '--max-instances', '10',
32
33
  '--set-env-vars', 'RAILS_ENV=production',
34
+ '--set-secrets', 'DATABASE_PASSWORD=database-password:latest',
33
35
  '--set-cloudsql-instances', '$PROJECT_ID:us-central1:query-optimizer-db'
34
36
  ]
35
37
 
36
38
  # Store images in Container Registry
37
39
  images:
38
- - 'gcr.io/$PROJECT_ID/query-optimizer-api:$COMMIT_SHA'
40
+ - 'gcr.io/$PROJECT_ID/query-optimizer-api:$BUILD_ID'
39
41
  - 'gcr.io/$PROJECT_ID/query-optimizer-api:latest'
40
42
 
41
43
  # Build options
@@ -14,7 +14,7 @@ module QueryOptimizerApi
14
14
  # Please, add to the `ignore` list any other `lib` subdirectories that do
15
15
  # not contain `.rb` files, or that should not be reloaded or eager loaded.
16
16
  # Common ones are `templates`, `generators`, or `middleware`, for example.
17
- config.autoload_lib(ignore: %w[assets tasks])
17
+ config.autoload_lib(ignore: %w[assets tasks query_optimizer_client])
18
18
 
19
19
  # Configuration for the application, engines, and railties goes here.
20
20
  #
data/config/database.yml CHANGED
@@ -52,18 +52,21 @@ test:
52
52
  production:
53
53
  primary: &primary_production
54
54
  <<: *default
55
- database: query_optimizer_api_production
56
- username: query_optimizer_api
57
- password: <%= ENV["QUERY_OPTIMIZER_API_DATABASE_PASSWORD"] %>
55
+ adapter: postgresql
56
+ database: query_optimizer_production
57
+ username: rails
58
+ password: <%= ENV["DATABASE_PASSWORD"] %>
59
+ host: <%= ENV["DB_HOST"] || "/cloudsql/vital-form-469501-s9:us-central1:query-optimizer-db" %>
60
+ port: <%= ENV["DB_PORT"] || 5432 %>
58
61
  cache:
59
62
  <<: *primary_production
60
- database: query_optimizer_api_production_cache
63
+ database: query_optimizer_production
61
64
  migrations_paths: db/cache_migrate
62
65
  queue:
63
66
  <<: *primary_production
64
- database: query_optimizer_api_production_queue
67
+ database: query_optimizer_production
65
68
  migrations_paths: db/queue_migrate
66
69
  cable:
67
70
  <<: *primary_production
68
- database: query_optimizer_api_production_cable
71
+ database: query_optimizer_production
69
72
  migrations_paths: db/cable_migrate
@@ -44,11 +44,13 @@ Rails.application.configure do
44
44
  config.active_support.report_deprecations = false
45
45
 
46
46
  # Replace the default in-process memory cache store with a durable alternative.
47
- config.cache_store = :solid_cache_store
47
+ # config.cache_store = :solid_cache_store
48
+ config.cache_store = :memory_store
48
49
 
49
50
  # Replace the default in-process and non-durable queuing backend for Active Job.
50
- config.active_job.queue_adapter = :solid_queue
51
- config.solid_queue.connects_to = { database: { writing: :queue } }
51
+ # config.active_job.queue_adapter = :solid_queue
52
+ # config.solid_queue.connects_to = { database: { writing: :queue } }
53
+ config.active_job.queue_adapter = :async
52
54
 
53
55
  # Ignore bad email addresses and do not raise email delivery errors.
54
56
  # Set this to true and configure the email server for immediate delivery to raise delivery errors.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QueryOptimizerClient
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.2"
5
5
  end
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = "QueryWise is a lightweight, developer-friendly tool that helps Ruby on Rails teams detect, analyze, and fix inefficient database queries. Automatically detect N+1 queries, slow queries, and missing indexes without needing heavy, expensive Application Performance Monitoring (APM) software."
13
13
  spec.homepage = "https://github.com/BlairLane22/QueryOptimizerAPI"
14
14
  spec.license = "MIT"
15
- spec.required_ruby_version = ">= 3.0.0"
15
+ spec.required_ruby_version = ">= 3.2.2"
16
16
 
17
17
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
18
  spec.metadata["homepage_uri"] = spec.homepage
@@ -44,7 +44,7 @@ Gem::Specification.new do |spec|
44
44
 
45
45
  # Runtime dependencies
46
46
  spec.add_dependency "httparty", "~> 0.21"
47
- spec.add_dependency "activesupport", "~> 7.0"
47
+ spec.add_dependency "activesupport", "~> 8.0", ">= 8.0.2.1"
48
48
 
49
49
  # Development dependencies
50
50
  spec.add_development_dependency "rake", "~> 13.0"
@@ -52,7 +52,7 @@ Gem::Specification.new do |spec|
52
52
  spec.add_development_dependency "rubocop", "~> 1.21"
53
53
  spec.add_development_dependency "webmock", "~> 3.18"
54
54
  spec.add_development_dependency "vcr", "~> 6.1"
55
- spec.add_development_dependency "rails", "~> 7.0"
55
+ spec.add_development_dependency "rails", "~> 8.0"
56
56
  spec.add_development_dependency "sqlite3", "~> 1.4"
57
57
 
58
58
  # For more information and examples about making a new gem, check out our
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: QueryWise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Blair Lane
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-08-19 00:00:00.000000000 Z
11
+ date: 2025-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -30,14 +30,20 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '7.0'
33
+ version: '8.0'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 8.0.2.1
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
41
  - - "~>"
39
42
  - !ruby/object:Gem::Version
40
- version: '7.0'
43
+ version: '8.0'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 8.0.2.1
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: rake
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -114,14 +120,14 @@ dependencies:
114
120
  requirements:
115
121
  - - "~>"
116
122
  - !ruby/object:Gem::Version
117
- version: '7.0'
123
+ version: '8.0'
118
124
  type: :development
119
125
  prerelease: false
120
126
  version_requirements: !ruby/object:Gem::Requirement
121
127
  requirements:
122
128
  - - "~>"
123
129
  - !ruby/object:Gem::Version
124
- version: '7.0'
130
+ version: '8.0'
125
131
  - !ruby/object:Gem::Dependency
126
132
  name: sqlite3
127
133
  requirement: !ruby/object:Gem::Requirement
@@ -244,7 +250,7 @@ metadata:
244
250
  source_code_uri: https://github.com/BlairLane22/QueryWise
245
251
  changelog_uri: https://github.com/BlairLane22/QueryWise/blob/main/CHANGELOG.md
246
252
  documentation_uri: https://github.com/BlairLane22/QueryWise/blob/main/README.md
247
- post_install_message:
253
+ post_install_message:
248
254
  rdoc_options: []
249
255
  require_paths:
250
256
  - lib
@@ -252,15 +258,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
252
258
  requirements:
253
259
  - - ">="
254
260
  - !ruby/object:Gem::Version
255
- version: 3.0.0
261
+ version: 3.2.2
256
262
  required_rubygems_version: !ruby/object:Gem::Requirement
257
263
  requirements:
258
264
  - - ">="
259
265
  - !ruby/object:Gem::Version
260
266
  version: '0'
261
267
  requirements: []
262
- rubygems_version: 3.0.3.1
263
- signing_key:
268
+ rubygems_version: 3.4.10
269
+ signing_key:
264
270
  specification_version: 4
265
271
  summary: QueryWise - Rails Database Query Optimizer
266
272
  test_files: []