Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ jobs:
fi
echo "Couchbase configuration validated successfully"

- name: Setup Couchbase indexes
run: RAILS_ENV=test bundle exec rake couchbase:setup_indexes

- name: Run integration tests
run: bundle exec rspec spec/requests/api/v1

Expand Down
19 changes: 1 addition & 18 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,9 @@ ruby '3.4.1'
# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem 'rails', '~> 7.1.3', '>= 7.1.3.2'

# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
gem 'sprockets-rails'

# Use sqlite3 as the database for Active Record
gem 'sqlite3', '~> 1.4'

# Use the Puma web server [https://github.com/puma/puma]
gem 'puma', '>= 5.0'

# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
gem 'importmap-rails'

# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
gem 'turbo-rails'

# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
gem 'stimulus-rails'

# Build JSON APIs with ease [https://github.com/rails/jbuilder]
gem 'jbuilder'

Expand Down Expand Up @@ -72,7 +57,5 @@ group :development do
end

group :test do
# Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
gem 'capybara'
gem 'selenium-webdriver'
# API-only testing with RSpec (no system/browser testing needed)
end
85 changes: 85 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,91 @@ production:

> Note: The connection string expects the `couchbases://` or `couchbase://` part.

## Couchbase Index Management

This application requires specific N1QL indexes on the `travel-sample` bucket to function correctly. These indexes optimize the SQL++ queries used by the application for filtering and joining documents.

### Automatic Index Setup

Indexes are automatically created when you:

- Run `bin/setup` for local development setup
- Run tests in CI/CD (GitHub Actions automatically creates indexes before running tests)

The application uses idempotent index creation (using `CREATE INDEX IF NOT EXISTS`), so it's safe to run the setup multiple times.

### Required Indexes

The application requires the following indexes on the `travel-sample` bucket:

1. **`idx_type`** - General index on the `type` field for all document queries
2. **`idx_type_country`** - Index for airline queries filtered by country (`Airline.list_by_country_or_all`)
3. **`idx_type_destinationairport`** - Index for route queries by destination airport (`Airline.to_airport`)
4. **`idx_type_sourceairport_stops`** - Index for route queries by source airport and stops (`Route.direct_connections`)
5. **`idx_type_airlineid`** - Index for airline queries by airline ID (used in joins with routes)

### Manual Index Management

You can manually manage indexes using the following Rake tasks:

#### Create All Required Indexes

```sh
bundle exec rake couchbase:setup_indexes
```

This command creates all required indexes on the `travel-sample` bucket. It's idempotent and safe to run multiple times.

#### List All Indexes

```sh
bundle exec rake couchbase:list_indexes
```

This command lists all indexes currently present in the `travel-sample` bucket, including their state, type, and indexed fields.

#### Drop Application Indexes

```sh
bundle exec rake couchbase:drop_indexes
```

This command drops all application-managed indexes. It requires confirmation before executing. For automated scripts, you can force the operation:

```sh
FORCE_DROP=true bundle exec rake couchbase:drop_indexes
```

> **Warning**: Use with caution! Dropping indexes will cause queries to fail until indexes are recreated.

### Troubleshooting Index Issues

If you encounter index-related errors:

1. **Verify indexes exist**:
```sh
bundle exec rake couchbase:list_indexes
```

2. **Check index state**: Indexes should be in "online" state. If they're "building" or "pending", wait for them to complete.

3. **Recreate indexes**:
```sh
bundle exec rake couchbase:drop_indexes
bundle exec rake couchbase:setup_indexes
```

4. **Check permissions**: Ensure your Couchbase user has "Query Manage Index" permission to create and drop indexes.

### Index Creation in CI/CD

The GitHub Actions workflow automatically creates indexes before running tests. If index creation fails in CI:

1. Check the "Setup Couchbase indexes" step in the GitHub Actions log
2. Verify that `DB_CONN_STR`, `DB_USERNAME`, and `DB_PASSWORD` secrets/variables are correctly set
3. Ensure the Couchbase user has "Query Manage Index" permission
4. Check that the Couchbase cluster is accessible from GitHub Actions runners

## Running The Application

### Directly on machine
Expand Down
4 changes: 0 additions & 4 deletions app/channels/application_cable/channel.rb

This file was deleted.

4 changes: 0 additions & 4 deletions app/channels/application_cable/connection.rb

This file was deleted.

3 changes: 0 additions & 3 deletions app/javascript/application.js

This file was deleted.

9 changes: 0 additions & 9 deletions app/javascript/controllers/application.js

This file was deleted.

7 changes: 0 additions & 7 deletions app/javascript/controllers/hello_controller.js

This file was deleted.

11 changes: 0 additions & 11 deletions app/javascript/controllers/index.js

This file was deleted.

2 changes: 1 addition & 1 deletion app/models/airline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ class Airline < CouchbaseOrm::Base
}

n1ql :to_airport, query_fn: proc { |bucket, values, options|
cluster.query("SELECT raw META(air).id FROM (SELECT DISTINCT META(airline).id AS airlineId FROM `#{bucket.name}` AS route JOIN `#{bucket.name}` AS airline ON route.airlineid = META(airline).id WHERE route.destinationairport = #{quote(values[0])}) AS subquery JOIN `#{bucket.name}` AS air ON META(air).id = subquery.airlineId LIMIT #{values[1]} OFFSET #{values[2]}", options)
cluster.query("SELECT raw META(air).id FROM (SELECT DISTINCT META(airline).id AS airlineId FROM `#{bucket.name}` AS route JOIN `#{bucket.name}` AS airline ON route.airlineid = META(airline).id AND airline.type = 'airline' WHERE route.type = 'route' AND route.destinationairport = #{quote(values[0])} ORDER BY META(airline).id) AS subquery JOIN `#{bucket.name}` AS air ON META(air).id = subquery.airlineId AND air.type = 'airline' LIMIT #{values[1]} OFFSET #{values[2]}", options)
}
end
2 changes: 1 addition & 1 deletion app/models/route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ class Route < CouchbaseOrm::Base
validates :distance, presence: true, numericality: { greater_than_or_equal_to: 0 }

n1ql :direct_connections, query_fn: proc { |bucket, values, options|
cluster.query("SELECT distinct raw meta(route).id FROM `#{bucket.name}` AS airport JOIN `#{bucket.name}` AS route ON route.sourceairport = airport.faa WHERE airport.faa = #{quote(values[0])} AND route.stops = 0 LIMIT #{values[1]} OFFSET #{values[2]}", options)
cluster.query("SELECT distinct raw meta(route).id FROM `#{bucket.name}` AS airport JOIN `#{bucket.name}` AS route ON route.sourceairport = airport.faa AND route.type = 'route' WHERE airport.type = 'airport' AND airport.faa = #{quote(values[0])} AND route.stops = 0 LIMIT #{values[1]} OFFSET #{values[2]}", options)
}
end
3 changes: 3 additions & 0 deletions bin/setup
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ FileUtils.chdir APP_ROOT do
puts "\n== Preparing database =="
system! "bin/rails db:prepare"

puts "\n== Setting up Couchbase indexes =="
system! "bin/rails couchbase:setup_indexes"

puts "\n== Removing old logs and tempfiles =="
system! "bin/rails log:clear tmp:clear"

Expand Down
12 changes: 11 additions & 1 deletion config/application.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
require_relative 'boot'

require 'rails/all'
# Load Rails components individually (excluding activerecord since we use Couchbase via couchbase-orm)
require 'rails'
require 'action_controller/railtie'
require 'action_view/railtie'
require 'action_mailer/railtie'
require 'active_job/railtie'

# Excluded components (using Couchbase instead of ActiveRecord):
# - active_record/railtie - Using Couchbase via couchbase-orm
# - active_storage/engine - Not needed for API-only app
# - action_cable/railtie - Not using WebSockets

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Expand Down
22 changes: 8 additions & 14 deletions config/database.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
# SQLite. Versions 3.8.0 and up are supported.
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem "sqlite3"
# NOTE: This file is not used by the application.
# This application uses Couchbase as its database through the couchbase-orm gem.
# Database connections are configured in config/initializers/couchbase.rb
#
# The configuration below is kept for Rails compatibility but is not actively used.
# No SQL database (SQLite, PostgreSQL, MySQL) is required for this application.

# Minimal configuration to prevent Rails from attempting database connections
default: &default
adapter: sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
adapter: nulldb

development:
<<: *default
database: storage/development.sqlite3

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: storage/test.sqlite3

production:
<<: *default
database: storage/production.sqlite3
8 changes: 4 additions & 4 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
end

# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
# config.active_storage.service = :local # Commented out - not using ActiveStorage

# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
Expand All @@ -51,16 +51,16 @@
config.active_support.disallowed_deprecation_warnings = []

# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
# config.active_record.migration_error = :page_load # Commented out - not using ActiveRecord

# Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true
# config.active_record.verbose_query_logs = true # Commented out - not using ActiveRecord

# Highlight code that enqueued background job in logs.
config.active_job.verbose_enqueue_logs = true

# Suppress logger output for asset requests.
config.assets.quiet = true
# config.assets.quiet = true # Commented out - not using asset pipeline

# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true
Expand Down
6 changes: 3 additions & 3 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
# config.assets.css_compressor = :sass

# Do not fall back to assets pipeline if a precompiled asset is missed.
config.assets.compile = false
# config.assets.compile = false # Commented out - not using asset pipeline

# Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.asset_host = "http://assets.example.com"
Expand All @@ -37,7 +37,7 @@
# config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX

# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
# config.active_storage.service = :local # Commented out - not using ActiveStorage

# Mount Action Cable outside main process or domain.
# config.action_cable.mount_path = nil
Expand Down Expand Up @@ -85,7 +85,7 @@
config.active_support.report_deprecations = false

# Do not dump schema after migrations.
config.active_record.dump_schema_after_migration = false
# config.active_record.dump_schema_after_migration = false # Commented out - not using ActiveRecord

# Enable DNS rebinding protection and other `Host` header attacks.
# config.hosts = [
Expand Down
2 changes: 1 addition & 1 deletion config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
config.action_controller.allow_forgery_protection = false

# Store uploaded files on the local file system in a temporary directory.
config.active_storage.service = :test
# config.active_storage.service = :test # Commented out - not using ActiveStorage

config.action_mailer.perform_caching = false

Expand Down
7 changes: 0 additions & 7 deletions config/importmap.rb

This file was deleted.

12 changes: 0 additions & 12 deletions config/initializers/assets.rb

This file was deleted.

9 changes: 0 additions & 9 deletions db/seeds.rb

This file was deleted.

Loading