openapi_first 1.0.0.beta5 → 1.0.0.beta6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.github/workflows/ruby.yml +2 -1
 - data/CHANGELOG.md +8 -0
 - data/Gemfile +2 -1
 - data/Gemfile.lock +6 -9
 - data/Gemfile.rack2 +15 -0
 - data/lib/openapi_first/{body_parser_middleware.rb → body_parser.rb} +3 -15
 - data/lib/openapi_first/definition/cookie_parameters.rb +12 -0
 - data/lib/openapi_first/definition/has_content.rb +37 -0
 - data/lib/openapi_first/definition/header_parameters.rb +12 -0
 - data/lib/openapi_first/definition/operation.rb +103 -0
 - data/lib/openapi_first/definition/parameters.rb +47 -0
 - data/lib/openapi_first/definition/path_item.rb +23 -0
 - data/lib/openapi_first/definition/path_parameters.rb +13 -0
 - data/lib/openapi_first/definition/query_parameters.rb +12 -0
 - data/lib/openapi_first/definition/request_body.rb +32 -0
 - data/lib/openapi_first/definition/response.rb +37 -0
 - data/lib/openapi_first/{json_schema → definition/schema}/result.rb +1 -1
 - data/lib/openapi_first/{json_schema.rb → definition/schema.rb} +2 -2
 - data/lib/openapi_first/definition.rb +26 -6
 - data/lib/openapi_first/error_response.rb +2 -0
 - data/lib/openapi_first/request_body_validator.rb +17 -21
 - data/lib/openapi_first/request_validation.rb +34 -30
 - data/lib/openapi_first/response_validation.rb +31 -11
 - data/lib/openapi_first/router.rb +19 -53
 - data/lib/openapi_first/version.rb +1 -1
 - data/openapi_first.gemspec +7 -4
 - metadata +32 -52
 - data/.rspec +0 -3
 - data/.rubocop.yml +0 -14
 - data/Rakefile +0 -15
 - data/benchmarks/Gemfile +0 -16
 - data/benchmarks/Gemfile.lock +0 -142
 - data/benchmarks/README.md +0 -29
 - data/benchmarks/apps/committee_with_hanami_api.ru +0 -26
 - data/benchmarks/apps/committee_with_response_validation.ru +0 -29
 - data/benchmarks/apps/committee_with_sinatra.ru +0 -31
 - data/benchmarks/apps/grape.ru +0 -21
 - data/benchmarks/apps/hanami_api.ru +0 -21
 - data/benchmarks/apps/hanami_router.ru +0 -14
 - data/benchmarks/apps/openapi.yaml +0 -268
 - data/benchmarks/apps/openapi_first_with_hanami_api.ru +0 -24
 - data/benchmarks/apps/openapi_first_with_plain_rack.ru +0 -32
 - data/benchmarks/apps/openapi_first_with_response_validation.ru +0 -25
 - data/benchmarks/apps/openapi_first_with_sinatra.ru +0 -29
 - data/benchmarks/apps/roda.ru +0 -27
 - data/benchmarks/apps/sinatra.ru +0 -26
 - data/benchmarks/apps/syro.ru +0 -25
 - data/benchmarks/benchmark-wrk.sh +0 -3
 - data/benchmarks/benchmarks.rb +0 -48
 - data/benchmarks/post.lua +0 -3
 - data/bin/console +0 -15
 - data/bin/setup +0 -8
 - data/examples/README.md +0 -13
 - data/examples/app.rb +0 -18
 - data/examples/config.ru +0 -7
 - data/examples/openapi.yaml +0 -29
 - data/lib/openapi_first/operation.rb +0 -170
 - data/lib/openapi_first/string_keyed_hash.rb +0 -20
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 8173c11075add6c2c7a1a6cf024ea86ba448b185574e346fe288afa3aafcf677
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: f3dcd95b9c06adcfcada5430a8412a3ee6ea37c1c117e79546436ade496c5778
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: d29578cdcc1573ff8903a7060a85e2b334b1ff908d4b3b7973e64b336ba414bfdf7b6fb762aff48cf0feaf78f5a29403d2d063b117fb1278bed628195363dd74
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 0a6409930941ea9a1f4b734f49752ec18f8e9779ce6f5279e2db65022066500d8b7f9067af23625e780e20cf0d62f542c0a4b8388fea6f7cc37f63cd372baaf5
         
     | 
    
        data/.github/workflows/ruby.yml
    CHANGED
    
    | 
         @@ -9,4 +9,5 @@ jobs: 
     | 
|
| 
       9 
9 
     | 
    
         
             
                  with:
         
     | 
| 
       10 
10 
     | 
    
         
             
                    ruby-version: '3.1'
         
     | 
| 
       11 
11 
     | 
    
         
             
                    bundler-cache: true # runs 'bundle install' and caches installed gems automatically
         
     | 
| 
       12 
     | 
    
         
            -
                - run: bundle exec rake
         
     | 
| 
      
 12 
     | 
    
         
            +
                - run: BUNDLE_GEMFILE=Gemfile bundle exec rake
         
     | 
| 
      
 13 
     | 
    
         
            +
                - run: BUNDLE_GEMFILE=Gemfile.rack2 bundle lock --add-platform x86_64-linux && bundle exec rake
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -2,6 +2,14 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            ## Unreleased
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
            ## 1.0.0.beta6
         
     | 
| 
      
 6 
     | 
    
         
            +
            - Fix: Make response header validation work with rack 3
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Refactor router
         
     | 
| 
      
 8 
     | 
    
         
            +
              - Remove dependency hanami-router 
         
     | 
| 
      
 9 
     | 
    
         
            +
              - PathItem and Operation for a request can be found by calling methods on the Definitnion
         
     | 
| 
      
 10 
     | 
    
         
            +
            - Fixed https://github.com/ahx/openapi_first/issues/155
         
     | 
| 
      
 11 
     | 
    
         
            +
            - Breaking / Regression: A paths like /pets/{from}-{to} if there is a path "/pets/{id}"
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
       5 
13 
     | 
    
         
             
            ## 1.0.0.beta5
         
     | 
| 
       6 
14 
     | 
    
         | 
| 
       7 
15 
     | 
    
         
             
            - Added: `OpenapiFirst::Config.default_options=` to set default options globally
         
     | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/Gemfile.lock
    CHANGED
    
    | 
         @@ -1,12 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            PATH
         
     | 
| 
       2 
2 
     | 
    
         
             
              remote: .
         
     | 
| 
       3 
3 
     | 
    
         
             
              specs:
         
     | 
| 
       4 
     | 
    
         
            -
                openapi_first (1.0.0. 
     | 
| 
       5 
     | 
    
         
            -
                  hanami-router (~> 2.0.0)
         
     | 
| 
      
 4 
     | 
    
         
            +
                openapi_first (1.0.0.beta6)
         
     | 
| 
       6 
5 
     | 
    
         
             
                  json_refs (~> 0.1, >= 0.1.7)
         
     | 
| 
       7 
6 
     | 
    
         
             
                  json_schemer (~> 2.0.0)
         
     | 
| 
       8 
7 
     | 
    
         
             
                  multi_json (~> 1.15)
         
     | 
| 
       9 
     | 
    
         
            -
                   
     | 
| 
      
 8 
     | 
    
         
            +
                  mustermann-contrib (~> 3.0.0)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  openapi_parameters (>= 0.3.2, < 2.0)
         
     | 
| 
       10 
10 
     | 
    
         
             
                  rack (>= 2.2, < 4.0)
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
            GEM
         
     | 
| 
         @@ -15,10 +15,6 @@ GEM 
     | 
|
| 
       15 
15 
     | 
    
         
             
                ast (2.4.2)
         
     | 
| 
       16 
16 
     | 
    
         
             
                diff-lcs (1.5.0)
         
     | 
| 
       17 
17 
     | 
    
         
             
                hana (1.3.7)
         
     | 
| 
       18 
     | 
    
         
            -
                hanami-router (2.0.2)
         
     | 
| 
       19 
     | 
    
         
            -
                  mustermann (~> 3.0)
         
     | 
| 
       20 
     | 
    
         
            -
                  mustermann-contrib (~> 3.0)
         
     | 
| 
       21 
     | 
    
         
            -
                  rack (~> 2.0)
         
     | 
| 
       22 
18 
     | 
    
         
             
                hansi (0.2.1)
         
     | 
| 
       23 
19 
     | 
    
         
             
                json (2.6.3)
         
     | 
| 
       24 
20 
     | 
    
         
             
                json_refs (0.1.8)
         
     | 
| 
         @@ -34,7 +30,7 @@ GEM 
     | 
|
| 
       34 
30 
     | 
    
         
             
                mustermann-contrib (3.0.0)
         
     | 
| 
       35 
31 
     | 
    
         
             
                  hansi (~> 0.2.0)
         
     | 
| 
       36 
32 
     | 
    
         
             
                  mustermann (= 3.0.0)
         
     | 
| 
       37 
     | 
    
         
            -
                openapi_parameters (0.3. 
     | 
| 
      
 33 
     | 
    
         
            +
                openapi_parameters (0.3.2)
         
     | 
| 
       38 
34 
     | 
    
         
             
                  rack (>= 2.2)
         
     | 
| 
       39 
35 
     | 
    
         
             
                  zeitwerk (~> 2.6)
         
     | 
| 
       40 
36 
     | 
    
         
             
                parallel (1.23.0)
         
     | 
| 
         @@ -42,7 +38,7 @@ GEM 
     | 
|
| 
       42 
38 
     | 
    
         
             
                  ast (~> 2.4.1)
         
     | 
| 
       43 
39 
     | 
    
         
             
                  racc
         
     | 
| 
       44 
40 
     | 
    
         
             
                racc (1.7.3)
         
     | 
| 
       45 
     | 
    
         
            -
                rack ( 
     | 
| 
      
 41 
     | 
    
         
            +
                rack (3.0.8)
         
     | 
| 
       46 
42 
     | 
    
         
             
                rack-test (2.1.0)
         
     | 
| 
       47 
43 
     | 
    
         
             
                  rack (>= 1.3)
         
     | 
| 
       48 
44 
     | 
    
         
             
                rainbow (3.1.1)
         
     | 
| 
         @@ -92,6 +88,7 @@ PLATFORMS 
     | 
|
| 
       92 
88 
     | 
    
         
             
            DEPENDENCIES
         
     | 
| 
       93 
89 
     | 
    
         
             
              bundler
         
     | 
| 
       94 
90 
     | 
    
         
             
              openapi_first!
         
     | 
| 
      
 91 
     | 
    
         
            +
              rack (>= 3.0.0)
         
     | 
| 
       95 
92 
     | 
    
         
             
              rack-test
         
     | 
| 
       96 
93 
     | 
    
         
             
              rake
         
     | 
| 
       97 
94 
     | 
    
         
             
              rspec
         
     | 
    
        data/Gemfile.rack2
    ADDED
    
    
| 
         @@ -3,21 +3,7 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require 'multi_json'
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            module OpenapiFirst
         
     | 
| 
       6 
     | 
    
         
            -
              class  
     | 
| 
       7 
     | 
    
         
            -
                def initialize(app)
         
     | 
| 
       8 
     | 
    
         
            -
                  @app = app
         
     | 
| 
       9 
     | 
    
         
            -
                end
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
                ROUTER_PARSED_BODY = 'router.parsed_body'
         
     | 
| 
       12 
     | 
    
         
            -
                private_constant :ROUTER_PARSED_BODY
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
                def call(env)
         
     | 
| 
       15 
     | 
    
         
            -
                  env[ROUTER_PARSED_BODY] = parse_body(env)
         
     | 
| 
       16 
     | 
    
         
            -
                  @app.call(env)
         
     | 
| 
       17 
     | 
    
         
            -
                end
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
                private
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
      
 6 
     | 
    
         
            +
              class BodyParser
         
     | 
| 
       21 
7 
     | 
    
         
             
                def parse_body(env)
         
     | 
| 
       22 
8 
     | 
    
         
             
                  request = Rack::Request.new(env)
         
     | 
| 
       23 
9 
     | 
    
         
             
                  body = read_body(request)
         
     | 
| 
         @@ -31,6 +17,8 @@ module OpenapiFirst 
     | 
|
| 
       31 
17 
     | 
    
         
             
                  raise BodyParsingError, 'Failed to parse body as application/json'
         
     | 
| 
       32 
18 
     | 
    
         
             
                end
         
     | 
| 
       33 
19 
     | 
    
         | 
| 
      
 20 
     | 
    
         
            +
                private
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
       34 
22 
     | 
    
         
             
                def read_body(request)
         
     | 
| 
       35 
23 
     | 
    
         
             
                  body = request.body.read
         
     | 
| 
       36 
24 
     | 
    
         
             
                  request.body.rewind
         
     | 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'openapi_parameters'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'parameters'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module OpenapiFirst
         
     | 
| 
      
 7 
     | 
    
         
            +
              class CookieParameters < Parameters
         
     | 
| 
      
 8 
     | 
    
         
            +
                def unpack(env)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  OpenapiParameters::Cookie.new(@parameter_definitions).unpack(env['HTTP_COOKIE'])
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'schema'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module OpenapiFirst
         
     | 
| 
      
 6 
     | 
    
         
            +
              module HasContent
         
     | 
| 
      
 7 
     | 
    
         
            +
                def schema_for(content_type)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  return unless content&.any?
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  content_schemas&.fetch(content_type) do
         
     | 
| 
      
 11 
     | 
    
         
            +
                    type = content_type.split(';')[0]
         
     | 
| 
      
 12 
     | 
    
         
            +
                    content_schemas[type] || content_schemas["#{type.split('/')[0]}/*"] || content_schemas['*/*']
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                private
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def content
         
     | 
| 
      
 19 
     | 
    
         
            +
                  raise NotImplementedError
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def schema_write?
         
     | 
| 
      
 23 
     | 
    
         
            +
                  raise NotImplementedError
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def content_schemas
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @content_schemas ||= content&.each_with_object({}) do |kv, result|
         
     | 
| 
      
 28 
     | 
    
         
            +
                    type, media_type = kv
         
     | 
| 
      
 29 
     | 
    
         
            +
                    schema_object = media_type['schema']
         
     | 
| 
      
 30 
     | 
    
         
            +
                    next unless schema_object
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    result[type] = Schema.new(schema_object, write: schema_write?,
         
     | 
| 
      
 33 
     | 
    
         
            +
                                                             openapi_version: @operation.openapi_version)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'openapi_parameters'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'parameters'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module OpenapiFirst
         
     | 
| 
      
 7 
     | 
    
         
            +
              class HeaderParameters < Parameters
         
     | 
| 
      
 8 
     | 
    
         
            +
                def unpack(env)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  OpenapiParameters::Header.new(@parameter_definitions).unpack_env(env)
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,103 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'forwardable'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'set'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative 'request_body'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require_relative 'response'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require_relative 'query_parameters'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require_relative 'header_parameters'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require_relative 'path_parameters'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require_relative 'cookie_parameters'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require_relative 'schema'
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            module OpenapiFirst
         
     | 
| 
      
 14 
     | 
    
         
            +
              class Operation
         
     | 
| 
      
 15 
     | 
    
         
            +
                extend Forwardable
         
     | 
| 
      
 16 
     | 
    
         
            +
                def_delegators :operation_object,
         
     | 
| 
      
 17 
     | 
    
         
            +
                               :[],
         
     | 
| 
      
 18 
     | 
    
         
            +
                               :dig
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                WRITE_METHODS = Set.new(%w[post put patch delete]).freeze
         
     | 
| 
      
 21 
     | 
    
         
            +
                private_constant :WRITE_METHODS
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                attr_reader :path, :method, :openapi_version
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def initialize(path, request_method, path_item_object, openapi_version:)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @path = path
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @method = request_method
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @path_item_object = path_item_object
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @openapi_version = openapi_version
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @operation_object = @path_item_object[request_method]
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def operation_id
         
     | 
| 
      
 34 
     | 
    
         
            +
                  operation_object['operationId']
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def read?
         
     | 
| 
      
 38 
     | 
    
         
            +
                  !write?
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def write?
         
     | 
| 
      
 42 
     | 
    
         
            +
                  WRITE_METHODS.include?(method)
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def request_body
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @request_body ||= RequestBody.new(operation_object['requestBody'], self) if operation_object['requestBody']
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def response_for(status)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  response_object = operation_object.dig('responses', status.to_s) ||
         
     | 
| 
      
 51 
     | 
    
         
            +
                                    operation_object.dig('responses', "#{status / 100}XX") ||
         
     | 
| 
      
 52 
     | 
    
         
            +
                                    operation_object.dig('responses', "#{status / 100}xx") ||
         
     | 
| 
      
 53 
     | 
    
         
            +
                                    operation_object.dig('responses', 'default')
         
     | 
| 
      
 54 
     | 
    
         
            +
                  Response.new(status, response_object, self) if response_object
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def name
         
     | 
| 
      
 58 
     | 
    
         
            +
                  @name ||= "#{method.upcase} #{path} (#{operation_id})"
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                def query_parameters
         
     | 
| 
      
 62 
     | 
    
         
            +
                  @query_parameters ||= build_parameters(all_parameters.filter { |p| p['in'] == 'query' }, QueryParameters)
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                def path_parameters
         
     | 
| 
      
 66 
     | 
    
         
            +
                  @path_parameters ||= build_parameters(all_parameters.filter { |p| p['in'] == 'path' }, PathParameters)
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                IGNORED_HEADERS = Set['Content-Type', 'Accept', 'Authorization'].freeze
         
     | 
| 
      
 70 
     | 
    
         
            +
                private_constant :IGNORED_HEADERS
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                def header_parameters
         
     | 
| 
      
 73 
     | 
    
         
            +
                  @header_parameters ||= build_parameters(find_header_parameters, HeaderParameters)
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                def cookie_parameters
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @cookie_parameters ||= build_parameters(all_parameters.filter { |p| p['in'] == 'cookie' }, CookieParameters)
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                def all_parameters
         
     | 
| 
      
 81 
     | 
    
         
            +
                  @all_parameters ||= begin
         
     | 
| 
      
 82 
     | 
    
         
            +
                    parameters = @path_item_object['parameters']&.dup || []
         
     | 
| 
      
 83 
     | 
    
         
            +
                    parameters_on_operation = operation_object['parameters']
         
     | 
| 
      
 84 
     | 
    
         
            +
                    parameters.concat(parameters_on_operation) if parameters_on_operation
         
     | 
| 
      
 85 
     | 
    
         
            +
                    parameters
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                private
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                attr_reader :operation_object
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                def build_parameters(parameters, klass)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  klass.new(parameters, openapi_version:) if parameters.any?
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                def find_header_parameters
         
     | 
| 
      
 98 
     | 
    
         
            +
                  all_parameters.filter do |p|
         
     | 
| 
      
 99 
     | 
    
         
            +
                    p['in'] == 'header' && !IGNORED_HEADERS.include?(p['name'])
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end
         
     | 
| 
      
 101 
     | 
    
         
            +
                end
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'forwardable'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'schema'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module OpenapiFirst
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Parameters
         
     | 
| 
      
 8 
     | 
    
         
            +
                extend Forwardable
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(parameter_definitions, openapi_version:)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @parameter_definitions = parameter_definitions
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @openapi_version = openapi_version
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def_delegators :parameters, :map
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def empty?
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @parameter_definitions.empty?
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def schema
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @schema ||= build_schema
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def parameters
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @parameter_definitions.map do |parameter_object|
         
     | 
| 
      
 27 
     | 
    
         
            +
                    OpenapiParameters::Parameter.new(parameter_object)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                private
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def build_schema
         
     | 
| 
      
 34 
     | 
    
         
            +
                  init_schema = {
         
     | 
| 
      
 35 
     | 
    
         
            +
                    'type' => 'object',
         
     | 
| 
      
 36 
     | 
    
         
            +
                    'properties' => {},
         
     | 
| 
      
 37 
     | 
    
         
            +
                    'required' => []
         
     | 
| 
      
 38 
     | 
    
         
            +
                  }
         
     | 
| 
      
 39 
     | 
    
         
            +
                  schema = @parameter_definitions.each_with_object(init_schema) do |parameter_def, result|
         
     | 
| 
      
 40 
     | 
    
         
            +
                    parameter = OpenapiParameters::Parameter.new(parameter_def)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    result['properties'][parameter.name] = parameter.schema if parameter.schema
         
     | 
| 
      
 42 
     | 
    
         
            +
                    result['required'] << parameter.name if parameter.required?
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
                  Schema.new(schema, openapi_version: @openapi_version)
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'operation'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module OpenapiFirst
         
     | 
| 
      
 6 
     | 
    
         
            +
              class PathItem
         
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(path, path_item_object, openapi_version:)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @path = path
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @path_item_object = path_item_object
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @openapi_version = openapi_version
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                attr_reader :path
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def find_operation(request_method)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  return unless @path_item_object[request_method]
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  Operation.new(
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @path, request_method, @path_item_object, openapi_version: @openapi_version
         
     | 
| 
      
 20 
     | 
    
         
            +
                  )
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'openapi_parameters'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'parameters'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative '../router'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module OpenapiFirst
         
     | 
| 
      
 8 
     | 
    
         
            +
              class PathParameters < Parameters
         
     | 
| 
      
 9 
     | 
    
         
            +
                def unpack(env)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  OpenapiParameters::Path.new(@parameter_definitions).unpack(env[Router::RAW_PATH_PARAMS])
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'openapi_parameters'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'parameters'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module OpenapiFirst
         
     | 
| 
      
 7 
     | 
    
         
            +
              class QueryParameters < Parameters
         
     | 
| 
      
 8 
     | 
    
         
            +
                def unpack(env)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  OpenapiParameters::Query.new(@parameter_definitions).unpack(env['QUERY_STRING'])
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'has_content'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module OpenapiFirst
         
     | 
| 
      
 6 
     | 
    
         
            +
              class RequestBody
         
     | 
| 
      
 7 
     | 
    
         
            +
                include HasContent
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize(request_body_object, operation)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @object = request_body_object
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @operation = operation
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def description
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @object['description']
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def required?
         
     | 
| 
      
 19 
     | 
    
         
            +
                  !!@object['required']
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                private
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def schema_write?
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @operation.write?
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def content
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @object['content']
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'has_content'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module OpenapiFirst
         
     | 
| 
      
 6 
     | 
    
         
            +
              class Response
         
     | 
| 
      
 7 
     | 
    
         
            +
                include HasContent
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize(status, response_object, operation)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @status = status&.to_i
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @object = response_object
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @operation = operation
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                attr_reader :status
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def description
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @object['description']
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def headers
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @object['headers']
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def content?
         
     | 
| 
      
 26 
     | 
    
         
            +
                  !!content&.any?
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                private
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def schema_write? = false
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def content
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @object['content']
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,19 +1,39 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'mustermann/template'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'definition/path_item'
         
     | 
| 
       4 
5 
     | 
    
         | 
| 
       5 
6 
     | 
    
         
             
            module OpenapiFirst
         
     | 
| 
       6 
7 
     | 
    
         
             
              # Represents an OpenAPI API Description document
         
     | 
| 
       7 
8 
     | 
    
         
             
              class Definition
         
     | 
| 
       8 
     | 
    
         
            -
                attr_reader :filepath, : 
     | 
| 
      
 9 
     | 
    
         
            +
                attr_reader :filepath, :paths, :openapi_version
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
11 
     | 
    
         
             
                def initialize(resolved, filepath)
         
     | 
| 
       11 
12 
     | 
    
         
             
                  @filepath = filepath
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @paths = resolved['paths']
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @openapi_version = detect_version(resolved)
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # @param request_path String
         
     | 
| 
      
 18 
     | 
    
         
            +
                def find_path_item_and_params(request_path)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  matches = paths.each_with_object([]) do |kv, result|
         
     | 
| 
      
 20 
     | 
    
         
            +
                    path, path_item_object = kv
         
     | 
| 
      
 21 
     | 
    
         
            +
                    template = Mustermann::Template.new(path)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    path_params = template.params(request_path)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    next unless path_params
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    path_item = PathItem.new(path, path_item_object, openapi_version:)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    result << [path_item, path_params]
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # Thanks to open ota42y/openapi_parser for this part
         
     | 
| 
      
 29 
     | 
    
         
            +
                  matches.min_by { |match| match[1].size }
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def operations
         
     | 
| 
       12 
33 
     | 
    
         
             
                  methods = %w[get head post put patch delete trace options]
         
     | 
| 
       13 
     | 
    
         
            -
                  @operations  
     | 
| 
       14 
     | 
    
         
            -
                    path_item. 
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @operations ||= paths.flat_map do |path, path_item_object|
         
     | 
| 
      
 35 
     | 
    
         
            +
                    path_item = PathItem.new(path, path_item_object, openapi_version:)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    path_item_object.slice(*methods).keys.map { |method| path_item.find_operation(method) }
         
     | 
| 
       17 
37 
     | 
    
         
             
                  end
         
     | 
| 
       18 
38 
     | 
    
         
             
                end
         
     | 
| 
       19 
39 
     | 
    
         | 
| 
         @@ -5,37 +5,33 @@ module OpenapiFirst 
     | 
|
| 
       5 
5 
     | 
    
         
             
                def initialize(operation, env)
         
     | 
| 
       6 
6 
     | 
    
         
             
                  @operation = operation
         
     | 
| 
       7 
7 
     | 
    
         
             
                  @env = env
         
     | 
| 
       8 
     | 
    
         
            -
                  @parsed_request_body = env[REQUEST_BODY]
         
     | 
| 
       9 
8 
     | 
    
         
             
                end
         
     | 
| 
       10 
9 
     | 
    
         | 
| 
       11 
10 
     | 
    
         
             
                def validate!
         
     | 
| 
       12 
     | 
    
         
            -
                   
     | 
| 
       13 
     | 
    
         
            -
                   
     | 
| 
       14 
     | 
    
         
            -
                  validate_request_body!(@operation, @parsed_request_body, content_type)
         
     | 
| 
       15 
     | 
    
         
            -
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
                  request_body = @operation.request_body
         
     | 
| 
      
 12 
     | 
    
         
            +
                  return unless request_body
         
     | 
| 
       16 
13 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
      
 14 
     | 
    
         
            +
                  request_content_type = Rack::Request.new(@env).content_type
         
     | 
| 
      
 15 
     | 
    
         
            +
                  schema = request_body.schema_for(request_content_type)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  RequestValidation.fail!(415, :header) unless schema
         
     | 
| 
       18 
17 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
                   
     | 
| 
      
 18 
     | 
    
         
            +
                  parsed_request_body = BodyParser.new.parse_body(@env)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  RequestValidation.fail!(400, :body) if request_body.required? && parsed_request_body.nil?
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  validate_body!(parsed_request_body, schema)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  parsed_request_body
         
     | 
| 
      
 23 
     | 
    
         
            +
                rescue BodyParsingError => e
         
     | 
| 
      
 24 
     | 
    
         
            +
                  RequestValidation.fail!(400, :body, message: e.message)
         
     | 
| 
       21 
25 
     | 
    
         
             
                end
         
     | 
| 
       22 
26 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                 
     | 
| 
       24 
     | 
    
         
            -
                  validate_request_body_presence!(body, operation)
         
     | 
| 
       25 
     | 
    
         
            -
                  return if content_type.nil?
         
     | 
| 
      
 27 
     | 
    
         
            +
                private
         
     | 
| 
       26 
28 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
                   
     | 
| 
      
 29 
     | 
    
         
            +
                def validate_body!(parsed_request_body, schema)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  request_body_schema = schema
         
     | 
| 
      
 31 
     | 
    
         
            +
                  return unless request_body_schema
         
     | 
| 
       29 
32 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
                  schema_validation =  
     | 
| 
      
 33 
     | 
    
         
            +
                  schema_validation = request_body_schema.validate(parsed_request_body)
         
     | 
| 
       31 
34 
     | 
    
         
             
                  RequestValidation.fail!(400, :body, schema_validation:) if schema_validation.error?
         
     | 
| 
       32 
     | 
    
         
            -
                  body
         
     | 
| 
       33 
     | 
    
         
            -
                end
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                def validate_request_body_presence!(body, operation)
         
     | 
| 
       36 
     | 
    
         
            -
                  return unless operation.request_body['required'] && body.nil?
         
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
                  RequestValidation.fail!(400, :body)
         
     | 
| 
       39 
35 
     | 
    
         
             
                end
         
     | 
| 
       40 
36 
     | 
    
         
             
              end
         
     | 
| 
       41 
37 
     | 
    
         
             
            end
         
     |