Skip to content

Commit 56abad4

Browse files
authored
Merge pull request #3 from SiloGecho97/feat/call-validator-conditional
feat: add request and response schema validator conditionally
2 parents b42c679 + f6832ad commit 56abad4

File tree

7 files changed

+102
-22
lines changed

7 files changed

+102
-22
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@
3030

3131
# Ignore master key for decrypting credentials and more.
3232
/config/master.key
33+
.byebug_history

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ gem "scalar_ruby", "~> 1.1"
4040
group :development, :test do
4141
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
4242
gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
43-
43+
gem "byebug"
4444
# Static analysis for security vulnerabilities [https://brakemanscanner.org/]
4545
gem "brakeman", require: false
4646

Gemfile.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ GEM
8383
brakeman (7.0.0)
8484
racc
8585
builder (3.3.0)
86+
byebug (11.1.3)
8687
committee (5.5.1)
8788
json_schema (~> 0.14, >= 0.14.3)
8889
openapi_parser (~> 2.0)
@@ -304,6 +305,7 @@ PLATFORMS
304305
DEPENDENCIES
305306
bootsnap
306307
brakeman
308+
byebug
307309
committee
308310
debug
309311
kamal

README.md

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,41 @@
1-
# README
1+
# Documentation first API development
22

3-
This README would normally document whatever steps are necessary to get the
4-
application up and running.
3+
### What is Documentation-First Approach and its Benefits
54

6-
Things you may want to cover:
5+
The documentation-first approach, also known as design-first or API-first, emphasizes creating API documentation before writing any code.The documentation itself acts as a specification.
76

8-
* Ruby version
7+
### Benefits of Documentation-First Approach
98

10-
* System dependencies
9+
- **Clarity and Communication**: By documenting the API first, all team members, including developers, testers, and business stakeholders, have a clear understanding of the API's purpose and functionality.
10+
- **Early Feedback**: Documentation can be reviewed and validated by stakeholders before any code is written, allowing for early detection of potential issues or misunderstandings.
11+
- **Consistency**: Ensures that the implementation adheres strictly to the agreed-upon design, reducing the risk of discrepancies between the documentation and the actual API.
12+
- **Improved Collaboration**: Facilitates better collaboration between different teams (e.g., frontend and backend) as they can work from a shared, well-defined API specification.
13+
- **Better Testing**: Test cases can be derived from the documentation, ensuring comprehensive test coverage and validation against the specified API behavior.
14+
- **Ease of Maintenance**: Having a well-documented API makes it easier to maintain and update, as changes can be tracked and communicated effectively through the documentation.
1115

12-
* Configuration
16+
By adopting a documentation-first approach, teams can create more reliable, understandable, and maintainable APIs, ultimately leading to better software quality and user satisfaction.
1317

14-
* Database creation
18+
### OpenAPI Specification
1519

16-
* Database initialization
20+
The OpenAPI Specification (OAS) is a standard for defining and describing RESTful APIs. It allows both humans and computers to understand the capabilities of a service without accessing its source code or documentation.
1721

18-
* How to run the test suite
22+
OpenAPI uses a different specification: JSON Schema. This can be used to validate JSON even outside OpenAPI, and it’s a great tool; it’s really readable and easy to understand, but it’s a bit verbose. To fix that, we can use references:
1923

20-
* Services (job queues, cache servers, search engines, etc.)
24+
e.g., OpenAPI schema: [Petstore Example](https://petstore3.swagger.io/#/)
2125

22-
* Deployment instructions
26+
### Gem Used: `committee`
2327

24-
* ...
28+
We used the `committee` gem for our API documentation and validation. It helps in ensuring that our API conforms to the OpenAPI specification.
29+
30+
Altenative gem : [Skooma](https://github.com/skryukov/skooma), [Open-api-first](https://github.com/ahx/openapi_first)
31+
32+
### Non Breaking change
33+
34+
- Add headers to requests, only do validation if headers persent
35+
This is how the request validation added
36+
- For non-breaking changes, instead of returning errors, we log the errors. This approach helps in maintaining the stability of the API while still capturing any issues that need to be addressed.
37+
- this is how the response validation added
38+
39+
### Documenation UI
40+
41+
Scalar is add for open api schema UI

config/application.rb

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
# Require the gems listed in Gemfile, including any gems
66
# you've limited to :test, :development, or :production.
77
Bundler.require(*Rails.groups)
8-
8+
require_relative "../lib/open_api_schema/request_validator_middleware"
9+
require_relative "../lib/open_api_schema/response_validator_middleware"
910
module PocDocsFirst
1011
class Application < Rails::Application
1112
# Initialize configuration defaults for originally generated Rails version.
@@ -29,12 +30,9 @@ class Application < Rails::Application
2930
# Skip views, helpers and assets when generating a new resource.
3031
config.api_only = true
3132

32-
config.middleware.use Committee::Middleware::RequestValidation,
33-
schema_path: "docs/openapi.json",
34-
coerce_date_times: true,
35-
params_key: "action_dispatch.request.request_parameters",
36-
query_hash_key: "action_dispatch.request.query_parameters",
37-
strict_reference_validation: true
38-
config.middleware.use Committee::Middleware::ResponseValidation, schema_path: "docs/openapi.json", strict_reference_validation: true
33+
# Add the request and response validation middleware to the application.
34+
# TODO: This can be changed later to add middleware in exact position e.g config.middleware.after Rack::Runtime, OpenApiSchema::RequestValidatorMiddleware
35+
config.middleware.use ::OpenApiSchema::RequestValidatorMiddleware
36+
config.middleware.use ::OpenApiSchema::ResponseValidatorMiddleware
3937
end
4038
end
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
module OpenApiSchema
3+
class RequestValidatorMiddleware
4+
def initialize(app)
5+
@app = app
6+
@request_validator = Committee::Middleware::RequestValidation.new(app, schema_path: "docs/openapi.json", strict_reference_validation: true, coerce_date_times: true, params_key: "action_dispatch.request.request_parameters", query_hash_key: "action_dispatch.request.query_parameters")
7+
end
8+
9+
# Handles the middleware call to validate the schema if the "VALIDATE_SCHEMA" header is present.
10+
# If the header VALIDATE_SCHEMA is exist, it validates the request schema.
11+
#
12+
# @param env [Hash] The Rack environment hash.
13+
# @return [Array] The status, headers, and response.
14+
def call(env)
15+
status, headers, response = @app.call(env)
16+
17+
18+
if condition_for_request_validation?(env)
19+
status, headers, response = @request_validator.call(env)
20+
end
21+
22+
[ status, headers, response ]
23+
end
24+
25+
private
26+
27+
def condition_for_request_validation?(env)
28+
env.key?("HTTP_VALIDATE_SCHEMA")
29+
end
30+
end
31+
end
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
module OpenApiSchema
3+
class ResponseValidatorMiddleware
4+
# Initializes the middleware with the given Rack application and sets up the response validator.
5+
#
6+
# @param app [Object] The Rack application.
7+
def initialize(app)
8+
@app = app
9+
@response_validator = Committee::Middleware::ResponseValidation.new(app, schema_path: "docs/openapi.json", strict_reference_validation: true)
10+
end
11+
12+
# Sets up the middleware to validate the response schema if the "VALIDATE_SCHEMA" header is present.
13+
#
14+
# @param env [Hash] The Rack environment hash.
15+
# @return [Array] The status, headers, and response.
16+
def call(env)
17+
status, headers, response = @app.call(env)
18+
19+
if condition_for_response_validation?(env)
20+
status, headers, response = @response_validator.call(env)
21+
end
22+
23+
[ status, headers, response ]
24+
end
25+
26+
private
27+
def condition_for_response_validation?(env)
28+
env.key?("HTTP_VALIDATE_SCHEMA")
29+
end
30+
end
31+
end

0 commit comments

Comments
 (0)