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

feat: Add NullableFromType option #110

Closed
wants to merge 8 commits into from

Conversation

candiduslynx
Copy link
Contributor

Instead of #106.

Allows to define property nullability based on the field type instead of the jsonschema:"nullable" tag.
Useful when the types included are referenced from 3rd party packages.

Consider the following example:

type TestNullableField struct {
	A *string `json:"a"`
}

The following JSON is a perfectly valid value for Go:

{
  "a": null
}

The schemas produced for the TestNullableField struct are:

Old behavior (`NullableFromType:false`)

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://github.com/invopop/jsonschema/test-nullable-field",
  "$ref": "#/$defs/TestNullableField",
  "$defs": {
    "TestNullableField": {
      "properties": {
        "a": {
          "type": "string"
        }
      },
      "additionalProperties": false,
      "type": "object",
      "required": [
        "a"
      ]
    }
  }
}

New behavior (`NullableFromType:true`)

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://github.com/invopop/jsonschema/test-nullable-field",
  "$ref": "#/$defs/TestNullableField",
  "$defs": {
    "TestNullableField": {
      "properties": {
        "a": {
          "oneOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "additionalProperties": false,
      "type": "object",
      "required": [
        "a"
      ]
    }
  }
}

In both cases a is required, but the old behavior will fail for {"a":null} whereas the new one will succeed.

@candiduslynx
Copy link
Contributor Author

@samlown this is a less intrusive implementation for #106.
It will kick in the new logic only if the NullableFromType option is used.

@candiduslynx candiduslynx changed the title feat: Add Reflector.NullableFromType option feat: Add NullableFromType option Oct 6, 2023
kodiakhq bot pushed a commit to cloudquery/codegen that referenced this pull request Oct 6, 2023
kodiakhq bot pushed a commit to cloudquery/cloudquery that referenced this pull request Oct 6, 2023
Closes #14027

Blocked by:
* cloudquery/codegen#39
* invopop/jsonschema#109 – merged to `cloudquery/jsonschema@cqmain`
* invopop/jsonschema#110 – merged to `cloudquery/jsonschema@cqmain`

I propose reviewing the annotations along with tests, as the JSON schemas generated are just too long to grasp visually.
@candiduslynx
Copy link
Contributor Author

candiduslynx commented Oct 9, 2023

@samlown I hope you're doing well in these uneasy days.

I've implemented the changes in #109 & #110 & tested them via a fork at github.com/cloudquery/jsonschema@cqmain.

The results of using the updated code along with NullableFromType: true, RequiredFromJSONSchemaTags: true is something I'm quite satisfied with.
You can find an example in schema.json file. It includes both the nullable properties based on the field type & the clashing types (you can easily find them searching for -1" suffix)).

Could you please give an estimate when these PRs (#109 & #110) could be merged? It'll be far easier to pin an upstream version instead of maintaining a fork for these 2 changes.

hydratim pushed a commit to hydratim/cloudquery that referenced this pull request Oct 20, 2023
Closes cloudquery#14027

Blocked by:
* cloudquery/codegen#39
* invopop/jsonschema#109 – merged to `cloudquery/jsonschema@cqmain`
* invopop/jsonschema#110 – merged to `cloudquery/jsonschema@cqmain`

I propose reviewing the annotations along with tests, as the JSON schemas generated are just too long to grasp visually.
@kop
Copy link

kop commented Dec 5, 2023

Highly interested in this PR as well. Is there anything I can help with to get this merged?

@candiduslynx
Copy link
Contributor Author

candiduslynx commented Dec 5, 2023

Highly interested in this PR as well. Is there anything I can help with to get this merged?

@kop I really hope @samlown has time to review/merge that.
In the meantime, we use a fork at github.com/cloudquery/jsonschema. It has cqmain default branch, that has the following PRs merged:

Copy link
Contributor

@samlown samlown left a comment

Choose a reason for hiding this comment

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

Sorry for the delay there! This looks good to me, thanks! I do think there should be some extra tests for those non-pointer reflection types though.

reflect.go Show resolved Hide resolved
@candiduslynx
Copy link
Contributor Author

@samlown thanks for the review!

I have an additional question here: currently the library uses oneOf to make schema nullable.
In my opinion, a better way would be anyOf:
If the schema is already nullable (e.g., it already is {"oneOf":[{...}, {"type":"null"}]}) then adding another oneOf layer will effectively make null value to fail validation, as the oneOf requires one & only one variant to be valid:

{
  "oneOf": [
    {
      "oneOf": [
        {},
        {
          "type": "null"
        }
      ]
    },
    {
      "type": "null"
    }
  ]
}

If we use anyOf this is lifted to at least one of the variants.

I can raise this as a separate issue & address it in a follow-up PR.
WDYT?

@elise-prequel
Copy link

Hi! Echoing interest in this PR -- happy to help if there's anything outstanding to get it in.

@Rosswell
Copy link

@samlown would you be able to take another look when you have a chance?

@candiduslynx
Copy link
Contributor Author

Hi @samlown!

Could you please advice if you need any assistance in maintenance of this library?

I see that the last commit here was 4 months ago, & I wonder whether this library is actually actively maintained or not.

Copy link
Contributor

@samlown samlown left a comment

Choose a reason for hiding this comment

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

Sorry for the delay, I really don't have much time at the moment. There are quite a few style things here that I'd be worried about, especially regarding public methods which are not tested (just make them private!)

// This will be performed if the schema isn't already nullable (as `oneOf` is used).
//
// {"oneOf":[s,{"type":"null"}]}
func (t *Schema) MakeNullable() {
Copy link
Contributor

Choose a reason for hiding this comment

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

These methods are inconsistent with the rest of the package, I don't think they should be in the schema.go file, nor should they be exposed publicly. Any public public method should be tested.

Also, Make in Go is typically reserved for constructors that return a new instance of a struct.

To avoid the reflect file getting too big, my suggestion would be to create a reflect_nullable.go file with these as private methods defined there.

Comment on lines +1059 to +1063
if r.NullableFromType {
nullable = isNullable(f.Type.Kind())
} else {
nullable = nullableFromJSONSchemaTags(schemaTags)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It feels like this logic should be inside its own method.

property.structKeywordsFromTags(f, st, name)
if property.Description == "" {
property.Description = r.lookupComment(t, f.Name)
unwrapped := property
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the objective here? I can't see where unwrapped is consumed, making this section much harder to read as I'd guess everything is happening on the pointer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@candiduslynx candiduslynx requested a review from samlown July 3, 2024 16:44
@candiduslynx
Copy link
Contributor Author

@samlown I'm closing this PR for 2 reasons:

  1. I no longer have access to the fork the PR was opened from
  2. It seems to me that you just don't have the capacity to get this reviewed and merged

If you'll be available for review & merging please tag me here & I'll reopen from my own fork.

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

Successfully merging this pull request may close these issues.

5 participants