sealink-param-validation 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9e5cff0982bbc8a5852373b1ee1f107a75622da013973e92015ebd911217240c
4
+ data.tar.gz: af56f88956411b92e1e30381c7cf3df60472feb1aa9f790f612c679987b9c9e8
5
+ SHA512:
6
+ metadata.gz: fc3f83d5da4ba310fe3ec86b8aee1688ac23d7737cda3321da5a2d3629c37a00921d7a4cb687c3d80db9cf41d8683a8bcf6bbb2ef232c6bf1098b80f782094f7
7
+ data.tar.gz: ed7ceff1bec538adbbf395ba916bff8970a9c62ef913b06466973ff1caa4395e12b0e11a01f53788c71d6e732797011bd5b97427778bc88174a24c1f3e1014fa
@@ -0,0 +1,4 @@
1
+ pkg
2
+ coverage
3
+ Gemfile.lock
4
+ tmp
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.6
4
+ - ruby-head
5
+ script: bundle exec rspec
6
+ gemfile:
7
+ - gemfiles/rails5.gemfile
8
+ - gemfiles/rails6.gemfile
9
+ notifications:
10
+ email:
11
+ - support@travellink.com.au
12
+ sudo: false
13
+ cache: bundler
@@ -0,0 +1,5 @@
1
+ # Sealink Param Validation
2
+
3
+ ## 0.1.0
4
+
5
+ * [TT-6430] Extract our param validation from QuickTravel
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,17 @@
1
+ # Sealink Param Validation
2
+
3
+ This is our gem for validation our params with schemas.
4
+
5
+ ## Development Environment
6
+
7
+ ### Prerequisites
8
+
9
+ 1. Ruby
10
+
11
+ ### General Steps
12
+
13
+ 1. bundle install
14
+
15
+ ### Testing
16
+
17
+ 1. bundle exec rspec
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,2 @@
1
+ test:
2
+ secret_key_base: AeQu8kaeshohyedaequei6onei9loh3z
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+ gemspec path: '../'
3
+
4
+ group :development, :test do
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'rspec-rails'
8
+ gem 'simplecov'
9
+ gem 'simplecov-rcov'
10
+ gem 'rails', '~> 5.0'
11
+ end
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+ gemspec path: '../'
3
+
4
+ group :development, :test do
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'rspec-rails'
8
+ gem 'simplecov'
9
+ gem 'simplecov-rcov'
10
+ gem 'rails', '~> 6.0'
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sealink_param_validation/version'
4
+ require 'sealink_param_validation/error'
5
+ require 'sealink_param_validation/helper'
6
+ require 'sealink_param_validation/concern'
7
+
8
+ module SealinkParamValidation
9
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module SealinkParamValidation
6
+ module Concern
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ # Accessor for validation results hash
11
+ attr_reader :validation_result
12
+ class_attribute :schemas
13
+ self.schemas = {}
14
+
15
+ def self.schema_for(action, schema = nil)
16
+ schemas[action.to_sym] = schema if schema
17
+ before_action :ensure_schema
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def ensure_schema!
24
+ @validation_result = schema_for_action.call(params.to_unsafe_h)
25
+ return if @validation_result.success?
26
+ error = SealinkParamValidation::Helper.generate_humanized_error_message(@validation_result)
27
+ fail SealinkParamValidation::InvalidInputError, error
28
+ end
29
+
30
+ def ensure_schema
31
+ schema = schema_for_action
32
+ return unless schema.present?
33
+ @validation_result = schema.call(params.to_unsafe_h)
34
+ return if @validation_result.success?
35
+ render json: {error: SealinkParamValidation::Helper.generate_error_message(@validation_result)}, status: 422
36
+ end
37
+
38
+ def action
39
+ params['action'].to_sym
40
+ end
41
+
42
+ def schema_for_action
43
+ self.class.schemas[action]
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SealinkParamValidation
4
+ class InvalidInputError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SealinkParamValidation
4
+ class Helper
5
+ def self.generate_error_message(result)
6
+ result.errors(full: true).to_h.values.map { |v|
7
+ v.is_a?(Hash) ? v.values : v
8
+ }.flatten.join(', ')
9
+ end
10
+
11
+ def self.generate_humanized_error_message(result)
12
+ build_humanized_errors(result.errors.to_h).to_sentence
13
+ end
14
+
15
+ def self.build_humanized_errors(errors, parent = nil)
16
+ errors.to_h.flat_map { |field, messages|
17
+ if messages.is_a?(Hash)
18
+ build_humanized_errors(messages, field)
19
+ else
20
+ generate_humanized_error(parent, field, messages)
21
+ end
22
+ }
23
+ end
24
+ private_class_method :build_humanized_errors
25
+
26
+ def self.generate_humanized_error(parent, field, messages)
27
+ return "#{parent.to_s.humanize}.#{field.to_s.humanize} #{messages.join}" if parent.present?
28
+ "#{field.to_s.humanize} #{messages.join}"
29
+ end
30
+ private_class_method :generate_humanized_error
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SealinkParamValidation
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'sealink_param_validation/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'sealink-param-validation'
9
+ spec.version = SealinkParamValidation::VERSION
10
+ spec.authors = ['Sean Earle']
11
+ spec.email = 'development@travellink.com.au'
12
+ spec.description = 'Simple validation of params with a concern'
13
+ spec.summary = 'This is some logic we want to use across many projects so we have extracted it into a gem.'
14
+ spec.homepage = 'http://github.com/sealink/sealink-param-validation'
15
+
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'rails'
23
+ spec.add_dependency 'dry-schema', '>= 1.0.0'
24
+
25
+ spec.add_development_dependency 'bundler'
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'rspec'
28
+ spec.add_development_dependency 'rspec-rails'
29
+ spec.add_development_dependency 'coverage-kit'
30
+ spec.add_development_dependency 'simplecov-rcov'
31
+ spec.add_development_dependency 'coveralls'
32
+ spec.add_development_dependency 'travis'
33
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe SealinkParamValidation::Helper do
6
+ context '.generate_error_message' do
7
+ let(:errors) { schema.call(params) }
8
+ subject { SealinkParamValidation::Helper.generate_error_message(errors) }
9
+
10
+ context 'flat schema' do
11
+ let(:schema) {
12
+ Dry::Schema.Params {
13
+ required(:id).filled(:integer)
14
+ required(:name).filled(:string)
15
+ }
16
+ }
17
+ let(:params) { {} }
18
+ it { is_expected.to eq 'id is missing, name is missing' }
19
+ end
20
+
21
+ context 'nested schema' do
22
+ let(:schema) {
23
+ Dry::Schema.Params {
24
+ required(:id).filled(:integer)
25
+ required(:type).hash {
26
+ required(:id).filled(:integer)
27
+ }
28
+ }
29
+ }
30
+ let(:params) { { type: {} } }
31
+ it { is_expected.to eq 'id is missing, id is missing' }
32
+ end
33
+
34
+ context 'arrays' do
35
+ let(:schema) {
36
+ Dry::Schema.Params {
37
+ required(:ids).array(:integer)
38
+ }
39
+ }
40
+ let(:params) { { ids: [ 1, 'a', 'b' ] } }
41
+ it { is_expected.to eq '1 must be an integer, 2 must be an integer' }
42
+ end
43
+ end
44
+
45
+ context '.generate_humanized_error_message' do
46
+ let(:errors) { schema.call(params) }
47
+ subject { SealinkParamValidation::Helper.generate_humanized_error_message(errors) }
48
+
49
+ context 'flat schema' do
50
+ let(:schema) {
51
+ Dry::Schema.Params {
52
+ required(:id).filled(:integer)
53
+ required(:name).filled(:string)
54
+ }
55
+ }
56
+ let(:params) { {} }
57
+ it { is_expected.to eq 'Id is missing and Name is missing' }
58
+ end
59
+
60
+ context 'nested schema' do
61
+ let(:schema) {
62
+ Dry::Schema.Params {
63
+ required(:id).filled(:integer)
64
+ required(:type).hash {
65
+ required(:id).filled(:integer)
66
+ }
67
+ }
68
+ }
69
+ let(:params) { { type: {} } }
70
+ it { is_expected.to eq 'Id is missing and Type.Id is missing' }
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV['RAILS_ENV'] ||= 'test'
4
+ require 'rails'
5
+ require 'action_controller/railtie'
6
+ require 'dry-schema'
7
+ require 'rspec/rails'
8
+ require 'sealink-param-validation'
9
+
10
+ module SealinkParamValidation
11
+ class Application < Rails::Application
12
+ end
13
+ end
14
+
15
+ RSpec.configure do |config|
16
+ config.disable_monkey_patching!
17
+ config.raise_errors_for_deprecations!
18
+
19
+ config.order = :random unless ENV.key? 'NO_RANDOM_ORDER'
20
+
21
+ config.before :suite do
22
+ srand RSpec.configuration.seed
23
+ end
24
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'SealinkParamValidation::Concern', type: :controller do
6
+ before(:all) do
7
+ class DummyController < ActionController::Base
8
+ include SealinkParamValidation::Concern
9
+
10
+ schema_for :index, Dry::Schema.Params { required(:data).value(eql?: 'text') }
11
+ def index
12
+ head :ok
13
+ end
14
+
15
+ def show
16
+ head :ok
17
+ end
18
+
19
+ schema_for :new, Dry::Schema.Params { required(:key).value(eql?: 'text') }
20
+ def new
21
+ head :ok
22
+ end
23
+ end
24
+
25
+ Rails.application.routes.draw do
26
+ get 'index' => 'dummy#index'
27
+ get 'show' => 'dummy#show'
28
+ get 'new' => 'dummy#new'
29
+ end
30
+
31
+ @controller = DummyController.new
32
+ end
33
+
34
+ describe 'validation' do
35
+ context 'successful validation' do
36
+ let(:params) { { 'data' => 'text' } }
37
+ before { get :index, params: params }
38
+ it { expect(response.status).to eq 200 }
39
+ end
40
+
41
+ context 'successful validation on second schema' do
42
+ let(:params) { { 'key' => 'text' } }
43
+ before { get :new, params: params }
44
+ it { expect(response.status).to eq 200 }
45
+ end
46
+
47
+ context 'unprotected route' do
48
+ let(:params) { { 'data1' => 'text' } }
49
+ before { get :show, params: params }
50
+ it { expect(response.status).to eq 200 }
51
+ end
52
+
53
+ context 'failed validation' do
54
+ let(:params) { { 'data1' => 'text1' } }
55
+ before { get :index, params: params }
56
+
57
+ it 'should return a 422' do
58
+ expect(response.status).to eq 422
59
+ end
60
+
61
+ it 'should return a json body with error' do
62
+ expect(JSON.parse(response.body)['error']).to eq 'data is missing'
63
+ end
64
+ end
65
+ end
66
+ end
metadata ADDED
@@ -0,0 +1,204 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sealink-param-validation
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sean Earle
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-12-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-schema
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: coverage-kit
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov-rcov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: coveralls
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: travis
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: Simple validation of params with a concern
154
+ email: development@travellink.com.au
155
+ executables: []
156
+ extensions: []
157
+ extra_rdoc_files: []
158
+ files:
159
+ - ".gitignore"
160
+ - ".travis.yml"
161
+ - CHANGELOG.md
162
+ - Gemfile
163
+ - README.md
164
+ - Rakefile
165
+ - config/secrets.yml
166
+ - gemfiles/rails5.gemfile
167
+ - gemfiles/rails6.gemfile
168
+ - lib/sealink-param-validation.rb
169
+ - lib/sealink_param_validation/concern.rb
170
+ - lib/sealink_param_validation/error.rb
171
+ - lib/sealink_param_validation/helper.rb
172
+ - lib/sealink_param_validation/version.rb
173
+ - sealink_param_validation.gemspec
174
+ - spec/helper_spec.rb
175
+ - spec/spec_helper.rb
176
+ - spec/validation_spec.rb
177
+ homepage: http://github.com/sealink/sealink-param-validation
178
+ licenses:
179
+ - MIT
180
+ metadata: {}
181
+ post_install_message:
182
+ rdoc_options: []
183
+ require_paths:
184
+ - lib
185
+ required_ruby_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ required_rubygems_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ requirements: []
196
+ rubygems_version: 3.0.3
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: This is some logic we want to use across many projects so we have extracted
200
+ it into a gem.
201
+ test_files:
202
+ - spec/helper_spec.rb
203
+ - spec/spec_helper.rb
204
+ - spec/validation_spec.rb