From 492c8b3392dc6a61b490d0c04233e21c98b9a41c Mon Sep 17 00:00:00 2001 From: Pablo Vicente Date: Mon, 23 Dec 2024 14:35:36 +0100 Subject: [PATCH 1/4] Register "application/vnd.api+json" as media type This change allows JSON:API response bodies to be parsed. --- lib/skooma/body_parsers.rb | 1 + spec/openapi_test_suite/openapi_formats.json | 73 ++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/lib/skooma/body_parsers.rb b/lib/skooma/body_parsers.rb index 075c3fb..acf6b3a 100644 --- a/lib/skooma/body_parsers.rb +++ b/lib/skooma/body_parsers.rb @@ -27,5 +27,6 @@ def self.call(body, **_options) end end register "application/json", JSONParser + register "application/vnd.api+json", JSONParser end end diff --git a/spec/openapi_test_suite/openapi_formats.json b/spec/openapi_test_suite/openapi_formats.json index 1db488c..4f820be 100644 --- a/spec/openapi_test_suite/openapi_formats.json +++ b/spec/openapi_test_suite/openapi_formats.json @@ -41,6 +41,59 @@ } } } + }, + "/users": { + "get": { + "responses": { + "200": { + "description": "List of users", + "content": { + "application/vnd.api+json": { + "schema": { + "type": "object", + "required": ["data"], + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "required": ["type", "id", "attributes"], + "properties": { + "type": { + "type": "string", + "enum": ["users"] + }, + "id": { + "type": "string" + }, + "attributes": { + "type": "object", + "required": ["name", "email"], + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "age": { + "type": "integer", + "minimum": 0, + "maximum": 120 + } + } + } + } + } + } + } + } + } + } + } + } + } } } }, @@ -104,6 +157,26 @@ } }, "valid": false + }, + { + "description": "valid users response", + "data": { + "method": "get", + "path": "/users", + "request": { + "headers": { + "Content-Type": "application/vnd.api+json" + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/vnd.api+json" + }, + "body": "{\"data\":[{\"type\":\"users\",\"id\":\"1\",\"attributes\":{\"name\":\"John Doe\",\"email\":\"john@example.com\",\"age\":30}},{\"type\":\"users\",\"id\":\"2\",\"attributes\":{\"name\":\"Jane Smith\",\"email\":\"jane@example.com\",\"age\":25}}]}" + } + }, + "valid": true } ] } From 5c8b6544ac74343a1954497b513e65368ecda4fc Mon Sep 17 00:00:00 2001 From: Svyatoslav Kryukov Date: Tue, 24 Dec 2024 12:57:39 +0300 Subject: [PATCH 2/4] Set APP_ENV to fix rack-protection returning 403 --- examples/minitest.rb | 2 ++ examples/rails_app/spec/rails_helper.rb | 1 + examples/rspec.rb | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/examples/minitest.rb b/examples/minitest.rb index bf74907..bf4ddda 100644 --- a/examples/minitest.rb +++ b/examples/minitest.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +ENV["APP_ENV"] = "test" + require_relative "test_app" require "minitest/autorun" diff --git a/examples/rails_app/spec/rails_helper.rb b/examples/rails_app/spec/rails_helper.rb index 007608e..de91e2d 100644 --- a/examples/rails_app/spec/rails_helper.rb +++ b/examples/rails_app/spec/rails_helper.rb @@ -1,4 +1,5 @@ ENV["RAILS_ENV"] = "test" +ENV["APP_ENV"] = "test" require_relative "../app" diff --git a/examples/rspec.rb b/examples/rspec.rb index 2764dbb..823f323 100644 --- a/examples/rspec.rb +++ b/examples/rspec.rb @@ -1,3 +1,7 @@ +# frozen_string_literal: true + +ENV["APP_ENV"] = "test" + require_relative "test_app" require "rspec/autorun" From 91203499799099800cd3bc463fcbc935d40a7262 Mon Sep 17 00:00:00 2001 From: Svyatoslav Kryukov Date: Tue, 24 Dec 2024 12:58:43 +0300 Subject: [PATCH 3/4] Support fallback parsers for vendor-specific media types --- lib/skooma/body_parsers.rb | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/skooma/body_parsers.rb b/lib/skooma/body_parsers.rb index acf6b3a..71f2fd5 100644 --- a/lib/skooma/body_parsers.rb +++ b/lib/skooma/body_parsers.rb @@ -6,16 +6,38 @@ class << self DEFAULT_PARSER = ->(body, **_options) { body } def [](media_type) - parsers[media_type.to_s.strip.downcase] || DEFAULT_PARSER + key = normalize_media_type(media_type) + parsers[key] || + find_suffix_parser(key) || + find_fallback_parser(key) || + DEFAULT_PARSER end attr_accessor :parsers def register(*media_types, parser) media_types.each do |media_type| - parsers[media_type.to_s.strip.downcase] = parser + parsers[normalize_media_type(media_type)] = parser end end + + private + + def normalize_media_type(media_type) + media_type.to_s.strip.downcase + end + + def find_suffix_parser(media_type) + return unless media_type.include?("+") + + suffix = media_type.split("+").last + parsers["application/#{suffix}"] + end + + def find_fallback_parser(media_type) + type = media_type.split("/").first + parsers["#{type}/*"] || parsers["*/*"] + end end self.parsers = {} @@ -27,6 +49,5 @@ def self.call(body, **_options) end end register "application/json", JSONParser - register "application/vnd.api+json", JSONParser end end From 0afc3870dfb82c2fbbf8854b5717cbcc4f8f1317 Mon Sep 17 00:00:00 2001 From: Svyatoslav Kryukov Date: Tue, 24 Dec 2024 13:06:35 +0300 Subject: [PATCH 4/4] Drop truffleruby and jruby from CI Please open an issue if skooma doesn't work on those platforms. --- .github/workflows/main.yml | 56 ++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d4f298b..ff675b1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,16 +9,16 @@ on: env: CI: 1 + BUNDLE_JOBS: 4 + BUNDLE_RETRY: 3 + BUNDLE_PATH: vendor/bundle jobs: lint: runs-on: ubuntu-latest name: Linter - env: - BUNDLE_JOBS: 4 - BUNDLE_RETRY: 3 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: @@ -30,10 +30,8 @@ jobs: build: runs-on: ubuntu-latest name: Ruby ${{ matrix.ruby }} - env: - BUNDLE_JOBS: 4 - BUNDLE_RETRY: 3 strategy: + fail-fast: false matrix: ruby: - "3.3" @@ -41,8 +39,7 @@ jobs: - "3.1" - "3.0" - "2.7" - - "jruby" - - "truffleruby" + steps: - uses: actions/checkout@v4 - name: Set up Ruby @@ -50,17 +47,36 @@ jobs: with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - - name: Run RSpec - run: bundle exec rspec - - name: Install RSpec example dependencies - run: bundle install --gemfile ./examples/Gemfile-rspec - - name: Run RSpec example - run: bundle exec --gemfile ./examples/Gemfile-rspec ruby examples/rspec.rb - - name: Install minitest example dependencies - run: bundle install --gemfile ./examples/Gemfile-minitest - - name: Run minitest example - run: bundle exec --gemfile ./examples/Gemfile-minitest ruby examples/minitest.rb - - name: Run RSpec Rails example + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: | + vendor/bundle + examples/vendor/bundle + examples/rails_app/vendor/bundle + key: ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + ${{ runner.os }}-gems-${{ matrix.ruby }}- + + + - name: Run Core Tests + run: | + bundle config path vendor/bundle + bundle install + bundle exec rspec + + - name: Run RSpec Example + run: | + bundle install --gemfile ./examples/Gemfile-rspec + bundle exec --gemfile ./examples/Gemfile-rspec ruby examples/rspec.rb + + - name: Run Minitest Example + run: | + bundle install --gemfile ./examples/Gemfile-minitest + bundle exec --gemfile ./examples/Gemfile-minitest ruby examples/minitest.rb + + - name: Run Rails Example working-directory: ./examples/rails_app run: | bundle install