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

Strict schema validation #604

Merged
merged 6 commits into from
Mar 31, 2023
Merged

Conversation

IlkhamGaysin
Copy link
Contributor

@IlkhamGaysin IlkhamGaysin commented Mar 5, 2023

Problem

I see some problems in ruby projects - it's data bloating. As projects grow they get complicated so it's hard to keep responses clean. It happed to projects I am working on and others(see the issue). So I decided to bring the changes back from initial issue.

Solution

I want to add a new configuration option on top of the main called additionalProperties from json-schema to allow users to validate their schemas strictly.

This concerns this parts of the OpenAPI Specification:

The changes I made are compatible with:

  • OAS2
  • OAS3
  • OAS3.1

Related Issues

Closes #163

Checklist

  • Added tests
  • Changelog updated
  • Added documentation to README.md

Steps to Test or Reproduce

Outline the steps to test or reproduce the PR here.

@IlkhamGaysin
Copy link
Contributor Author

@jtannas
Could you please take a look, thank you so much! 🍻

@romanblanco romanblanco linked an issue Mar 20, 2023 that may be closed by this pull request
@romanblanco romanblanco changed the title Strict schema validation, closes #163 Strict schema validation Mar 20, 2023
@romanblanco romanblanco added this to the Gem 2.X.0 milestone Mar 20, 2023
@IlkhamGaysin
Copy link
Contributor Author

IlkhamGaysin commented Mar 27, 2023

@romanblanco I've fixed conflicts and updated the branch

@rusllonrails
Copy link

Looks good 👍 Plus 1 for merging 🥇

@romanblanco
Copy link
Member

GitHub had issues earlier today. Re-opening the PR to try and run CI tests.

@romanblanco romanblanco reopened this Mar 27, 2023
Copy link
Member

@romanblanco romanblanco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@IlkhamGaysin, thank you for the PR!

I like the suggestion, but I've tried to run the tests locally and they're failing as well as in the CI.

Please, take a look at https://github.com/rswag/rswag/blob/master/CONTRIBUTING.md#test and update the PR.

@romanblanco
Copy link
Member

I'm also thinking that it would be really nice if you could update the test-app to provide an example of use of the strict option.

@romanblanco romanblanco self-assigned this Mar 27, 2023
@IlkhamGaysin
Copy link
Contributor Author

IlkhamGaysin commented Mar 28, 2023

@romanblanco I will fix the specs and add a test case, thank you. I had a problem with docker locally so I relied on CI...

@IlkhamGaysin
Copy link
Contributor Author

@romanblanco I've fixed the specs and added the test case. The GitHub Actions has issues but locally the test suites aregreen

@@ -132,7 +138,7 @@ def run_test!(&block)
end

it "returns a #{metadata[:response][:code]} response", rswag: true do |example|
assert_response_matches_metadata(example.metadata, &block)
assert_response_matches_metadata(example.metadata.merge(options), &block)
Copy link
Contributor

@a-lavis a-lavis Mar 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of merging **options with metadata, could we pass **options to it?

      def run_test!(**options, &block)
        options[:rswag] = true unless options.key?(:rswag)

        if RSPEC_VERSION < 3
          ActiveSupport::Deprecation.warn('Rswag::Specs: WARNING: Support for RSpec 2.X will be dropped in v3.0')
          before do
            submit_request(example.metadata)
          end

          it "returns a #{metadata[:response][:code]} response", **options do
            assert_response_matches_metadata(metadata)
            block.call(response) if block_given?
          end
        else
          before do |example|
            submit_request(example.metadata)
          end

          it "returns a #{metadata[:response][:code]} response", **options do |example|
            assert_response_matches_metadata(example.metadata, &block)
            example.instance_exec(response, &block) if block_given?
          end
        end
      end

This should work, according to the README:

rswag/README.md

Lines 276 to 278 in 4043166

it 'returns a valid 201 response', swagger_strict_schema_validation: true do |example|
assert_response_matches_metadata(example.metadata)
end

This would allow us to pass in other options to it, which is a feature I'd like to have. For example, this would allow us to do the following:

run_test! focus: true

I could put this up in a separate PR after this one is merged if folks would prefer that, but I thought I'd mention it here since we're already adding the **options parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the idea 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I put up a sub-PR here if that is helpful:
IlkhamGaysin#1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@romanblanco I'm gonna merge changes of @a-lavis to my PR to push it all together
are you ok with that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@IlkhamGaysin, sounds good!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@IlkhamGaysin, I'll need you to rebase your branch once more because of the recent merge.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@romanblanco done!

* pass `options` to `it`

* update README

* Update README.md with one more example

Co-authored-by: Ilkham Gaysin <ilgamgaysin@gmail.com>

---------

Co-authored-by: Ilkham Gaysin <ilgamgaysin@gmail.com>
Copy link
Member

@romanblanco romanblanco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@IlkhamGaysin, @a-lavis, thank you guys!

@romanblanco romanblanco merged commit 68aaeb6 into rswag:master Mar 31, 2023
10 checks passed
@romanblanco romanblanco removed this from the Gem 2.10.0 milestone May 8, 2023
@romanblanco
Copy link
Member

Hey @IlkhamGaysin,

I'm trying to use this feature in a new API spec I'm working on, and I'm running into a problem with fields that are not required.

In README.md we have an example of how to mark required fields:

rswag/README.md

Line 117 in 0edbe25

required: [ 'title', 'content' ]

Using the same approach to describe pagination schema:

LINKS = {
  type: 'object',
  properties: {
    first: {
      type: 'string'
    },
    last: {
      type: 'string'
    },
    previous: {
      type: 'string'
    },
    next: {
      type: 'string'
    },
  },
  required: ['first', 'last']
}.freeze

If I have an example request that only has one page, I'm getting back error message:

...
       The property '#/links' did not contain a required property of 'previous' in schema 3efaf5d2-6a88-5939-8604-d152fa0e4574#
       The property '#/links' did not contain a required property of 'next' in schema 3efaf5d2-6a88-5939-8604-d152fa0e4574#
...

I suspect, that parametrizing JSON::Validator.fully_validate with { strict: true } does not take the required field into consideration.

I'm wondering if I'm using it wrong or if it is a bug. Any ideas?

@a-lavis
Copy link
Contributor

a-lavis commented Jul 19, 2023

@romanblanco

The strict option for the json-schema gem does two things:

  1. All Properties are Required
  2. Additional Properties are Not Permitted

Because of number 1, it will ignore what you set in the required field. All fields are required regardless.

In the future, we should take advantage of this new feature in json-schema: voxpupuli/json-schema#494

That will allow us to either make All Properties Required or make Additional Properties Not Permitted.

@mattpolito
Copy link
Collaborator

We ran into that as well when we manually made json-schema strict. The findings were the same as @a-lavis. Strict schema validation makes all declared keys required regardless of what is defined as 'required' in rswag.

@IlkhamGaysin
Copy link
Contributor Author

IlkhamGaysin commented Sep 14, 2023

@mattpolito @romanblanco Hi guys, sorry for the delay, I've been on vacation. The initial idea of the strict option is to strictly ask for the presence of all options listed. I can apply the new flexible changes from json-schema if you would like to have them.
To do that I need:

  • update the dependency in the rswag
  • add a new option and replace the existing one to fit the json-schema options

Are you ok with me doing that?

@IlkhamGaysin IlkhamGaysin mentioned this pull request Sep 14, 2023
7 tasks
@IlkhamGaysin
Copy link
Contributor Author

Here is the first PR guys
#675
@romanblanco @mattpolito

@IlkhamGaysin
Copy link
Contributor Author

@mattpolito @romanblanco hey guys, here is the fix that will make the restrictions more flexible.
#721

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Strict schema validation
6 participants