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

Modularize Session Store #147

Merged
merged 20 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from 18 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
85 changes: 47 additions & 38 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ on:
branches:
- main

env:
ruby_version: 3.3

jobs:
test:
runs-on: ubuntu-latest
Expand All @@ -19,7 +22,7 @@ jobs:
rails: ['7.0', 7.1, 7.2, main]

exclude:
# Rails 7.1 dropped support for older rubygems
# Rails 7.2 dropped support for older rubygems
- rails: 7.2
ruby: 2.7
- rails: 7.2
Expand Down Expand Up @@ -61,49 +64,55 @@ jobs:
rubocop:
runs-on: ubuntu-latest

steps:
- name: Set up Ruby 3.3
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.3

- uses: actions/checkout@v4

- name: Install gems
run: bundle install

- name: Rubocop
run: bundle exec rubocop -E -S

test-sample-app:
runs-on: ubuntu-latest
services:
dynamodb:
image: amazon/dynamodb-local:latest
ports:
- 8000:8000
env:
AWS_REGION: us-east-1
AWS_ACCESS_KEY_ID: dummy
AWS_SECRET_ACCESS_KEY: dummy
AWS_ENDPOINT_URL: http://localhost:8000
steps:
- uses: actions/checkout@v4

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
working-directory: ${{ github.workspace }}/sample_app
ruby-version: ${{ env.ruby_version }}

- name: Create table
run: cd ${{ github.workspace }}/sample_app && bin/rails runner Aws::SessionStore::DynamoDB::Table.create_table

- name: Insert dummy manifest.json
- name: Install gems
run: |
mkdir -p ${{ github.workspace }}/sample_app/public/packs-test
echo "{\"application.js\":\"/packs-test/js/application-dummy.js\"}" >> ${{ github.workspace }}/sample_app/public/packs-test/manifest.json
bundle config set --local with 'development'
bundle install

- name: Test
run: bundle exec rake test
working-directory: ${{ github.workspace }}/sample_app
- name: Rubocop
run: bundle exec rake rubocop

# test-sample-app:
# runs-on: ubuntu-latest
# services:
# dynamodb:
# image: amazon/dynamodb-local:latest
# ports:
# - 8000:8000
# env:
# AWS_REGION: us-east-1
# AWS_ACCESS_KEY_ID: dummy
# AWS_SECRET_ACCESS_KEY: dummy
# AWS_ENDPOINT_URL: http://localhost:8000
# steps:
# - uses: actions/checkout@v4
#
# - name: Setup Ruby
# uses: ruby/setup-ruby@v1
# with:
# ruby-version: ${{ env.ruby_version }}
# bundler-cache: true
# working-directory: ${{ github.workspace }}/sample_app
#
# - name: Create table
# run: cd ${{ github.workspace }}/sample_app && bin/rails runner Aws::SessionStore::DynamoDB::Table.create_table
#
# - name: Insert dummy manifest.json
# run: |
# mkdir -p ${{ github.workspace }}/sample_app/public/packs-test
# echo "{\"application.js\":\"/packs-test/js/application-dummy.js\"}" >> ${{ github.workspace }}/sample_app/public/packs-test/manifest.json
#
# - name: DB Migrate
# run: cd ${{ github.workspace }}/sample_app && bin/rails db:migrate
#
# - name: Test
# run: bundle exec rake test
# working-directory: ${{ github.workspace }}/sample_app
31 changes: 18 additions & 13 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
.bundle
.byebug_history
.yardoc
.ruby-version
.DS_Store

/.byebug_history
/.bundle
/.yardoc
/doc
/Gemfile.lock
/coverage
*.gem
coverage
doc
docs.zip
Gemfile.lock
sample_app/Gemfile.lock
/.release

.idea/

gemfiles/*.gemfile.lock

test/dummy/db/migrate
test/dummy/log/

sample_app/Gemfile.lock
spec/dummy/db/test.db-shm
spec/dummy/db/test.db-wal
spec/dummy/db/test.db
spec/dummy/db/schema.rb
spec/dummy/log/
spec/dummy/tmp/
vendor
.idea/
.release/
spec/dummy/tmp/
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ Style/Documentation:
Exclude:
- 'lib/generators/**/*.rb'
- 'lib/aws/rails/notifications.rb'
- 'test/**/*.rb'
- 'spec/**/*.rb'
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Unreleased Changes
------------------

* Feature - Add session store config generation.

* Feature - Prepare modularization of `aws-sessionstore-dynamodb`.

4.1.0 (2024-09-27)
------------------

Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ group :development, :test do
end

group :development do
gem 'byebug', platforms: :ruby
gem 'rubocop'
end

group :test do
gem 'bcrypt'
gem 'minitest-spec-rails'
gem 'rspec-rails'
gem 'webmock'
end
Expand Down
114 changes: 85 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ This gem also brings in the following AWS gems:
* `aws-sdk-sqs`
* `aws-sdk-sns`
* `aws-record`
* `aws-sessionstore-dynamodb`

You will have to ensure that you provide credentials for the SDK to use. See the
latest [AWS SDK for Ruby Docs](https://docs.aws.amazon.com/sdk-for-ruby/v3/api/index.html#Configuration)
Expand Down Expand Up @@ -72,7 +71,35 @@ configuration: they will be loaded automatically.

You can configure session storage in Rails to use DynamoDB instead of cookies,
allowing access to sessions from other applications and devices. You will need
to have an existing Amazon DynamoDB session table to use this feature.
to have or create an existing Amazon DynamoDB session table to use this feature.

To enable this feature, add the following to your Gemfile:

```ruby
gem 'aws-sessionstore-dynamodb', '~> 2'
```

For more information about this feature and configuration options, see the
[API reference](https://docs.aws.amazon.com/sdk-for-ruby/aws-sessionstore-dynamodb/api/)
and the
[GitHub repository](https://github.com/aws/aws-sessionstore-dynamodb-ruby).

### Configuration generator

You can generate a configuration file for the session store using the following
command (--environment=<environment> is optional):

```bash
rails generate dynamo_db:session_store_config --environment=<Rails.env>
```

The session store configuration generator command will generate a YAML file
`config/dynamo_db_session_store.yml` with default options. If provided an
environment, the file will be named
`config/dynamo_db_session_store/<Rails.env>.yml`, which takes precedence over
the default file.

### ActiveRecord Migration generator

You can generate a migration file for the session table using the following
command (<MigrationName> is optional):
Expand All @@ -81,29 +108,55 @@ command (<MigrationName> is optional):
rails generate dynamo_db:session_store_migration <MigrationName>
```

The session store migration generator command will generate two files: a
migration file, `db/migration/#{VERSION}_#{MIGRATION_NAME}.rb`, and a
configuration YAML file, `config/dynamo_db_session_store.yml`.
The session store migration generator command will generate a
migration file `db/migration/#{VERSION}_#{MIGRATION_NAME}.rb`.

The migration file will create and delete a table with default options. These
options can be changed prior to running the migration and are documented in the
[Table](https://docs.aws.amazon.com/sdk-for-ruby/aws-sessionstore-dynamodb/api/Aws/SessionStore/DynamoDB/Table.html) class.
options can be changed prior to running the migration either in code by editing
the migration file or in the config YAML file. These options are documented in
the
[Table](https://docs.aws.amazon.com/sdk-for-ruby/aws-sessionstore-dynamodb/api/Aws/SessionStore/DynamoDB/Table.html)
class.

To create the table, run migrations as normal with:

```bash
rails db:migrate
```

Next, configure the Rails session store to be `:dynamodb_store` by editing
`config/initializers/session_store.rb` to contain the following:
To delete the table and rollback, run the following command:

```bash
rails db:migrate:down VERSION=<VERSION>
```

### Migration Rake tasks

If you are not using ActiveRecord, you can manage the table using the provided
Rake tasks:

```bash
rake dynamo_db:session_store:create_table
rake dynamo_db:session_store:delete_table
```

The rake tasks will create and delete a table with default options. These
options can be changed prior to running the task in the config YAML file. These
options are documented in the
[Table](https://docs.aws.amazon.com/sdk-for-ruby/aws-sessionstore-dynamodb/api/Aws/SessionStore/DynamoDB/Table.html)
class.

### Usage

To use the session store, add or edit your
`config/initializers/session_store.rb` file:

```ruby
# config/initializers/session_store.rb
Rails.application.config.session_store :dynamodb_store, key: '_your_app_session'
options = { table_name: '_your_app_session' } # overrides from YAML or ENV
Rails.application.config.session_store :dynamo_db_store, **options
```

You can now start your Rails application with session support.
You can now start your Rails application with DynamoDB session support.

### Configuration

Expand All @@ -113,38 +166,41 @@ initializer like so:

```ruby
# config/initializers/session_store.rb
Rails.application.config.session_store :dynamodb_store,
key: '_your_app_session',
table_name: 'foo',
Rails.application.config.session_store :dynamo_db_store,
table_name: 'your-table-name',
table_key: 'your-session-key',
dynamo_db_client: my_ddb_client
```

Alternatively, you can use the generated YAML configuration file
`config/dynamo_db_session_store.yml`. YAML configuration may also be specified
per environment, with environment configuration having precedence. To do this,
create `config/dynamo_db_session_store/#{Rails.env}.yml` files as needed.

For configuration options, see the [Configuration](https://docs.aws.amazon.com/sdk-for-ruby/aws-sessionstore-dynamodb/api/Aws/SessionStore/DynamoDB/Configuration.html) class.
Alternatively, you can use the generated YAML configuration files
`config/dynamo_db_session_store.yml` or
`config/dynamo_db_session_store/<Rails.env>.yml`.

#### Rack Configuration
For configuration options, see the [Configuration]
(https://docs.aws.amazon.com/sdk-for-ruby/aws-sessionstore-dynamodb/api/Aws/SessionStore/DynamoDB/Configuration.html)
class.

DynamoDB session storage is implemented in the [\`aws-sessionstore-dynamodb\`](https://github.com/aws/aws-sessionstore-dynamodb-ruby)
gem. The Rack middleware inherits from the [\`Rack::Session::Abstract::Persisted\`](https://www.rubydoc.info/github/rack/rack/Rack/Session/Abstract/Persisted)
The session store inherits from the
[`Rack::Session::Abstract::Persisted`](https://rubydoc.info/github/rack/rack-session/main/Rack/Session/Abstract/Persisted)
class, which also includes additional options (such as `:key`) that can be
passed into the Rails initializer.

### Cleaning old sessions

By default sessions do not expire. See `config/dynamo_db_session_store.yml` to
By default sessions do not expire. You can use `:max_age` and `:max_stale` to
configure the max age or stale period of a session.

You can use the DynamoDB [Time to Live (TTL) feature](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html)
on the `expire_at` attribute to automatically delete expired items.
You can use the DynamoDB
[Time to Live (TTL) feature](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html)
on the `expire_at` attribute to automatically delete expired items, saving you
the trouble of manually deleting them and reducing costs.

Alternatively, a Rake task for garbage collection is provided:
If you wish to delete old sessions based on creation age (invalidating valid
sessions) or if you want control over the garbage collection process, you can
use the provided Rake task:

```bash
rake dynamo_db:collect_garbage
rake dynamo_db:session_store:clean
```

## Amazon Simple Email Service (SES) as an ActionMailer Delivery Method
Expand Down
12 changes: 11 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'rspec/core/rake_task'
require 'rake/testtask'
require 'rubocop/rake_task'

$REPO_ROOT = File.dirname(__FILE__)
Expand All @@ -14,6 +15,15 @@ RuboCop::RakeTask.new

RSpec::Core::RakeTask.new(:spec)

# Eventually, migrate all tests back into the minitest
# runner. But use minitest-spec-rails to enable syntax.
# Currently, rails generator specs are not running.
Rake::TestTask.new('test:rails') do |t|
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.warning = false
end

task :db_migrate do
Dir.chdir('spec/dummy') do
version = ENV.delete('VERSION') # ActiveRecord uses this
Expand All @@ -22,6 +32,6 @@ task :db_migrate do
end
end

task test: %i[db_migrate spec]
task test: [:db_migrate, :spec, 'test:rails']
task default: :test
task 'release:test' => :test
2 changes: 1 addition & 1 deletion gemfiles/rails-main.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ group :test do
# this is not published for some reason
git: 'https://github.com/jruby/activerecord-jdbc-adapter',
glob: 'activerecord-jdbcsqlite3-adapter/activerecord-jdbcsqlite3-adapter.gemspec'
gem 'sqlite3', '~> 2.0.0', platform: :ruby
gem 'sqlite3', platform: :ruby
end
Loading