QueryWise 0.2.0
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 +7 -0
- data/CHANGELOG.md +45 -0
- data/CLOUD_RUN_README.md +263 -0
- data/DOCKER_README.md +327 -0
- data/Dockerfile +69 -0
- data/Dockerfile.cloudrun +76 -0
- data/Dockerfile.dev +36 -0
- data/GEM_Gemfile +16 -0
- data/GEM_README.md +421 -0
- data/GEM_Rakefile +10 -0
- data/GEM_gitignore +137 -0
- data/LICENSE.txt +21 -0
- data/PUBLISHING_GUIDE.md +269 -0
- data/README.md +392 -0
- data/app/controllers/api/v1/analysis_controller.rb +340 -0
- data/app/controllers/api/v1/api_keys_controller.rb +83 -0
- data/app/controllers/api/v1/base_controller.rb +93 -0
- data/app/controllers/api/v1/health_controller.rb +86 -0
- data/app/controllers/application_controller.rb +2 -0
- data/app/controllers/concerns/.keep +0 -0
- data/app/jobs/application_job.rb +7 -0
- data/app/mailers/application_mailer.rb +4 -0
- data/app/models/app_profile.rb +18 -0
- data/app/models/application_record.rb +3 -0
- data/app/models/concerns/.keep +0 -0
- data/app/models/optimization_suggestion.rb +44 -0
- data/app/models/query_analysis.rb +47 -0
- data/app/models/query_pattern.rb +55 -0
- data/app/services/missing_index_detector_service.rb +244 -0
- data/app/services/n_plus_one_detector_service.rb +177 -0
- data/app/services/slow_query_analyzer_service.rb +225 -0
- data/app/services/sql_parser_service.rb +352 -0
- data/app/validators/query_data_validator.rb +96 -0
- data/app/views/layouts/mailer.html.erb +13 -0
- data/app/views/layouts/mailer.text.erb +1 -0
- data/app.yaml +109 -0
- data/cloudbuild.yaml +47 -0
- data/config/application.rb +32 -0
- data/config/boot.rb +4 -0
- data/config/cable.yml +17 -0
- data/config/cache.yml +16 -0
- data/config/credentials.yml.enc +1 -0
- data/config/database.yml +69 -0
- data/config/deploy.yml +116 -0
- data/config/environment.rb +5 -0
- data/config/environments/development.rb +70 -0
- data/config/environments/production.rb +87 -0
- data/config/environments/test.rb +53 -0
- data/config/initializers/cors.rb +16 -0
- data/config/initializers/filter_parameter_logging.rb +8 -0
- data/config/initializers/inflections.rb +16 -0
- data/config/locales/en.yml +31 -0
- data/config/master.key +1 -0
- data/config/puma.rb +41 -0
- data/config/puma_cloudrun.rb +48 -0
- data/config/queue.yml +18 -0
- data/config/recurring.yml +15 -0
- data/config/routes.rb +28 -0
- data/config/storage.yml +34 -0
- data/config.ru +6 -0
- data/db/cable_schema.rb +11 -0
- data/db/cache_schema.rb +14 -0
- data/db/migrate/20250818214709_create_app_profiles.rb +13 -0
- data/db/migrate/20250818214731_create_query_analyses.rb +22 -0
- data/db/migrate/20250818214740_create_query_patterns.rb +22 -0
- data/db/migrate/20250818214805_create_optimization_suggestions.rb +20 -0
- data/db/queue_schema.rb +129 -0
- data/db/schema.rb +79 -0
- data/db/seeds.rb +9 -0
- data/init.sql +9 -0
- data/lib/query_optimizer_client/client.rb +176 -0
- data/lib/query_optimizer_client/configuration.rb +43 -0
- data/lib/query_optimizer_client/generators/install_generator.rb +43 -0
- data/lib/query_optimizer_client/generators/templates/README +46 -0
- data/lib/query_optimizer_client/generators/templates/analysis_job.rb +84 -0
- data/lib/query_optimizer_client/generators/templates/initializer.rb +30 -0
- data/lib/query_optimizer_client/middleware.rb +126 -0
- data/lib/query_optimizer_client/railtie.rb +37 -0
- data/lib/query_optimizer_client/tasks.rake +228 -0
- data/lib/query_optimizer_client/version.rb +5 -0
- data/lib/query_optimizer_client.rb +48 -0
- data/lib/tasks/.keep +0 -0
- data/public/robots.txt +1 -0
- data/query_optimizer_client.gemspec +60 -0
- data/script/.keep +0 -0
- data/storage/.keep +0 -0
- data/storage/development.sqlite3 +0 -0
- data/storage/test.sqlite3 +0 -0
- data/vendor/.keep +0 -0
- metadata +265 -0
data/Dockerfile.cloudrun
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Optimized Dockerfile for Google Cloud Run
|
2
|
+
FROM ruby:3.2-slim AS base
|
3
|
+
|
4
|
+
# Install system dependencies
|
5
|
+
RUN apt-get update -qq && \
|
6
|
+
apt-get install --no-install-recommends -y \
|
7
|
+
curl \
|
8
|
+
libjemalloc2 \
|
9
|
+
libvips \
|
10
|
+
postgresql-client \
|
11
|
+
&& rm -rf /var/lib/apt/lists /var/cache/apt/archives
|
12
|
+
|
13
|
+
# Rails app lives here
|
14
|
+
WORKDIR /rails
|
15
|
+
|
16
|
+
# Set production environment
|
17
|
+
ENV RAILS_ENV="production" \
|
18
|
+
BUNDLE_DEPLOYMENT="1" \
|
19
|
+
BUNDLE_PATH="/usr/local/bundle" \
|
20
|
+
BUNDLE_WITHOUT="development test" \
|
21
|
+
RAILS_SERVE_STATIC_FILES="true" \
|
22
|
+
RAILS_LOG_TO_STDOUT="true"
|
23
|
+
|
24
|
+
# Throw-away build stage to reduce size of final image
|
25
|
+
FROM base AS build
|
26
|
+
|
27
|
+
# Install packages needed to build gems
|
28
|
+
RUN apt-get update -qq && \
|
29
|
+
apt-get install --no-install-recommends -y \
|
30
|
+
build-essential \
|
31
|
+
git \
|
32
|
+
libpq-dev \
|
33
|
+
libyaml-dev \
|
34
|
+
pkg-config \
|
35
|
+
&& rm -rf /var/lib/apt/lists /var/cache/apt/archives
|
36
|
+
|
37
|
+
# Install application gems
|
38
|
+
COPY Gemfile Gemfile.lock ./
|
39
|
+
RUN bundle install && \
|
40
|
+
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
|
41
|
+
bundle exec bootsnap precompile --gemfile
|
42
|
+
|
43
|
+
# Copy application code
|
44
|
+
COPY . .
|
45
|
+
|
46
|
+
# Precompile bootsnap code for faster boot times
|
47
|
+
RUN bundle exec bootsnap precompile app/ lib/
|
48
|
+
|
49
|
+
# Final stage for app image
|
50
|
+
FROM base
|
51
|
+
|
52
|
+
# Copy built artifacts: gems, application
|
53
|
+
COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
|
54
|
+
COPY --from=build /rails /rails
|
55
|
+
|
56
|
+
# Run and own only the runtime files as a non-root user for security
|
57
|
+
RUN groupadd --system --gid 1000 rails && \
|
58
|
+
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
|
59
|
+
mkdir -p /rails/log /rails/tmp && \
|
60
|
+
chown -R rails:rails /rails/log /rails/tmp
|
61
|
+
|
62
|
+
USER rails:rails
|
63
|
+
|
64
|
+
# Cloud Run expects the app to listen on $PORT
|
65
|
+
ENV PORT=8080
|
66
|
+
EXPOSE $PORT
|
67
|
+
|
68
|
+
# Health check for Cloud Run
|
69
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
70
|
+
CMD curl -f http://localhost:$PORT/api/v1/health || exit 1
|
71
|
+
|
72
|
+
# Entrypoint prepares the database
|
73
|
+
ENTRYPOINT ["/rails/bin/docker-entrypoint"]
|
74
|
+
|
75
|
+
# Start server optimized for Cloud Run
|
76
|
+
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb", "-p", "$PORT", "-e", "production"]
|
data/Dockerfile.dev
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
FROM ruby:3.2-slim
|
2
|
+
|
3
|
+
# Install system dependencies
|
4
|
+
RUN apt-get update -qq && \
|
5
|
+
apt-get install --no-install-recommends -y \
|
6
|
+
build-essential \
|
7
|
+
curl \
|
8
|
+
git \
|
9
|
+
libpq-dev \
|
10
|
+
libvips \
|
11
|
+
pkg-config \
|
12
|
+
postgresql-client \
|
13
|
+
&& rm -rf /var/lib/apt/lists /var/cache/apt/archives
|
14
|
+
|
15
|
+
# Set working directory
|
16
|
+
WORKDIR /rails
|
17
|
+
|
18
|
+
# Install gems
|
19
|
+
COPY Gemfile Gemfile.lock ./
|
20
|
+
RUN bundle install
|
21
|
+
|
22
|
+
# Copy application code
|
23
|
+
COPY . .
|
24
|
+
|
25
|
+
# Create non-root user
|
26
|
+
RUN groupadd --system --gid 1000 rails && \
|
27
|
+
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
|
28
|
+
chown -R rails:rails /rails
|
29
|
+
|
30
|
+
USER rails:rails
|
31
|
+
|
32
|
+
# Expose port
|
33
|
+
EXPOSE 3000
|
34
|
+
|
35
|
+
# Start the server
|
36
|
+
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
|
data/GEM_Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in query_optimizer_client.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem "rake", "~> 13.0"
|
9
|
+
gem "rspec", "~> 3.0"
|
10
|
+
gem "rubocop", "~> 1.21"
|
11
|
+
|
12
|
+
# Development dependencies
|
13
|
+
gem "webmock", "~> 3.18"
|
14
|
+
gem "vcr", "~> 6.1"
|
15
|
+
gem "rails", ">= 6.0"
|
16
|
+
gem "sqlite3", "~> 1.4"
|
data/GEM_README.md
ADDED
@@ -0,0 +1,421 @@
|
|
1
|
+
# Rails Database Query Optimizer API
|
2
|
+
|
3
|
+
A powerful Rails API for analyzing and optimizing database queries in real-time. Detect N+1 queries, identify slow queries, suggest missing indexes, and get actionable optimization recommendations.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- 🔍 **N+1 Query Detection**: Automatically detect N+1 query patterns and suggest eager loading solutions
|
8
|
+
- 🐌 **Slow Query Analysis**: Identify slow queries with severity levels and optimization suggestions
|
9
|
+
- 📊 **Missing Index Detection**: Analyze WHERE clauses and suggest database indexes
|
10
|
+
- 🔐 **API Authentication**: Secure API key-based authentication with rate limiting
|
11
|
+
- 📈 **CI/CD Integration**: Built-in scoring system for continuous integration
|
12
|
+
- 🧪 **Comprehensive Testing**: 200+ test examples with full coverage
|
13
|
+
|
14
|
+
## Quick Start
|
15
|
+
|
16
|
+
### 1. Requirements
|
17
|
+
|
18
|
+
- **Ruby**: 3.2.0 or higher
|
19
|
+
- **Rails**: 8.0.0 or higher
|
20
|
+
- **PostgreSQL**: 13+ (required for JSON support)
|
21
|
+
|
22
|
+
### 2. Installation
|
23
|
+
|
24
|
+
Add to your Gemfile:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
gem 'rails', '~> 8.0.0'
|
28
|
+
gem 'pg', '~> 1.1'
|
29
|
+
gem 'pg_query', '~> 4.2'
|
30
|
+
gem 'bcrypt', '~> 3.1.7'
|
31
|
+
gem 'rack-cors'
|
32
|
+
```
|
33
|
+
|
34
|
+
### 3. Database Setup
|
35
|
+
|
36
|
+
```bash
|
37
|
+
rails db:create
|
38
|
+
rails db:migrate
|
39
|
+
```
|
40
|
+
|
41
|
+
### 4. Generate API Key
|
42
|
+
|
43
|
+
```bash
|
44
|
+
rails console
|
45
|
+
```
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
# Create an app profile and generate API key
|
49
|
+
app_profile = AppProfile.create!(name: 'My Rails App')
|
50
|
+
api_key = app_profile.generate_api_key!
|
51
|
+
puts "Your API Key: #{api_key}"
|
52
|
+
```
|
53
|
+
|
54
|
+
### 5. Basic Usage
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
require 'net/http'
|
58
|
+
require 'json'
|
59
|
+
|
60
|
+
# Analyze queries
|
61
|
+
uri = URI('http://localhost:3000/api/v1/analyze')
|
62
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
63
|
+
|
64
|
+
request = Net::HTTP::Post.new(uri)
|
65
|
+
request['Content-Type'] = 'application/json'
|
66
|
+
request['X-API-Key'] = 'your_api_key_here'
|
67
|
+
|
68
|
+
request.body = {
|
69
|
+
queries: [
|
70
|
+
{
|
71
|
+
sql: "SELECT * FROM users WHERE id = 1",
|
72
|
+
duration_ms: 50
|
73
|
+
},
|
74
|
+
{
|
75
|
+
sql: "SELECT * FROM posts WHERE user_id = 1",
|
76
|
+
duration_ms: 200
|
77
|
+
}
|
78
|
+
]
|
79
|
+
}.to_json
|
80
|
+
|
81
|
+
response = http.request(request)
|
82
|
+
result = JSON.parse(response.body)
|
83
|
+
|
84
|
+
puts result['data']['n_plus_one']
|
85
|
+
puts result['data']['slow_queries']
|
86
|
+
puts result['data']['missing_indexes']
|
87
|
+
```
|
88
|
+
|
89
|
+
## API Endpoints
|
90
|
+
|
91
|
+
### Authentication
|
92
|
+
|
93
|
+
All endpoints (except API key creation) require authentication via the `X-API-Key` header.
|
94
|
+
|
95
|
+
### POST /api/v1/api_keys
|
96
|
+
|
97
|
+
Create a new API key.
|
98
|
+
|
99
|
+
**Request:**
|
100
|
+
```json
|
101
|
+
{
|
102
|
+
"app_name": "My Rails Application"
|
103
|
+
}
|
104
|
+
```
|
105
|
+
|
106
|
+
**Response:**
|
107
|
+
```json
|
108
|
+
{
|
109
|
+
"success": true,
|
110
|
+
"data": {
|
111
|
+
"app_name": "My Rails Application",
|
112
|
+
"api_key": "abc123...",
|
113
|
+
"created_at": "2024-01-15T10:30:00Z"
|
114
|
+
}
|
115
|
+
}
|
116
|
+
```
|
117
|
+
|
118
|
+
### POST /api/v1/analyze
|
119
|
+
|
120
|
+
Analyze queries for optimization opportunities.
|
121
|
+
|
122
|
+
**Headers:**
|
123
|
+
- `X-API-Key`: Your API key
|
124
|
+
- `Content-Type`: application/json
|
125
|
+
|
126
|
+
**Request:**
|
127
|
+
```json
|
128
|
+
{
|
129
|
+
"queries": [
|
130
|
+
{
|
131
|
+
"sql": "SELECT * FROM users WHERE id = ?",
|
132
|
+
"duration_ms": 150
|
133
|
+
}
|
134
|
+
]
|
135
|
+
}
|
136
|
+
```
|
137
|
+
|
138
|
+
**Response:**
|
139
|
+
```json
|
140
|
+
{
|
141
|
+
"success": true,
|
142
|
+
"data": {
|
143
|
+
"n_plus_one": {
|
144
|
+
"detected": true,
|
145
|
+
"patterns": [
|
146
|
+
{
|
147
|
+
"table": "posts",
|
148
|
+
"column": "user_id",
|
149
|
+
"query_count": 5,
|
150
|
+
"suggestion": "Use includes(:posts) to preload associations"
|
151
|
+
}
|
152
|
+
]
|
153
|
+
},
|
154
|
+
"slow_queries": [
|
155
|
+
{
|
156
|
+
"sql": "SELECT * FROM users WHERE email LIKE '%@gmail.com'",
|
157
|
+
"duration_ms": 2000,
|
158
|
+
"severity": "very_slow",
|
159
|
+
"suggestions": ["Add index on email column", "Avoid leading wildcards"]
|
160
|
+
}
|
161
|
+
],
|
162
|
+
"missing_indexes": [
|
163
|
+
{
|
164
|
+
"table": "users",
|
165
|
+
"columns": ["email"],
|
166
|
+
"sql": "CREATE INDEX idx_users_email ON users (email);",
|
167
|
+
"priority": "high"
|
168
|
+
}
|
169
|
+
],
|
170
|
+
"summary": {
|
171
|
+
"total_queries": 10,
|
172
|
+
"issues_found": 3,
|
173
|
+
"optimization_score": 75
|
174
|
+
}
|
175
|
+
}
|
176
|
+
}
|
177
|
+
```
|
178
|
+
|
179
|
+
### POST /api/v1/analyze_ci
|
180
|
+
|
181
|
+
Analyze queries for CI/CD integration with pass/fail scoring.
|
182
|
+
|
183
|
+
**Request:**
|
184
|
+
```json
|
185
|
+
{
|
186
|
+
"queries": [...],
|
187
|
+
"threshold_score": 80
|
188
|
+
}
|
189
|
+
```
|
190
|
+
|
191
|
+
**Response:**
|
192
|
+
```json
|
193
|
+
{
|
194
|
+
"success": true,
|
195
|
+
"data": {
|
196
|
+
"score": 85,
|
197
|
+
"passed": true,
|
198
|
+
"threshold": 80,
|
199
|
+
"issues": {
|
200
|
+
"n_plus_one": 1,
|
201
|
+
"slow_queries": 2,
|
202
|
+
"missing_indexes": 1,
|
203
|
+
"total": 4
|
204
|
+
},
|
205
|
+
"recommendations": [
|
206
|
+
"Add eager loading for user associations",
|
207
|
+
"Create index on posts.user_id"
|
208
|
+
]
|
209
|
+
}
|
210
|
+
}
|
211
|
+
```
|
212
|
+
|
213
|
+
### GET /api/v1/health
|
214
|
+
|
215
|
+
Check API health status.
|
216
|
+
|
217
|
+
**Response:**
|
218
|
+
```json
|
219
|
+
{
|
220
|
+
"status": "ok",
|
221
|
+
"timestamp": "2024-01-15T10:30:00Z",
|
222
|
+
"version": "1.0.0",
|
223
|
+
"services": {
|
224
|
+
"database": "ok",
|
225
|
+
"sql_parser": "ok",
|
226
|
+
"analysis_services": "ok"
|
227
|
+
}
|
228
|
+
}
|
229
|
+
```
|
230
|
+
|
231
|
+
## Integration Examples
|
232
|
+
|
233
|
+
### Rails Application Integration
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
# app/services/query_optimizer_service.rb
|
237
|
+
class QueryOptimizerService
|
238
|
+
API_BASE_URL = 'http://localhost:3000/api/v1'
|
239
|
+
|
240
|
+
def initialize(api_key)
|
241
|
+
@api_key = api_key
|
242
|
+
end
|
243
|
+
|
244
|
+
def analyze_queries(queries)
|
245
|
+
uri = URI("#{API_BASE_URL}/analyze")
|
246
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
247
|
+
|
248
|
+
request = Net::HTTP::Post.new(uri)
|
249
|
+
request['Content-Type'] = 'application/json'
|
250
|
+
request['X-API-Key'] = @api_key
|
251
|
+
|
252
|
+
request.body = { queries: queries }.to_json
|
253
|
+
|
254
|
+
response = http.request(request)
|
255
|
+
JSON.parse(response.body)
|
256
|
+
end
|
257
|
+
|
258
|
+
def check_ci_quality(queries, threshold = 80)
|
259
|
+
uri = URI("#{API_BASE_URL}/analyze_ci")
|
260
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
261
|
+
|
262
|
+
request = Net::HTTP::Post.new(uri)
|
263
|
+
request['Content-Type'] = 'application/json'
|
264
|
+
request['X-API-Key'] = @api_key
|
265
|
+
|
266
|
+
request.body = {
|
267
|
+
queries: queries,
|
268
|
+
threshold_score: threshold
|
269
|
+
}.to_json
|
270
|
+
|
271
|
+
response = http.request(request)
|
272
|
+
result = JSON.parse(response.body)
|
273
|
+
|
274
|
+
result.dig('data', 'passed')
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# Usage in your Rails app
|
279
|
+
optimizer = QueryOptimizerService.new(ENV['QUERY_OPTIMIZER_API_KEY'])
|
280
|
+
|
281
|
+
# Collect queries from your application
|
282
|
+
queries = [
|
283
|
+
{
|
284
|
+
sql: User.where(active: true).to_sql,
|
285
|
+
duration_ms: 45
|
286
|
+
}
|
287
|
+
]
|
288
|
+
|
289
|
+
# Analyze for optimization opportunities
|
290
|
+
result = optimizer.analyze_queries(queries)
|
291
|
+
|
292
|
+
if result['data']['n_plus_one']['detected']
|
293
|
+
Rails.logger.warn "N+1 queries detected: #{result['data']['n_plus_one']['patterns']}"
|
294
|
+
end
|
295
|
+
```
|
296
|
+
|
297
|
+
### CI/CD Integration
|
298
|
+
|
299
|
+
```yaml
|
300
|
+
# .github/workflows/query_optimization.yml
|
301
|
+
name: Query Optimization Check
|
302
|
+
|
303
|
+
on: [push, pull_request]
|
304
|
+
|
305
|
+
jobs:
|
306
|
+
query-optimization:
|
307
|
+
runs-on: ubuntu-latest
|
308
|
+
|
309
|
+
steps:
|
310
|
+
- uses: actions/checkout@v2
|
311
|
+
|
312
|
+
- name: Setup Ruby
|
313
|
+
uses: ruby/setup-ruby@v1
|
314
|
+
with:
|
315
|
+
ruby-version: 3.2
|
316
|
+
|
317
|
+
- name: Install dependencies
|
318
|
+
run: bundle install
|
319
|
+
|
320
|
+
- name: Run query optimization check
|
321
|
+
run: |
|
322
|
+
ruby scripts/check_query_optimization.rb
|
323
|
+
env:
|
324
|
+
QUERY_OPTIMIZER_API_KEY: ${{ secrets.QUERY_OPTIMIZER_API_KEY }}
|
325
|
+
OPTIMIZATION_THRESHOLD: 85
|
326
|
+
```
|
327
|
+
|
328
|
+
```ruby
|
329
|
+
# scripts/check_query_optimization.rb
|
330
|
+
require 'net/http'
|
331
|
+
require 'json'
|
332
|
+
|
333
|
+
# Collect sample queries from your test suite
|
334
|
+
queries = [
|
335
|
+
{ sql: "SELECT * FROM users WHERE active = true", duration_ms: 50 },
|
336
|
+
{ sql: "SELECT * FROM posts WHERE user_id = 1", duration_ms: 120 }
|
337
|
+
]
|
338
|
+
|
339
|
+
uri = URI('http://your-optimizer-api.com/api/v1/analyze_ci')
|
340
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
341
|
+
|
342
|
+
request = Net::HTTP::Post.new(uri)
|
343
|
+
request['Content-Type'] = 'application/json'
|
344
|
+
request['X-API-Key'] = ENV['QUERY_OPTIMIZER_API_KEY']
|
345
|
+
|
346
|
+
request.body = {
|
347
|
+
queries: queries,
|
348
|
+
threshold_score: ENV['OPTIMIZATION_THRESHOLD'].to_i
|
349
|
+
}.to_json
|
350
|
+
|
351
|
+
response = http.request(request)
|
352
|
+
result = JSON.parse(response.body)
|
353
|
+
|
354
|
+
if result['data']['passed']
|
355
|
+
puts "✅ Query optimization check passed (Score: #{result['data']['score']})"
|
356
|
+
exit 0
|
357
|
+
else
|
358
|
+
puts "❌ Query optimization check failed (Score: #{result['data']['score']})"
|
359
|
+
puts "Issues found:"
|
360
|
+
result['data']['recommendations'].each { |rec| puts " - #{rec}" }
|
361
|
+
exit 1
|
362
|
+
end
|
363
|
+
```
|
364
|
+
|
365
|
+
## Rate Limits
|
366
|
+
|
367
|
+
- **1000 requests per hour** per API key
|
368
|
+
- Rate limit headers included in responses
|
369
|
+
- Contact support for higher limits
|
370
|
+
|
371
|
+
## Error Handling
|
372
|
+
|
373
|
+
All endpoints return consistent error responses:
|
374
|
+
|
375
|
+
```json
|
376
|
+
{
|
377
|
+
"success": false,
|
378
|
+
"error": "Error message",
|
379
|
+
"errors": ["Detailed error 1", "Detailed error 2"]
|
380
|
+
}
|
381
|
+
```
|
382
|
+
|
383
|
+
Common HTTP status codes:
|
384
|
+
- `400` - Bad Request (validation errors)
|
385
|
+
- `401` - Unauthorized (invalid/missing API key)
|
386
|
+
- `429` - Too Many Requests (rate limit exceeded)
|
387
|
+
- `500` - Internal Server Error
|
388
|
+
|
389
|
+
## Development
|
390
|
+
|
391
|
+
### Running Tests
|
392
|
+
|
393
|
+
```bash
|
394
|
+
bundle exec rspec
|
395
|
+
```
|
396
|
+
|
397
|
+
### Starting the Server
|
398
|
+
|
399
|
+
```bash
|
400
|
+
rails server
|
401
|
+
```
|
402
|
+
|
403
|
+
### Environment Variables
|
404
|
+
|
405
|
+
```bash
|
406
|
+
# .env
|
407
|
+
DATABASE_URL=postgresql://user:password@localhost/query_optimizer_development
|
408
|
+
RAILS_ENV=development
|
409
|
+
```
|
410
|
+
|
411
|
+
## Contributing
|
412
|
+
|
413
|
+
1. Fork the repository
|
414
|
+
2. Create a feature branch
|
415
|
+
3. Add tests for new functionality
|
416
|
+
4. Ensure all tests pass
|
417
|
+
5. Submit a pull request
|
418
|
+
|
419
|
+
## License
|
420
|
+
|
421
|
+
MIT License - see LICENSE file for details.
|
data/GEM_Rakefile
ADDED
data/GEM_gitignore
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
# === SENSITIVE INFORMATION ===
|
2
|
+
# Environment files with API keys and secrets
|
3
|
+
.env*
|
4
|
+
!.env.example
|
5
|
+
|
6
|
+
# RubyGems credentials and API keys
|
7
|
+
.gem/
|
8
|
+
~/.gem/credentials
|
9
|
+
|
10
|
+
# Any files containing API keys or tokens
|
11
|
+
*api_key*
|
12
|
+
*secret*
|
13
|
+
*token*
|
14
|
+
*password*
|
15
|
+
!*example*
|
16
|
+
!*template*
|
17
|
+
!*spec*
|
18
|
+
|
19
|
+
# === GEM DEVELOPMENT ===
|
20
|
+
# Bundler
|
21
|
+
.bundle/
|
22
|
+
vendor/bundle/
|
23
|
+
|
24
|
+
# Built gems
|
25
|
+
*.gem
|
26
|
+
|
27
|
+
# Gem build artifacts
|
28
|
+
pkg/
|
29
|
+
|
30
|
+
# === TESTING ===
|
31
|
+
# RSpec
|
32
|
+
.rspec_status
|
33
|
+
|
34
|
+
# Test coverage
|
35
|
+
coverage/
|
36
|
+
|
37
|
+
# VCR cassettes (may contain API responses with sensitive data)
|
38
|
+
spec/vcr_cassettes/
|
39
|
+
spec/fixtures/vcr_cassettes/
|
40
|
+
|
41
|
+
# Test databases
|
42
|
+
test.db
|
43
|
+
spec/test.db
|
44
|
+
|
45
|
+
# === DEVELOPMENT TOOLS ===
|
46
|
+
# IDE and editor files
|
47
|
+
.vscode/
|
48
|
+
.idea/
|
49
|
+
*.swp
|
50
|
+
*.swo
|
51
|
+
*~
|
52
|
+
|
53
|
+
# Vim
|
54
|
+
*.un~
|
55
|
+
Session.vim
|
56
|
+
|
57
|
+
# Emacs
|
58
|
+
*~
|
59
|
+
\#*\#
|
60
|
+
/.emacs.desktop
|
61
|
+
/.emacs.desktop.lock
|
62
|
+
*.elc
|
63
|
+
auto-save-list
|
64
|
+
tramp
|
65
|
+
.\#*
|
66
|
+
|
67
|
+
# === OS GENERATED FILES ===
|
68
|
+
# macOS
|
69
|
+
.DS_Store
|
70
|
+
.DS_Store?
|
71
|
+
._*
|
72
|
+
.Spotlight-V100
|
73
|
+
.Trashes
|
74
|
+
.fseventsd
|
75
|
+
|
76
|
+
# Windows
|
77
|
+
ehthumbs.db
|
78
|
+
Thumbs.db
|
79
|
+
Desktop.ini
|
80
|
+
|
81
|
+
# Linux
|
82
|
+
*~
|
83
|
+
.directory
|
84
|
+
|
85
|
+
# === DOCUMENTATION ===
|
86
|
+
# Generated documentation
|
87
|
+
doc/
|
88
|
+
.yardoc/
|
89
|
+
.yard/
|
90
|
+
|
91
|
+
# === LOGS ===
|
92
|
+
# Log files
|
93
|
+
*.log
|
94
|
+
log/
|
95
|
+
|
96
|
+
# === TEMPORARY FILES ===
|
97
|
+
# Temporary files
|
98
|
+
tmp/
|
99
|
+
temp/
|
100
|
+
|
101
|
+
# === BACKUP FILES ===
|
102
|
+
*.bak
|
103
|
+
*.backup
|
104
|
+
*.old
|
105
|
+
*.orig
|
106
|
+
|
107
|
+
# === RUBY SPECIFIC ===
|
108
|
+
# IRB history
|
109
|
+
.irb_history
|
110
|
+
|
111
|
+
# Ruby version managers
|
112
|
+
.rvmrc
|
113
|
+
.ruby-version
|
114
|
+
.ruby-gemset
|
115
|
+
|
116
|
+
# === CI/CD ===
|
117
|
+
# GitHub Actions artifacts
|
118
|
+
.github/workflows/artifacts/
|
119
|
+
|
120
|
+
# === DEPLOYMENT ===
|
121
|
+
# Deployment configurations
|
122
|
+
deploy.rb
|
123
|
+
config/deploy/
|
124
|
+
|
125
|
+
# Docker
|
126
|
+
Dockerfile.prod
|
127
|
+
docker-compose.prod.yml
|
128
|
+
|
129
|
+
# === MISC ===
|
130
|
+
# Ignore any local configuration overrides
|
131
|
+
local_config.rb
|
132
|
+
config/local.rb
|
133
|
+
|
134
|
+
# Ignore any personal notes or TODO files
|
135
|
+
TODO.md
|
136
|
+
NOTES.md
|
137
|
+
personal_notes.txt
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Query Optimizer Client
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|