From e1b46f462eeb03efd72e73f14d1714b9fdf7dbf3 Mon Sep 17 00:00:00 2001 From: Alessandro Desantis Date: Tue, 17 Jul 2018 14:33:38 +0200 Subject: [PATCH] Implement Pragma::Operation hooks --- .rubocop.yml | 3 + lib/pragma/migration.rb | 35 +++++++ lib/pragma/migration/hooks/operation.rb | 37 +++++++ lib/pragma/migration/middleware.rb | 20 +--- pragma-migration.gemspec | 1 + spec/pragma/migration/hooks/operation_spec.rb | 97 +++++++++++++++++++ spec/pragma/migration/middleware_spec.rb | 14 +-- spec/pragma/migration/migration_spec.rb | 11 +++ spec/spec_helper.rb | 1 + 9 files changed, 198 insertions(+), 21 deletions(-) create mode 100644 lib/pragma/migration/hooks/operation.rb create mode 100644 spec/pragma/migration/hooks/operation_spec.rb create mode 100644 spec/pragma/migration/migration_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index 934edb3..3f80120 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -26,6 +26,9 @@ Style/BlockDelimiters: Exclude: - 'spec/**/*' +Style/Documentation: + Enabled: false + Layout/AlignParameters: EnforcedStyle: with_fixed_indentation diff --git a/lib/pragma/migration.rb b/lib/pragma/migration.rb index 5bb0cad..4489eeb 100644 --- a/lib/pragma/migration.rb +++ b/lib/pragma/migration.rb @@ -9,10 +9,45 @@ require 'pragma/migration/runner' require 'pragma/migration/bond' require 'pragma/migration/middleware' +require 'pragma/migration/hooks/operation' require 'pragma/migration/gem_version' module Pragma # Provides API payload migrations to support clients on older versions of your API. module Migration + # The default +user_version_proc+. + DEFAULT_USER_VERSION_PROC = lambda do |request| + request.get_header('X-Api-Version') + end + + class << self + # @!attribute [rw] repository + # @return [Pragma::Migration::Repository] your migrations repository + # + # @!attribute [rw] user_version_proc + # @return [Object] a callable taking a +Rack::Request+ as argument and returning an API + # version identifier + attr_accessor :repository + attr_writer :user_version_proc + + def user_version_proc + @user_version_proc ||= DEFAULT_USER_VERSION_PROC + end + + # Returns the user's API version for the given request. + # + # @param request [Rack::Request] the request + # + # @return [String] the API version identifier + def user_version_from(request) + version = user_version_proc.call(request) + + if version && repository.sorted_versions.include?(version) + version + else + repository.sorted_versions.last.number + end + end + end end end diff --git a/lib/pragma/migration/hooks/operation.rb b/lib/pragma/migration/hooks/operation.rb new file mode 100644 index 0000000..ab129bd --- /dev/null +++ b/lib/pragma/migration/hooks/operation.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Pragma + module Migration + module Hooks + module Operation + def rolled_migrations(options) + build_migration_bond(options).rolled_migrations + end + + def applying_migrations(options) + build_migration_bond(options).applying_migrations + end + + def migration_rolled?(options, migration) + build_migration_bond(options).migration_rolled?(migration) + end + + def migration_applies?(options, migration) + build_migration_bond(options).migration_applies?(migration) + end + + private + + def build_migration_bond(options) + Bond.new( + repository: Pragma::Migration.repository, + request: options['rack.request'], + user_version: Pragma::Migration.user_version_from(options['rack.request']) + ) + end + + ::Pragma::Operation::Base.prepend(self) if defined?(::Pragma::Operation::Base) + end + end + end +end diff --git a/lib/pragma/migration/middleware.rb b/lib/pragma/migration/middleware.rb index f875daf..34e9283 100644 --- a/lib/pragma/migration/middleware.rb +++ b/lib/pragma/migration/middleware.rb @@ -15,20 +15,11 @@ module Migration # request.get_header 'X-Api-Version' # end) class Middleware - # The default for +user_version_proc+. - DEFAULT_VERSION_PROC = lambda do |request| - request.get_header('X-Api-Version') - end - # Initializes the middleware. # # @param app [Object] your app - # @param repository [Repository] your migration repository - # @param user_version_proc [Proc] a proc that takes a request and returns a version number - def initialize(app, repository:, user_version_proc: DEFAULT_VERSION_PROC) + def initialize(app) @app = app - @repository = repository - @user_version_proc = user_version_proc end # Executes the middleware. @@ -51,9 +42,9 @@ def call(env) original_request = Rack::Request.new(env) runner = Runner.new(Bond.new( - repository: @repository, + repository: repository, request: original_request, - user_version: user_version_from(original_request) + user_version: Pragma::Migration.user_version_from(original_request) )) migrated_request = runner.run_upwards @@ -67,9 +58,8 @@ def call(env) private - def user_version_from(request) - version = @user_version_proc.call(request) - @repository.sorted_versions.include?(version) ? version : @repository.sorted_versions.last + def repository + Pragma::Migration.repository end end end diff --git a/pragma-migration.gemspec b/pragma-migration.gemspec index f0ef15b..243d799 100644 --- a/pragma-migration.gemspec +++ b/pragma-migration.gemspec @@ -25,6 +25,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'rack', '~> 2.0' spec.add_development_dependency 'coveralls' + spec.add_development_dependency 'pragma-operation' spec.add_development_dependency 'rake' spec.add_development_dependency 'rspec' spec.add_development_dependency 'rubocop' diff --git a/spec/pragma/migration/hooks/operation_spec.rb b/spec/pragma/migration/hooks/operation_spec.rb new file mode 100644 index 0000000..903d1aa --- /dev/null +++ b/spec/pragma/migration/hooks/operation_spec.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +RSpec.describe Pragma::Migration::Hooks::Operation do + subject(:result) do + operation_klass.call( + {}, + { + 'rack.request' => Rack::Request.new('X-Test-Api-Version' => api_version) + } + ) + end + + before do + module API + module Migration + class SendNewsletterAutomatically < Pragma::Migration::Base + end + end + + class MigrationRepository < Pragma::Migration::Repository + version '2018-07-15' + + version '2018-07-16', [ + API::Migration::SendNewsletterAutomatically + ] + end + end + + Pragma::Migration.repository = API::MigrationRepository + + Pragma::Migration.user_version_proc = lambda do |request| + request.get_header 'X-Test-Api-Version' + end + end + + let(:operation_klass) do + Class.new(Pragma::Operation::Base) do + success :check_migrations! + + def check_migrations!(options, **) + options['result.migration_rolled'] = migration_rolled?( + options, + API::Migration::SendNewsletterAutomatically + ) + + options['result.migration_rolled'] = migration_applies?( + options, + API::Migration::SendNewsletterAutomatically + ) + + options['result.rolled_migrations'] = rolled_migrations(options) + + options['result.applying_migrations'] = applying_migrations(options) + end + end + end + + context 'when the migration is not rolled' do + let(:api_version) { '2018-07-15' } + + it 'computes whether a migration was rolled' do + expect(result['result.migration_rolled']).to eq(false) + end + + it 'computes whether a migration applies' do + expect(result['result.migration_applies']).to eq(true) + end + + it 'returns the correct rolled migrations' do + expect(result['result.rolled_migrations']).to eq([]) + end + + it 'returns the correct applying migrations' do + expect(result['result.applying_migrations']).to eq([API::Migration::SendNewsletterAutomatically]) + end + end + + context 'when the migration is rolled' do + let(:api_version) { '2018-07-16' } + + it 'marks the newsletter as sent' do + expect(result['result.migration_rolled']).to eq(true) + end + + it 'computes whether a migration applies' do + expect(result['result.migration_applies']).to eq(false) + end + + it 'returns the correct rolled migrations' do + expect(result['result.rolled_migrations']).to eq([API::Migration::SendNewsletterAutomatically]) + end + + it 'returns the correct applying migrations' do + expect(result['result.applying_migrations']).to eq([]) + end + end +end diff --git a/spec/pragma/migration/middleware_spec.rb b/spec/pragma/migration/middleware_spec.rb index efdc2dc..b9d3163 100644 --- a/spec/pragma/migration/middleware_spec.rb +++ b/spec/pragma/migration/middleware_spec.rb @@ -2,12 +2,14 @@ RSpec.describe Pragma::Migration::Middleware do subject do - described_class.new(app, - repository: repository, - user_version_proc: (lambda do |request| - request.get_header 'X-Test-Api-Version' - end) - ) + described_class.new(app) + end + + before do + Pragma::Migration.repository = repository + Pragma::Migration.user_version_proc = lambda do |request| + request.get_header 'X-Test-Api-Version' + end end let(:app) do diff --git a/spec/pragma/migration/migration_spec.rb b/spec/pragma/migration/migration_spec.rb new file mode 100644 index 0000000..11ee4fa --- /dev/null +++ b/spec/pragma/migration/migration_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +RSpec.describe Pragma::Migration do + describe '#repository=' do + it 'changes the repository' do + expect { + subject.repository = 'test' + }.to change(subject, :repository).to('test') + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9bfc980..2bdc53a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,5 @@ require "bundler/setup" +require 'pragma/operation' require "pragma/migration" require 'coveralls'