diff --git a/app.rb b/app.rb index bc8e508..778d498 100644 --- a/app.rb +++ b/app.rb @@ -8,6 +8,7 @@ # Require base require 'sinatra/base' +require 'app/errors' require 'app/models' require 'app/routes' diff --git a/app/errors.rb b/app/errors.rb new file mode 100644 index 0000000..63a077f --- /dev/null +++ b/app/errors.rb @@ -0,0 +1,8 @@ +module Lorry + module Errors + + class ComposeValidationWarning < Kwalify::ValidationError + end + + end +end diff --git a/app/models/validation.rb b/app/models/validation.rb index 83bac58..451e9f5 100644 --- a/app/models/validation.rb +++ b/app/models/validation.rb @@ -4,7 +4,9 @@ class Validation def initialize(document) validator = ComposeValidator.new - validator.services = YAML.load(document).keys + if yaml = YAML.load(document) + validator.services = yaml.keys if yaml.respond_to?(:keys) + end @parser = Kwalify::Yaml::Parser.new(validator) @parser.parse(document) if document rescue Kwalify::SyntaxError => e @@ -12,7 +14,11 @@ def initialize(document) end def errors - @parser.errors unless Array(@parser.errors).empty? + @parser.errors.map { |err| err if err.instance_of? Kwalify::ValidationError }.compact + end + + def warnings + @parser.errors.map { |err| err if err.instance_of? Lorry::Errors::ComposeValidationWarning }.compact end end end diff --git a/app/routes/validation.rb b/app/routes/validation.rb index aa97ee5..8fea129 100644 --- a/app/routes/validation.rb +++ b/app/routes/validation.rb @@ -17,7 +17,12 @@ class Validation < Base post do @document = @payload[:document] @validation = Lorry::Models::Validation.new(@document) - json(lines: document_lines, status: validation_status, errors: validation_errors) + json( + lines: document_lines, + status: validation_status, + errors: validation_errors, + warnings: validation_warnings + ) end end @@ -34,6 +39,12 @@ def validation_errors end end + def validation_warnings + Array(@validation.warnings).map do |w| + { warning: { message: w.message, line: w.linenum, column: w.column } } + end + end + def validation_status @validation.errors ? 'invalid' : 'valid' end diff --git a/spec/models/validation_spec.rb b/spec/models/validation_spec.rb new file mode 100644 index 0000000..c8dbf5f --- /dev/null +++ b/spec/models/validation_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe Validation do + let(:parser_with_errors) do + double('parser', + parse: true, + errors: [Kwalify::ValidationError.new('error'), + Lorry::Errors::ComposeValidationWarning.new('warning')]) + end + let(:parser_without_errors) { double('parser', errors: [], warnings: []) } + let(:validator) { double('validator') } + + before do + allow(ComposeValidator).to receive(:new).and_return(validator) + allow(validator).to receive(:services=).and_return(true) + allow(YAML).to receive(:load).and_return(double(keys:[])) + end + + describe '#errors' do + context('when the document has errors') do + before do + allow(Kwalify::Yaml::Parser).to receive(:new).and_return(parser_with_errors) + end + + subject { Lorry::Models::Validation.new(nil) } + + it('returns an array') do + expect(subject.errors).to be_an(Array) + end + + it('returns an array with only Kwalify::ValidationError instances') do + expect(subject.errors).to all(be_an_instance_of Kwalify::ValidationError) + end + end + end + + describe '#warnings' do + context('when the document has warnings') do + before do + allow(Kwalify::Yaml::Parser).to receive(:new).and_return(parser_with_errors) + end + + subject { Lorry::Models::Validation.new(nil) } + + it('returns an array') do + expect(subject.warnings).to be_an(Array) + end + + it('returns an array with only Kwalify::ValidationError instances') do + expect(subject.warnings).to all(be_an_instance_of Lorry::Errors::ComposeValidationWarning) + end + end + end +end diff --git a/spec/routes/validations_spec.rb b/spec/routes/validations_spec.rb new file mode 100644 index 0000000..13667c6 --- /dev/null +++ b/spec/routes/validations_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +describe Lorry::Routes::Validation do + + describe 'OPTIONS /validation' do + it 'returns a response with status code 200' do + response = options '/validation' + expect(response.status).to be 200 + end + end + + describe 'POST validations' do + + let(:request_body) { { document: "" }.to_json } + + it 'returns a response with status code 200' do + response = post '/validation', request_body + expect(response.status).to eq 200 + end + + it 'returns a JSON object with an array of lines' do + response = post '/validation', request_body + expect(JSON.parse(response.body)['lines']).to eq [] + end + + it 'returns a JSON object with a status indicator' do + response = post '/validation', request_body + expect(JSON.parse(response.body)['status']).to eq 'invalid' + end + + it 'returns a JSON object with an array of errors' do + response = post '/validation', request_body + expect(JSON.parse(response.body)['errors']).to eq [] + end + + it 'returns a JSON object with an array of warnings' do + response = post '/validation', request_body + expect(JSON.parse(response.body)['warnings']).to eq [] + end + + context('when the validation returns errors') do + let(:request_body) { { document: "foo" }.to_json } + + it 'the errors array contains an error object with message, line, and column attributes' do + response = post '/validation', request_body + errors = JSON.parse(response.body)['errors'] + expect(errors.first['error'].keys).to match_array(%w(message line column)) + end + end + + context('when the validation returns warnings') do + let(:warnings) do + [Lorry::Errors::ComposeValidationWarning.new('warning')] + end + let(:validation) { double('validation', warnings: warnings, errors: nil) } + let(:request_body) { { document: "" }.to_json } + + before do + allow(Lorry::Models::Validation).to receive(:new).and_return(validation) + end + + it 'the warnings array contains a warning object with message, line, and column attributes' do + response = post '/validation', request_body + errors = JSON.parse(response.body)['warnings'] + expect(errors.first['warning'].keys).to match_array(%w(message line column)) + end + end + end + +end