diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 19708b9..4f9485f 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2022-05-04 19:32:20 UTC using RuboCop version 1.28.2. +# on 2022-06-02 11:51:09 UTC using RuboCop version 1.29.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -11,7 +11,7 @@ Metrics/AbcSize: Max: 27 -# Offense count: 2 +# Offense count: 3 # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. Metrics/MethodLength: Max: 15 diff --git a/README.md b/README.md index 8e1d746..e9a8b63 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ First parse your api documentation: ```ruby # This must point to the folder where the "openapi.yaml" file is -$doc = OpenapiContracts::Doc.parse(Rails.root.join('spec', 'fixtures', 'openapi', 'api-docs', 'openapi')) +$doc = OpenapiContracts::Doc.parse(Rails.root.join('spec/fixtures/openapi/api-docs/openapi')) ``` Ideally you do this once in a RSpec `before(:suite)` hook. diff --git a/bin/release b/bin/release new file mode 100755 index 0000000..e60e2f3 --- /dev/null +++ b/bin/release @@ -0,0 +1,32 @@ +#!/bin/bash -le + +name="openapi_contracts" + +git fetch origin +current=`bin/version` +sha=`git rev-parse HEAD` + +read -p "Which version? (${current}) " version +version=${version:=$current} + +VERSION=$version gem build ${name}.gemspec + +echo "Creating GitHub release" +link=`gh release create v${version} --target $sha --generate-notes` +echo $link +git fetch --tags origin + +file="${name}-${version}.gem" +read -p "Push to rubygems? (y/n) " yn +case $yn in + y ) echo Pushing to rubygems ...;; + n ) echo Aborting.; + exit;; + * ) echo invalid response; + exit 1;; +esac + +gem push $file +rm $file + +open $link diff --git a/lib/openapi_contracts/doc/parser.rb b/lib/openapi_contracts/doc/parser.rb index f686414..ea82697 100644 --- a/lib/openapi_contracts/doc/parser.rb +++ b/lib/openapi_contracts/doc/parser.rb @@ -12,7 +12,8 @@ def parse(path = 'openapi.yaml') abs_path = @dir.join(path) data = parse_file(abs_path, translate: false) data.deep_merge! merge_components - join_partials(abs_path.dirname, data) + data = join_partials(abs_path.dirname, data) + nullable_to_type!(data) end private @@ -34,10 +35,23 @@ def join_partials(cwd, data) end end + def nullable_to_type!(object) + case object + when Hash + if object['type'] && object['nullable'] + object['type'] = [object['type'], 'null'] + object.delete 'nullable' + else + object.each_value { |o| nullable_to_type! o } + end + when Array + object.each { |o| nullable_to_type! o } + end + end + def merge_components data = {} Dir[File.expand_path('components/**/*.yaml', @dir)].each do |file| - # pn = Pathname(file).relative_path_from(@dir) pointer = json_pointer(Pathname(file)).split('/') i = 0 pointer.reduce(data) do |h, p| diff --git a/spec/fixtures/openapi/components/schemas/User.yaml b/spec/fixtures/openapi/components/schemas/User.yaml index c680eca..156c1d1 100644 --- a/spec/fixtures/openapi/components/schemas/User.yaml +++ b/spec/fixtures/openapi/components/schemas/User.yaml @@ -8,10 +8,14 @@ properties: attributes: type: object properties: + name: + type: string + nullable: true email: $ref: './Email.yaml' additionalProperties: false required: + - name - email additionalProperties: false required: diff --git a/spec/integration/rspec_spec.rb b/spec/integration/rspec_spec.rb index 581b674..e6eadbf 100644 --- a/spec/integration/rspec_spec.rb +++ b/spec/integration/rspec_spec.rb @@ -18,6 +18,7 @@ id: 'some-id', type: 'user', attributes: { + name: nil, email: 'name@me.example' } } diff --git a/spec/openapi_contracts/doc_spec.rb b/spec/openapi_contracts/doc_spec.rb index 45bc652..0847f49 100644 --- a/spec/openapi_contracts/doc_spec.rb +++ b/spec/openapi_contracts/doc_spec.rb @@ -17,7 +17,7 @@ hash = subject.to_h expect(hash).to be_a(Hash) expect(hash['type']).to eq 'object' - expect(hash.dig('properties', 'attributes', 'required')).to eq %w(email) + expect(hash.dig('properties', 'attributes', 'required')).to eq %w(name email) end end diff --git a/spec/openapi_contracts/matchers/match_openapi_doc_spec.rb b/spec/openapi_contracts/matchers/match_openapi_doc_spec.rb index bb84586..7294d7d 100644 --- a/spec/openapi_contracts/matchers/match_openapi_doc_spec.rb +++ b/spec/openapi_contracts/matchers/match_openapi_doc_spec.rb @@ -19,6 +19,7 @@ id: 'some-id', type: 'user', attributes: { + name: 'Hugo', email: 'name@me.example' } } @@ -86,6 +87,14 @@ end end + context 'when a nullable attribute is null' do + before { response_body[:data][:attributes][:name] = nil } + + it 'behaves correctly' do + expect(matcher.matches?(response)).to be true + end + end + context 'when path is not documented' do before { request_env['PATH_INFO'] = '/unknown' }