diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml new file mode 100644 index 0000000..04125d2 --- /dev/null +++ b/.github/workflows/danger.yml @@ -0,0 +1,21 @@ +name: danger +on: pull_request + +jobs: + danger: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 100 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2 + bundler-cache: true + rubygems: latest + - name: Run Danger + run: | + # the token is public, has public_repo scope and belongs to the grape-bot user owned by @dblock, this is ok + TOKEN=$(echo -n Z2hwX2lYb0dPNXNyejYzOFJyaTV3QUxUdkNiS1dtblFwZTFuRXpmMwo= | base64 --decode) + DANGER_GITHUB_API_TOKEN=$TOKEN bundle exec danger --verbose diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..6decc1b --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: test + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + lint: + name: RuboCop + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2 + bundler-cache: true + rubygems: latest + + - name: Run RuboCop + run: bundle exec rubocop + + test: + env: + GRAPE_ENTITY: 0.8.0 + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby-version: ['3.0', '3.1', '3.2'] + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true + - name: Run tests + run: bundle exec rake spec diff --git a/.gitignore b/.gitignore index 31d3238..53a1834 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ /spec/reports/ /tmp/ .ruby-gemset -.ruby-version \ No newline at end of file +.ruby-version +/.idea diff --git a/.rubocop.yml b/.rubocop.yml index a591d19..709b6f7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,5 @@ AllCops: + NewCops: enable Exclude: - vendor/**/* TargetRubyVersion: @@ -6,18 +7,6 @@ AllCops: inherit_from: .rubocop_todo.yml -Metrics/BlockLength: - Exclude: - - spec/**/* - -Metrics/LineLength: - Exclude: - - spec/**/* - -Metrics/MethodLength: - Exclude: - - spec/**/* - Naming/FileName: Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index ebc16b8..4775e3a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,44 +1,96 @@ # This configuration was generated by -# `rubocop --auto-gen-config` -# on 2020-06-29 22:12:50 UTC using RuboCop version 0.86.0. +# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 5000` +# on 2023-07-06 17:02:48 UTC using RuboCop version 1.42.0. # 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 # versions of RuboCop, may require this file to be generated again. # Offense count: 1 -# Configuration parameters: Include. +# Configuration parameters: Severity, Include. # Include: **/*.gemspec Gemspec/RequiredRubyVersion: Exclude: - 'grape-swagger-entity.gemspec' +# Offense count: 3 +# Configuration parameters: AllowedMethods. +# AllowedMethods: enums +Lint/ConstantDefinitionInBlock: + Exclude: + - 'spec/grape-swagger/entities/response_model_spec.rb' + - 'spec/support/shared_contexts/inheritance_api.rb' + - 'spec/support/shared_contexts/this_api.rb' + +# Offense count: 1 +# Configuration parameters: AllowComments, AllowEmptyLambdas. +Lint/EmptyBlock: + Exclude: + - 'spec/grape-swagger/entity/attribute_parser_spec.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +# Configuration parameters: AllowedMethods. +# AllowedMethods: instance_of?, kind_of?, is_a?, eql?, respond_to?, equal? +Lint/RedundantSafeNavigation: + Exclude: + - 'lib/grape-swagger/entity/attribute_parser.rb' + # Offense count: 4 -# Configuration parameters: IgnoredMethods. +# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes, Max. Metrics/AbcSize: - Max: 30 + Exclude: + - 'lib/grape-swagger/entity/attribute_parser.rb' + - 'lib/grape-swagger/entity/parser.rb' + +# Offense count: 13 +# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods. +# AllowedMethods: refine +Metrics/BlockLength: + Exclude: + - '**/*.gemspec' + - 'spec/grape-swagger/entities/response_model_spec.rb' + - 'spec/grape-swagger/entity/attribute_parser_spec.rb' + - 'spec/grape-swagger/entity/parser_spec.rb' + - 'spec/support/shared_contexts/this_api.rb' # Offense count: 1 -# Configuration parameters: CountComments. +# Configuration parameters: CountComments, Max, CountAsOne. Metrics/ClassLength: - Max: 111 + Exclude: + - 'lib/grape-swagger/entity/parser.rb' # Offense count: 2 -# Configuration parameters: IgnoredMethods. +# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, Max. Metrics/CyclomaticComplexity: - Max: 11 + Exclude: + - 'lib/grape-swagger/entity/attribute_parser.rb' + - 'lib/grape-swagger/entity/parser.rb' # Offense count: 5 -# Configuration parameters: CountComments, ExcludedMethods. +# Configuration parameters: CountComments, Max, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods. Metrics/MethodLength: - Max: 25 + Exclude: + - 'lib/grape-swagger/entity/attribute_parser.rb' + - 'lib/grape-swagger/entity/parser.rb' # Offense count: 2 -# Configuration parameters: IgnoredMethods. +# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, Max. Metrics/PerceivedComplexity: - Max: 12 + Exclude: + - 'lib/grape-swagger/entity/attribute_parser.rb' + - 'lib/grape-swagger/entity/parser.rb' + +# Offense count: 2 +# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns. +# SupportedStyles: snake_case, normalcase, non_integer +# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339 +Naming/VariableNumber: + Exclude: + - 'spec/grape-swagger/entities/response_model_spec.rb' # Offense count: 4 +# Configuration parameters: AllowedConstants. Style/Documentation: Exclude: - 'spec/**/*' @@ -46,3 +98,9 @@ Style/Documentation: - 'lib/grape-swagger/entity.rb' - 'lib/grape-swagger/entity/attribute_parser.rb' - 'lib/grape-swagger/entity/parser.rb' + +# Offense count: 4 +Style/OpenStructUse: + Exclude: + - 'spec/grape-swagger/entities/response_model_spec.rb' + - 'spec/support/shared_contexts/this_api.rb' diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 96906a7..0000000 --- a/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -language: ruby - -sudo: false - -before_install: - - gem update --system - - gem install bundler - -after_success: - - bundle exec danger - -rvm: - - 2.5.8 - - 2.6.6 - - 2.7.1 - -env: - - GRAPE_ENTITY=0.6.1 - - GRAPE_ENTITY=0.7.1 - - GRAPE_ENTITY=0.8.0 - -matrix: - fast_finish: true - - include: - - rvm: 2.4.10 - env: - - rvm: ruby-head - env: - - rvm: jruby-head - env: - - allow_failures: - - rvm: 2.4.10 - - rvm: ruby-head - - rvm: jruby-head diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d4ab94..9535bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ #### Fixes +* [#61](https://github.com/ruby-grape/grape-swagger-entity/pull/61): Migrate from Travis to GHA for CI - [@mscrivo](https://github.com/mscrivo). * Your contribution here. ### 0.5.1 (June 30, 2020) diff --git a/Gemfile b/Gemfile index 7101d1e..9eb4c22 100644 --- a/Gemfile +++ b/Gemfile @@ -14,14 +14,14 @@ group :development, :test do gem 'rack-test' gem 'rake' gem 'rdoc' - gem 'rspec', '~> 3.9' - gem 'rubocop', '~> 0.85' + gem 'rspec' + gem 'rubocop' end gem 'grape-swagger', git: 'https://github.com/ruby-grape/grape-swagger.git' group :test do gem 'grape-entity', ENV.fetch('GRAPE_ENTITY', '0.6.1') - gem 'ruby-grape-danger', '~> 0.1.1', require: false + gem 'ruby-grape-danger', '~> 0.2.0', require: false gem 'simplecov', require: false end diff --git a/README.md b/README.md index d7c5639..bd141d0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -# GrapeSwagger::Entity [![Build Status](https://travis-ci.org/ruby-grape/grape-swagger-entity.svg)](https://travis-ci.org/ruby-grape/grape-swagger-entity) +# GrapeSwagger::Entity + +[![Gem Version](https://badge.fury.io/rb/grape-swagger-entity.svg)](https://badge.fury.io/rb/grape-swagger-entity) +[![Build Status](https://github.com/ruby-grape/grape-swagger-entity/workflows/test/badge.svg?branch=master)](https://github.com/ruby-grape/grape-swagger-entity/actions) A simple grape-swagger adapter to allow parse representers as response model diff --git a/grape-swagger-entity.gemspec b/grape-swagger-entity.gemspec index 46ed2cd..0c88a08 100644 --- a/grape-swagger-entity.gemspec +++ b/grape-swagger-entity.gemspec @@ -22,4 +22,5 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 2.4' s.add_runtime_dependency 'grape-entity', '>= 0.6.0' s.add_runtime_dependency 'grape-swagger', '>= 1.2.0' + s.metadata['rubygems_mfa_required'] = 'true' end diff --git a/lib/grape-swagger/entity.rb b/lib/grape-swagger/entity.rb index 9133ce3..af622ea 100644 --- a/lib/grape-swagger/entity.rb +++ b/lib/grape-swagger/entity.rb @@ -13,4 +13,4 @@ module Entity end end -GrapeSwagger.model_parsers.register(::GrapeSwagger::Entity::Parser, ::Grape::Entity) +GrapeSwagger.model_parsers.register(GrapeSwagger::Entity::Parser, Grape::Entity) diff --git a/lib/grape-swagger/entity/attribute_parser.rb b/lib/grape-swagger/entity/attribute_parser.rb index e643caf..8c4ca87 100644 --- a/lib/grape-swagger/entity/attribute_parser.rb +++ b/lib/grape-swagger/entity/attribute_parser.rb @@ -29,8 +29,8 @@ def call(entity_options) add_attribute_sample(param, documentation, :default) add_attribute_sample(param, documentation, :example) - if (values = documentation[:values]) - param[:enum] = values if values.is_a?(Array) + if (values = documentation[:values]) && values.is_a?(Array) + param[:enum] = values end if documentation[:is_array] diff --git a/lib/grape-swagger/entity/parser.rb b/lib/grape-swagger/entity/parser.rb index 17fa987..f80e12a 100644 --- a/lib/grape-swagger/entity/parser.rb +++ b/lib/grape-swagger/entity/parser.rb @@ -3,9 +3,7 @@ module GrapeSwagger module Entity class Parser - attr_reader :model - attr_reader :endpoint - attr_reader :attribute_parser + attr_reader :model, :endpoint, :attribute_parser def initialize(model, endpoint) @model = model diff --git a/spec/grape-swagger/entities/response_model_spec.rb b/spec/grape-swagger/entities/response_model_spec.rb index 355f91b..6ad4b34 100644 --- a/spec/grape-swagger/entities/response_model_spec.rb +++ b/spec/grape-swagger/entities/response_model_spec.rb @@ -40,7 +40,7 @@ def app expect(subject['definitions'].keys).to include 'ThisApi_Entities_Error' expect(subject['definitions']['ThisApi_Entities_Error']).to eq( 'type' => 'object', - 'description' => 'This returns something or an error', + 'description' => 'ThisApi_Entities_Error model', 'properties' => { 'code' => { 'type' => 'string', 'description' => 'Error code' }, 'message' => { 'type' => 'string', 'description' => 'Error message' } @@ -50,14 +50,16 @@ def app expect(subject['definitions'].keys).to include 'ThisApi_Entities_Something' expect(subject['definitions']['ThisApi_Entities_Something']).to eq( 'type' => 'object', - 'description' => 'This returns something', + 'description' => 'ThisApi_Entities_Something model', 'properties' => { 'text' => { 'type' => 'string', 'description' => 'Content of something.' }, 'colors' => { 'type' => 'array', 'items' => { 'type' => 'string' }, 'description' => 'Colors' }, - 'kind' => { '$ref' => '#/definitions/ThisApi_Entities_Kind', 'description' => 'The kind of this something.' }, + 'kind' => { '$ref' => '#/definitions/ThisApi_Entities_Kind', + 'description' => 'The kind of this something.' }, 'kind2' => { '$ref' => '#/definitions/ThisApi_Entities_Kind', 'description' => 'Secondary kind.' }, 'kind3' => { '$ref' => '#/definitions/ThisApi_Entities_Kind', 'description' => 'Tertiary kind.' }, - 'tags' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ThisApi_Entities_Tag' }, 'description' => 'Tags.' }, + 'tags' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/ThisApi_Entities_Tag' }, + 'description' => 'Tags.' }, 'relation' => { '$ref' => '#/definitions/ThisApi_Entities_Relation', 'description' => 'A related model.' }, 'code' => { 'type' => 'string', 'description' => 'Error code' }, 'message' => { 'type' => 'string', 'description' => 'Error message' }, @@ -67,7 +69,9 @@ def app expect(subject['definitions'].keys).to include 'ThisApi_Entities_Kind' expect(subject['definitions']['ThisApi_Entities_Kind']).to eq( - 'type' => 'object', 'properties' => { 'title' => { 'type' => 'string', 'description' => 'Title of the kind.' }, 'content' => { 'description' => 'Content', 'type' => 'string', 'x-some' => 'stuff' } } + 'type' => 'object', 'properties' => { 'title' => { 'type' => 'string', 'description' => 'Title of the kind.' }, + 'content' => { 'description' => 'Content', 'type' => 'string', + 'x-some' => 'stuff' } } ) expect(subject['definitions'].keys).to include 'ThisApi_Entities_Relation' @@ -101,6 +105,7 @@ class Kind < Grape::Entity class Relation < Grape::Entity expose :name, documentation: { type: String, desc: 'Name' } end + class Tag < Grape::Entity expose :name, documentation: { type: 'string', desc: 'Name', example: -> { 'random_tag' } } @@ -132,10 +137,18 @@ class Nested < Grape::Entity end class Polymorphic < Grape::Entity - expose :obj, as: :kind, if: ->(instance, _) { instance.type == 'kind' }, using: Kind, documentation: { desc: 'Polymorphic Kind' } - expose :obj, as: :values, if: ->(instance, _) { instance.type == 'values' }, using: Values, documentation: { desc: 'Polymorphic Values' } - expose :not_using_obj, as: :str, if: ->(instance, _) { instance.instance_of?(String) }, documentation: { desc: 'Polymorphic String' } - expose :not_using_obj, as: :num, if: ->(instance, _) { instance.instance_of?(Number) }, documentation: { desc: 'Polymorphic Number', type: 'Integer' } + expose :obj, as: :kind, if: lambda { |instance, _| + instance.type == 'kind' + }, using: Kind, documentation: { desc: 'Polymorphic Kind' } + expose :obj, as: :values, if: lambda { |instance, _| + instance.type == 'values' + }, using: Values, documentation: { desc: 'Polymorphic Values' } + expose :not_using_obj, as: :str, if: lambda { |instance, _| + instance.instance_of?(String) + }, documentation: { desc: 'Polymorphic String' } + expose :not_using_obj, as: :num, if: lambda { |instance, _| + instance.instance_of?(Number) + }, documentation: { desc: 'Polymorphic Number', type: 'Integer' } end class SomeEntity < Grape::Entity @@ -144,7 +157,8 @@ class SomeEntity < Grape::Entity expose :kind2, using: Kind, documentation: { desc: 'Secondary kind.' } expose :kind3, using: TheseApi::Entities::Kind, documentation: { desc: 'Tertiary kind.' } expose :tags, using: TheseApi::Entities::Tag, documentation: { desc: 'Tags.', is_array: true } - expose :relation, using: TheseApi::Entities::Relation, documentation: { type: 'TheseApi_Relation', desc: 'A related model.' } + expose :relation, using: TheseApi::Entities::Relation, + documentation: { type: 'TheseApi_Relation', desc: 'A related model.' } expose :values, using: TheseApi::Entities::Values, documentation: { desc: 'Tertiary kind.' } expose :nested, using: TheseApi::Entities::Nested, documentation: { desc: 'Nested object.' } expose :polymorphic, using: TheseApi::Entities::Polymorphic, documentation: { desc: 'Polymorphic Model' } @@ -181,19 +195,22 @@ def app 'type' => 'object', 'properties' => { 'guid' => { 'type' => 'string', 'enum' => %w[a b c], 'default' => 'c', 'description' => 'Some values' }, - 'uuid' => { 'type' => 'string', 'format' => 'own', 'description' => 'customer uuid', 'example' => 'e3008fba-d53d-4bcc-a6ae-adc56dff8020' } + 'uuid' => { 'type' => 'string', 'format' => 'own', 'description' => 'customer uuid', + 'example' => 'e3008fba-d53d-4bcc-a6ae-adc56dff8020' } } ) expect(subject['TheseApi_Entities_Kind']).to eql( 'type' => 'object', 'properties' => { - 'id' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'id of the kind.', 'enum' => [1, 2], 'readOnly' => true }, + 'id' => { 'type' => 'integer', 'format' => 'int32', 'description' => 'id of the kind.', 'enum' => [1, 2], + 'readOnly' => true }, 'title' => { 'type' => 'string', 'description' => 'Title of the kind.', 'readOnly' => false }, 'type' => { 'type' => 'string', 'description' => 'Type of the kind.', 'readOnly' => true } } ) expect(subject['TheseApi_Entities_Tag']).to eql( - 'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'Name', 'example' => 'random_tag' } } + 'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'Name', + 'example' => 'random_tag' } } ) expect(subject['TheseApi_Entities_Relation']).to eql( 'type' => 'object', 'properties' => { 'name' => { 'type' => 'string', 'description' => 'Name' } } @@ -267,17 +284,19 @@ def app 'kind' => { '$ref' => '#/definitions/TheseApi_Entities_Kind', 'description' => 'The kind of this something.' }, 'kind2' => { '$ref' => '#/definitions/TheseApi_Entities_Kind', 'description' => 'Secondary kind.' }, 'kind3' => { '$ref' => '#/definitions/TheseApi_Entities_Kind', 'description' => 'Tertiary kind.' }, - 'tags' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/TheseApi_Entities_Tag' }, 'description' => 'Tags.' }, + 'tags' => { 'type' => 'array', 'items' => { '$ref' => '#/definitions/TheseApi_Entities_Tag' }, + 'description' => 'Tags.' }, 'relation' => { '$ref' => '#/definitions/TheseApi_Entities_Relation', 'description' => 'A related model.' }, 'values' => { '$ref' => '#/definitions/TheseApi_Entities_Values', 'description' => 'Tertiary kind.' }, 'nested' => { '$ref' => '#/definitions/TheseApi_Entities_Nested', 'description' => 'Nested object.' }, 'code' => { 'type' => 'string', 'description' => 'Error code' }, 'message' => { 'type' => 'string', 'description' => 'Error message' }, - 'polymorphic' => { '$ref' => '#/definitions/TheseApi_Entities_Polymorphic', 'description' => 'Polymorphic Model' }, + 'polymorphic' => { '$ref' => '#/definitions/TheseApi_Entities_Polymorphic', + 'description' => 'Polymorphic Model' }, 'attr' => { 'type' => 'string', 'description' => 'Attribute' } }, 'required' => %w[attr], - 'description' => 'This returns something' + 'description' => 'TheseApi_Entities_SomeEntity model' ) end end diff --git a/spec/grape-swagger/entity/attribute_parser_spec.rb b/spec/grape-swagger/entity/attribute_parser_spec.rb index fd63e3c..4fe8865 100644 --- a/spec/grape-swagger/entity/attribute_parser_spec.rb +++ b/spec/grape-swagger/entity/attribute_parser_spec.rb @@ -31,14 +31,19 @@ end context 'when it contains unique_items' do - let(:entity_options) { { using: ThisApi::Entities::Tag, documentation: { is_array: true, unique_items: true } } } + let(:entity_options) do + { using: ThisApi::Entities::Tag, documentation: { is_array: true, unique_items: true } } + end it { is_expected.to include(uniqueItems: true) } end end context 'when it is not exposed as an array' do - let(:entity_options) { { using: ThisApi::Entities::Kind, documentation: { type: 'ThisApi::Kind', desc: 'The kind of this something.' } } } + let(:entity_options) do + { using: ThisApi::Entities::Kind, + documentation: { type: 'ThisApi::Kind', desc: 'The kind of this something.' } } + end it { is_expected.to_not include('type') } it { is_expected.to include('$ref' => '#/definitions/Kind') } @@ -89,7 +94,9 @@ end context 'when it contains unique_items' do - let(:entity_options) { { documentation: { type: 'string', desc: 'Colors', is_array: true, unique_items: true } } } + let(:entity_options) do + { documentation: { type: 'string', desc: 'Colors', is_array: true, unique_items: true } } + end it { is_expected.to include(uniqueItems: true) } end diff --git a/spec/support/shared_contexts/this_api.rb b/spec/support/shared_contexts/this_api.rb index 57af4e1..0ca3142 100644 --- a/spec/support/shared_contexts/this_api.rb +++ b/spec/support/shared_contexts/this_api.rb @@ -12,9 +12,11 @@ class Kind < Grape::Entity class Relation < Grape::Entity expose :name, documentation: { type: 'string', desc: 'Name' } end + class Tag < Grape::Entity expose :name, documentation: { type: 'string', desc: 'Name' } end + class Error < Grape::Entity expose :code, documentation: { type: 'string', desc: 'Error code' } expose :message, documentation: { type: 'string', desc: 'Error message' } @@ -33,7 +35,8 @@ class Something < Grape::Entity expose :kind2, using: Kind, documentation: { desc: 'Secondary kind.' } expose :kind3, using: ThisApi::Entities::Kind, documentation: { desc: 'Tertiary kind.' } expose :tags, using: ThisApi::Entities::Tag, documentation: { desc: 'Tags.', is_array: true } - expose :relation, using: ThisApi::Entities::Relation, documentation: { type: 'ThisApi::Relation', desc: 'A related model.' } + expose :relation, using: ThisApi::Entities::Relation, + documentation: { type: 'ThisApi::Relation', desc: 'A related model.' } expose :merged_attribute, using: ThisApi::Entities::Nested, merge: true end end