faraday-openapi 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 179cc13a116b7e00f6a1e36d860ae280235e2d8693407cdb31e4b4cca624234b
4
+ data.tar.gz: 14fdb596d1505e47cf59394302c907f02e5f43b2e60a2fe1dc1e278760959f0d
5
+ SHA512:
6
+ metadata.gz: a190ea86e84c6263c5150057d4b749320076b078d799b528f1ddca0bc345da0e82eb99deeb1fe24580e939de2a120f8da87e910800860fd03252d4efef6bff49
7
+ data.tar.gz: 4e9bad10ef7d26b02fb07b3645fd7a926e18a541ddbcf5b85b3456d4a5a78e42e58cba85947e1e2e63e76500bb600e1dfff22a582bf30d2bbddd638c28fe49c3
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.1.0
6
+
7
+ Initial release.
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Andreas Haller
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # Faraday Openapi
2
+
3
+ Validate requests/responses against OpenAPI API descriptions.
4
+
5
+ This middleware raises an error if the request or response does not match the given API description.
6
+ You can use this to test your client code or to make sure your mocks do match the actual implementation, described in the API description.
7
+
8
+ Note that the middleware currently deliberately ignores **unknown** responses with status codes 401 or higher because those usually don't come with a useful response body.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'faraday-openapi'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ ```shell
21
+ bundle install
22
+ ```
23
+
24
+ Or install it yourself as:
25
+
26
+ ```shell
27
+ gem install faraday-openapi
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```ruby
33
+ require 'faraday/openapi'
34
+
35
+ conn = Faraday.new do |f|
36
+ f.use :openapi, 'openapi/openapi.yaml'
37
+ end
38
+
39
+ # Or validate only requests
40
+ conn = Faraday.new do |f|
41
+ f.request :openapi, 'openapi/openapi.yaml'
42
+ end
43
+
44
+ # Or validate only responses
45
+ conn = Faraday.new do |f|
46
+ f.response :openapi, 'openapi/openapi.yaml'
47
+ end
48
+ ```
49
+
50
+ You can disable the whole middleware globally:
51
+
52
+ ```ruby
53
+ Faraday::Openapi::Middleware.default_options[:enabled] = false
54
+ ```
55
+
56
+
57
+ ## Development
58
+
59
+ After checking out the repo, run `bin/setup` to install dependencies.
60
+
61
+ Then, run `bin/test` to run the tests.
62
+
63
+ To install this gem onto your local machine, run `rake build`.
64
+
65
+ To release a new version, make a commit with a message such as "Bumped to 0.0.2" and then run `rake release`.
66
+ See how it works [here](https://bundler.io/guides/creating_gem.html#releasing-the-gem).
67
+
68
+ ## Contributing
69
+
70
+ Bug reports and pull requests are welcome on [Codeberg](https://codeberg.org/ahx/faraday-openapi).
71
+
72
+ ## License
73
+
74
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ module Openapi
5
+ # Base class for all errors
6
+ class Error < StandardError; end
7
+
8
+ # Raised if request does not match API description or is unknown
9
+ class RequestInvalidError < Error; end
10
+
11
+ # Raised if response does not match API description or is unknown
12
+ class ResponseInvalidError < Error; end
13
+ end
14
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'openapi_first'
5
+ require_relative 'request'
6
+ require_relative 'response'
7
+
8
+ module Faraday
9
+ module Openapi
10
+ # This class provides the main implementation for your middleware.
11
+ # Your middleware can implement any of the following methods:
12
+ # * on_request - called when the request is being prepared
13
+ # * on_complete - called when the response is being processed
14
+ #
15
+ # Optionally, you can also override the following methods from Faraday::Middleware
16
+ # * initialize(app, options = {}) - the initializer method
17
+ # * call(env) - the main middleware invocation method.
18
+ # This already calls on_request and on_complete, so you normally don't need to override it.
19
+ # You may need to in case you need to "wrap" the request or need more control
20
+ # (see "retry" middleware: https://github.com/lostisland/faraday-retry/blob/41b7ea27e30d99ebfed958abfa11d12b01f6b6d1/lib/faraday/retry/middleware.rb#L147).
21
+ # IMPORTANT: Remember to call `@app.call(env)` or `super` to not interrupt the middleware chain!
22
+ class Middleware < Faraday::Middleware
23
+ DEFAULT_OPTIONS = { enabled: true }.freeze
24
+
25
+ def initialize(app, filepath)
26
+ super(app)
27
+ @filepath = filepath
28
+ @enabled = options.fetch(:enabled, true)
29
+ @oad = OpenapiFirst.load(filepath) if @enabled
30
+ end
31
+
32
+ def call(env)
33
+ return app.call(env) unless @enabled
34
+
35
+ super
36
+ end
37
+
38
+ # This method will be called when the request is being prepared.
39
+ # You can alter it as you like, accessing things like request_body, request_headers, and more.
40
+ # Refer to Faraday::Env for a list of accessible fields:
41
+ # https://github.com/lostisland/faraday/blob/main/lib/faraday/options/env.rb
42
+ #
43
+ # @param env [Faraday::Env] the environment of the request being processed
44
+ def on_request(env)
45
+ request = Request.from_env(env)
46
+ @oad.validate_request(request, raise_error: true)
47
+ rescue OpenapiFirst::RequestInvalidError => e
48
+ raise RequestInvalidError, e.message
49
+ end
50
+
51
+ # This method will be called when the response is being processed.
52
+ # You can alter it as you like, accessing things like response_body, response_headers, and more.
53
+ # Refer to Faraday::Env for a list of accessible fields:
54
+ # https://github.com/lostisland/faraday/blob/main/lib/faraday/options/env.rb
55
+ #
56
+ # @param env [Faraday::Env] the environment of the response being processed.
57
+ def on_complete(env)
58
+ request = Request.from_env(env)
59
+ response = Response.from_env(env)
60
+ @oad.validate_response(request, response, raise_error: true)
61
+ rescue OpenapiFirst::ResponseInvalidError, OpenapiFirst::ResponseNotFoundError => e
62
+ return if e.is_a?(OpenapiFirst::ResponseNotFoundError) && (response.status >= 401)
63
+
64
+ raise ResponseInvalidError, e.message
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack'
4
+
5
+ module Faraday
6
+ module Openapi
7
+ # @visibility private
8
+ # Build a Rack::Request from a Faraday::Env
9
+ module Request
10
+ def self.from_env(env) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
11
+ if env.request_body.is_a?(Hash)
12
+ raise Error,
13
+ 'env.body is a Hash. You probably want to convert the request body ' \
14
+ 'to JSON before calling the Faraday openapi middleware.'
15
+ end
16
+
17
+ rack_env = {
18
+ 'REQUEST_METHOD' => env.method.to_s.upcase,
19
+ 'PATH_INFO' => env.url.path,
20
+ 'QUERY_STRING' => env.url.query || '',
21
+ 'SERVER_NAME' => env.url.host,
22
+ 'SERVER_PORT' => env.url.port.to_s,
23
+ 'rack.url_scheme' => env.url.scheme,
24
+ 'rack.input' => StringIO.new(env.request_body || '')
25
+ }
26
+
27
+ env.request_headers.each do |key, value|
28
+ rack_env["HTTP_#{key.upcase.tr('-', '_')}"] = value
29
+ end
30
+
31
+ Rack::Request.new(rack_env)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack'
4
+
5
+ module Faraday
6
+ module Openapi
7
+ # @visibility private
8
+ # Build a Rack::Response from a Faraday::Env
9
+ module Response
10
+ def self.from_env(env)
11
+ status = env.status
12
+ headers = env.response_headers
13
+ body = [env.response_body]
14
+
15
+ Rack::Response.new(body, status, headers)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ module Openapi
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'openapi/errors'
4
+ require_relative 'openapi/middleware'
5
+ require_relative 'openapi/version'
6
+
7
+ module Faraday
8
+ # This will be your middleware main module, though the actual middleware implementation will go
9
+ # into Faraday::Openapi::Middleware for the correct namespacing.
10
+ module Openapi
11
+ # Faraday allows you to register your middleware for easier configuration.
12
+ # This step is totally optional, but it basically allows users to use a
13
+ # custom symbol (in this case, `:openapi`), to use your middleware in their connections.
14
+ # After calling this line, the following are both valid ways to set the middleware in a connection:
15
+ # * conn.use Faraday::Openapi::Middleware
16
+ # * conn.use :openapi
17
+ # Without this line, only the former method is valid.
18
+ Faraday::Middleware.register_middleware(openapi: Faraday::Openapi::Middleware)
19
+ Faraday::Request.register_middleware(openapi: Faraday::Openapi::Middleware)
20
+ Faraday::Response.register_middleware(openapi: Faraday::Openapi::Middleware)
21
+
22
+ # Alternatively, you can register your middleware under Faraday::Request or Faraday::Response.
23
+ # This will allow to load your middleware using the `request` or `response` methods respectively.
24
+ #
25
+ # Load middleware with conn.request :openapi
26
+ # Faraday::Request.register_middleware(openapi: Faraday::Openapi::Middleware)
27
+ #
28
+ # Load middleware with conn.response :openapi
29
+ # Faraday::Response.register_middleware(openapi: Faraday::Openapi::Middleware)
30
+ end
31
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: faraday-openapi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andreas Haller
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-02-18 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: faraday
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '2.9'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '3'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '2.9'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '3'
32
+ - !ruby/object:Gem::Dependency
33
+ name: openapi_first
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '2.3'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '3'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '2.3'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '3'
52
+ - !ruby/object:Gem::Dependency
53
+ name: rack
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '2.2'
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '2.2'
69
+ - - "<"
70
+ - !ruby/object:Gem::Version
71
+ version: '4.0'
72
+ description: 'Validate requests/responses against OpenAPI API descriptions.
73
+
74
+ '
75
+ email:
76
+ - andreas.haller@posteo.com
77
+ executables: []
78
+ extensions: []
79
+ extra_rdoc_files: []
80
+ files:
81
+ - CHANGELOG.md
82
+ - LICENSE.md
83
+ - README.md
84
+ - lib/faraday/openapi.rb
85
+ - lib/faraday/openapi/errors.rb
86
+ - lib/faraday/openapi/middleware.rb
87
+ - lib/faraday/openapi/request.rb
88
+ - lib/faraday/openapi/response.rb
89
+ - lib/faraday/openapi/version.rb
90
+ homepage: https://codeberg.com/ahx/faraday-openapi
91
+ licenses:
92
+ - MIT
93
+ metadata:
94
+ bug_tracker_uri: https://codeberg.com/ahx/faraday-openapi/issues
95
+ changelog_uri: https://codeberg.com/ahx/faraday-openapi/src/branch/main/CHANGELOG.md
96
+ documentation_uri: http://www.rubydoc.info/gems/faraday-openapi/0.1.0
97
+ homepage_uri: https://codeberg.com/ahx/faraday-openapi
98
+ rubygems_mfa_required: 'true'
99
+ source_code_uri: https://codeberg.com/ahx/faraday-openapi
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '3.2'
108
+ - - "<"
109
+ - !ruby/object:Gem::Version
110
+ version: '4'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubygems_version: 3.6.2
118
+ specification_version: 4
119
+ summary: Validate requests/responses against OpenAPI API descriptions
120
+ test_files: []