Skip to content

Commit

Permalink
openapi: load oas resource before parsing with swagger parser
Browse files Browse the repository at this point in the history
  • Loading branch information
eguzki committed Dec 18, 2018
1 parent 9b36987 commit 8bba857
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 42 deletions.
4 changes: 2 additions & 2 deletions lib/3scale_toolbox/commands/import_command/openapi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ def create_context
end

def load_openapi
Swagger.build(*openapi_resource(arguments[:openapi_resource]))
Swagger.build(load_resource(arguments[:openapi_resource]))
# Disable validation step because https://petstore.swagger.io/v2/swagger.json
# does not pass validation. Maybe library's schema is outdated?
# openapi.tap(&:validate)
rescue Swagger::InvalidDefinition, Hashie::CoercionError, JSON::ParserError, Psych::SyntaxError => e
rescue Swagger::InvalidDefinition, Hashie::CoercionError, Psych::SyntaxError => e
raise ThreeScaleToolbox::Error, "OpenAPI schema validation failed: #{e.message}"
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@ module Commands
module ImportCommand
module OpenAPI
module ResourceReader
##
# Load resource from different types of sources.
# Supported types are: file, URL, stdin
# Loaded content is returned
def load_resource(resource)
# Json format is parsed as well
YAML.safe_load(read_content(resource))
end

##
# Reads resources from different types of sources.
# Supported types are: file, URL, stdin
# Return type is
# [content, format] where
# content: raw content
# format: Hash with single key: `format`. Value can be `:json` or `:yaml`
# format example: { format: :json }
def openapi_resource(resource)
# Resource raw content is returned
def read_content(resource)
case resource
when '-'
method(:read_stdin)
Expand All @@ -27,24 +32,15 @@ def openapi_resource(resource)

# Detect format from file extension
def read_file(resource)
[File.read(resource), { format: File.extname(resource) }]
File.read(resource)
end

def read_stdin(_resource)
content = STDIN.read
# will try parse json, otherwise yaml
format = :json
begin
JSON.parse(content)
rescue JSON::ParserError
format = :yaml
end
[content, { format: format }]
STDIN.read
end

def read_url(resource)
uri = URI.parse(resource)
[Net::HTTP.get(uri), { format: File.extname(uri.path) }]
Net::HTTP.get(URI.parse(resource))
end
end
end
Expand Down
81 changes: 64 additions & 17 deletions spec/unit/commands/import_command/openapi/resource_reader_spec.rb
Original file line number Diff line number Diff line change
@@ -1,30 +1,80 @@
require '3scale_toolbox'

RSpec.shared_examples 'parsed content' do
let(:result) { subject.openapi_resource(resource) }
RSpec.shared_examples 'content is read' do
let(:result) { subject.read_content(resource) }

it 'does not return nil' do
expect(result).not_to be_nil
expect(result.size).to eq(2)
end

it 'is read' do
expect(result[0]).to eq(content)
end

it 'has correct format' do
expect(result[1]).to include(expected_format)
expect(result).to eq(content)
end
end

RSpec.describe 'OpenAPI ResourceReader' do
include_context :temp_dir

context '#openapi_resource' do
subject do
Class.new { include ThreeScaleToolbox::Commands::ImportCommand::OpenAPI::ResourceReader }.new
subject do
Class.new { include ThreeScaleToolbox::Commands::ImportCommand::OpenAPI::ResourceReader }.new
end

context '#load_resource' do
let(:resource) { tmp_dir.join('petstore.file').tap { |conf| conf.write(content) } }
let(:result) { subject.load_resource(resource) }

context 'valid json' do
let(:content) { '{ "some_key": "some value" }' }

it 'does not return nil' do
expect(result).not_to be_nil
end

it 'is loaded' do
expect(result).to eq('some_key' => 'some value')
end
end

context 'valid yaml' do
let(:content) do
<<~YAML
---
some_key: "some value"
YAML
end

it 'does not return nil' do
expect(result).not_to be_nil
end

it 'is loaded' do
expect(result).to eq('some_key' => 'some value')
end
end

context 'invalid yaml' do
let(:content) do
<<~YAML
---
`
YAML
end

it 'raises error' do
expect { result }.to raise_error(Psych::SyntaxError)
end
end

context 'invalid json' do
let(:content) { '{ `some }' }

it 'raises error' do
expect { result }.to raise_error(Psych::SyntaxError)
end
end
end

context '#read_content' do
let(:content) do
<<~YAML
---
Expand All @@ -34,31 +84,28 @@

context 'from file' do
let(:resource) { tmp_dir.join('petstore.yaml').tap { |conf| conf.write(content) } }
let(:expected_format) { { format: '.yaml' } }
it_behaves_like 'parsed content'
it_behaves_like 'content is read'
end

context 'from URL' do
let(:resource) { 'https://example.com/petstore.yaml' }
let(:expected_format) { { format: '.yaml' } }

before :each do
net_class_stub = class_double(Net::HTTP).as_stubbed_const
expect(net_class_stub).to receive(:get).and_return(content)
end

it_behaves_like 'parsed content'
it_behaves_like 'content is read'
end

context 'from stdin' do
let(:resource) { '-' }
let(:expected_format) { { format: :yaml } }

before :each do
expect(STDIN).to receive(:read).and_return(content)
end

it_behaves_like 'parsed content'
it_behaves_like 'content is read'
end
end
end
10 changes: 5 additions & 5 deletions spec/unit/commands/import_command/openapi_spec.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
require '3scale_toolbox'

RSpec.describe ThreeScaleToolbox::Commands::ImportCommand::OpenAPI::OpenAPISubcommand do
include_context :temp_dir
include_context :resources

let(:arguments) { { 'openapi_resource': 'some_resource' } }
let(:arguments) { { 'openapi_resource': oas_resource } }
let(:options) { { 'destination': 'https://[email protected]' } }
subject { described_class.new(options, arguments, nil) }
let(:oas_resource) { [oas_content, { format: :yaml }] }

context 'valid openapi content' do
let(:oas_resource) { File.join(resources_path, 'valid_swagger.yaml') }

context '#run' do
let(:oas_content) { File.read(File.join(resources_path, 'valid_swagger.yaml')) }
let(:api_spec) { double('api_spec') }
let(:remote) { double('remote') }

before :each do
expect(subject).to receive(:openapi_resource).and_return(oas_resource)
threescale_api_spec_stub = class_double(ThreeScaleToolbox::Commands::ImportCommand::OpenAPI::ThreeScaleApiSpec).as_stubbed_const
expect(threescale_api_spec_stub).to receive(:new).and_return(api_spec)
expect_any_instance_of(ThreeScaleToolbox::Remotes).to receive(:remote).and_return(remote)
Expand Down Expand Up @@ -50,10 +50,10 @@
desSSSSScription: "Invalid description tag"
YAML
end
let(:oas_resource) { tmp_dir.join('invalid.yaml').tap { |conf| conf.write(oas_content) } }

context '#run' do
it 'raises error' do
expect(subject).to receive(:openapi_resource).and_return(oas_resource)
expect { subject.run }.to raise_error(ThreeScaleToolbox::Error,
/OpenAPI schema validation failed/)
end
Expand Down

0 comments on commit 8bba857

Please sign in to comment.