Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support fallback parsers for vendor-specific media types #38

Merged
merged 4 commits into from
Dec 25, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 36 additions & 20 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -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,37 +30,53 @@ 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"
- "3.2"
- "3.1"
- "3.0"
- "2.7"
- "jruby"
- "truffleruby"

steps:
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
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
2 changes: 2 additions & 0 deletions examples/minitest.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

ENV["APP_ENV"] = "test"

require_relative "test_app"

require "minitest/autorun"
1 change: 1 addition & 0 deletions examples/rails_app/spec/rails_helper.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
ENV["RAILS_ENV"] = "test"
ENV["APP_ENV"] = "test"

require_relative "../app"

4 changes: 4 additions & 0 deletions examples/rspec.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# frozen_string_literal: true

ENV["APP_ENV"] = "test"

require_relative "test_app"

require "rspec/autorun"
26 changes: 24 additions & 2 deletions lib/skooma/body_parsers.rb
Original file line number Diff line number Diff line change
@@ -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 = {}

73 changes: 73 additions & 0 deletions spec/openapi_test_suite/openapi_formats.json
Original file line number Diff line number Diff line change
@@ -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\":\"[email protected]\",\"age\":30}},{\"type\":\"users\",\"id\":\"2\",\"attributes\":{\"name\":\"Jane Smith\",\"email\":\"[email protected]\",\"age\":25}}]}"
}
},
"valid": true
}
]
}