这是indexloc提供的服务,不要输入任何密码
Skip to content

fix: check actual type in schema ref when enforcing non-nullable object schemas #599

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

Merged
merged 1 commit into from
Oct 7, 2024

Conversation

lsdch
Copy link
Contributor

@lsdch lsdch commented Oct 7, 2024

ca4b5f6 intends to prevent non-scalar types to be nullable in their schema definition. It does so by checking that the provided schema is not a ref.

However it is possible to end up having a ref schema for a scalar type, which should be nullable. This PR fixes this by actually checking that the schema type of the ref is object.

Example scalar type that is purposely registered as a ref :

type InstitutionKind string

const (
	Lab                InstitutionKind = "Lab"
	FoundingAgency     InstitutionKind = "FundingAgency"
	SequencingPlatform InstitutionKind = "SequencingPlatform"
	Other              InstitutionKind = "Other"
)

var InstitutionKindValues = []InstitutionKind{
	Lab,
	FoundingAgency,
	SequencingPlatform,
	Other,
}

// Register enum in OpenAPI specification
func (u InstitutionKind) Schema(r huma.Registry) *huma.Schema {
  if r.Map()["InstitutionKind"] == nil {
    schemaRef := r.Schema(reflect.TypeOf(""), false, "InstitutionKind")
    schemaRef.Title = "InstitutionKind"
    for _, v := range InstitutionKindValues {
      schemaRef.Enum = append(schemaRef.Enum, string(v))
    }
    r.Map()["InstitutionKind"] = schemaRef
  }

	return &huma.Schema{Ref: "#/components/schemas/InstitutionKind"}
}

Summary by CodeRabbit

  • Bug Fixes
    • Updated schema handling to prevent nullability for object types, ensuring correct representation of null values.
    • Introduced error handling for cases where object types are incorrectly marked as nullable.

Copy link

coderabbitai bot commented Oct 7, 2024

Walkthrough

The changes in this pull request modify the SchemaFromField function in the schema.go file to refine the handling of nullable fields. The condition for nullable fields has been updated to restrict nullability to scalar types only, ensuring that object types cannot be marked as nullable. If an object type is marked as nullable, the function will now panic with an error message. Additionally, a comment has been added to clarify the rationale behind this change.

Changes

File Change Summary
schema.go Modified SchemaFromField to restrict nullability to scalar types only; added panic for object types.

Possibly related PRs

  • fix(schema): add dereferencing for SchemaFromType #518: This PR modifies the SchemaFromType function in schema.go, which is closely related to the SchemaFromField function modified in the main PR, as both functions are involved in schema generation and handling types.
  • fix: nullable schemas for arrays/slices #527: This PR updates the schemaFromType function to allow nullable items in arrays, which relates to the handling of nullability in the main PR's changes to the SchemaFromField function, as both deal with nullable schema definitions.

Suggested reviewers

  • danielgtaylor

🐰 In the schema's field, a change so bright,
Nulls for objects, we say, "Not right!"
Scalars can play in the nullable game,
While objects must stay, oh, what a shame!
With logic refined, our code's now a delight,
Hopping along, everything feels just right! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Oct 7, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 92.82%. Comparing base (6e119e8) to head (86a0faa).
Report is 10 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #599      +/-   ##
==========================================
+ Coverage   92.80%   92.82%   +0.02%     
==========================================
  Files          22       22              
  Lines        3903     3915      +12     
==========================================
+ Hits         3622     3634      +12     
  Misses        236      236              
  Partials       45       45              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (3)
schema.go (3)

Line range hint 572-578: Consider improving error message and handling

The error message for unsupported nullable fields is informative, but it could be enhanced to provide more context and guidance.

Consider the following improvements:

  1. Use fmt.Errorf with %w to wrap the error, allowing for better error handling up the call stack.
  2. Provide more context in the error message, such as suggesting alternatives or explaining why it's not supported.

Here's an example of how you could refactor this:

-panic(fmt.Errorf("nullable is not supported for field '%s' which is type '%s'", f.Name, fs.Ref))
+return nil, fmt.Errorf("nullable is not supported for field '%s' of type '%s': %w", f.Name, fs.Ref, ErrUnsupportedNullableObject)

This change assumes you've defined an ErrUnsupportedNullableObject error type elsewhere in your package. You'd also need to update the function signature to return (*Schema, error) instead of just *Schema.

Consider creating a custom error type for schema-related errors. This would allow for more structured error handling throughout the application. For example:

type SchemaError struct {
    Field string
    Type  string
    Msg   string
}

func (e *SchemaError) Error() string {
    return fmt.Sprintf("schema error for field '%s' of type '%s': %s", e.Field, e.Type, e.Msg)
}

This would allow for more detailed error reporting and easier error handling in the calling code.


Line range hint 479-583: Consider refactoring SchemaFromField for improved maintainability

The SchemaFromField function is quite long and handles many different schema properties. This can make it difficult to maintain and extend in the future.

Consider breaking down this function into smaller, more focused functions. Here are some suggestions:

  1. Create separate functions for handling different types of tags (e.g., handleStringTags, handleNumericTags, etc.).
  2. Group related schema property settings together.
  3. Use a builder pattern to construct the schema, which could make the code more readable and easier to extend.

Here's a sketch of how this refactoring could look:

func SchemaFromField(registry Registry, f reflect.StructField, hint string) *Schema {
    fs := registry.Schema(f.Type, true, hint)
    if fs == nil {
        return fs
    }
    
    schemaBuilder := NewSchemaBuilder(fs)
    
    schemaBuilder.
        HandleStringTags(f).
        HandleNumericTags(f).
        HandleArrayTags(f).
        HandleObjectTags(f)
    
    handleNullableField(registry, f, schemaBuilder)
    
    return schemaBuilder.Build()
}

func handleNullableField(registry Registry, f reflect.StructField, builder *SchemaBuilder) {
    if builder.Schema.Nullable && isNullableObjectSchema(registry, builder.Schema) {
        // Handle nullable object schema...
    }
}

This approach would make the function easier to read, maintain, and extend in the future. It would also make it easier to add new schema properties or tag handling without cluttering the main function.

Consider introducing a SchemaBuilder struct that encapsulates the logic for building a schema. This could provide a more fluent interface for schema construction and make it easier to add new schema properties in the future.


Line range hint 1-583: Summary of review for schema.go

The changes in this file successfully address the issue described in the PR objectives, improving the handling of nullable object schemas. However, there are several opportunities for further improvement:

  1. The main change could be refactored into a separate function for better readability and reusability.
  2. Error handling for unsupported nullable fields could be improved to provide more context and better integration with error handling patterns.
  3. The SchemaFromField function is quite long and complex, and could benefit from being broken down into smaller, more focused functions.
  4. Consider introducing a SchemaBuilder pattern to improve the overall structure and extensibility of the schema generation code.

These suggestions aim to improve the maintainability, readability, and robustness of the code. While the current changes are functional and address the immediate issue, implementing these refactoring suggestions could lead to a more sustainable codebase in the long term.

Consider creating a follow-up task to implement the suggested refactorings, as they go beyond the scope of the current PR but could significantly improve the codebase.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 0fcf21d and 86a0faa.

📒 Files selected for processing (1)
  • schema.go (1 hunks)
🧰 Additional context used
🔇 Additional comments (1)
schema.go (1)

Line range hint 564-571: Improved handling of nullable object schemas

The changes introduce a more precise check for nullable object schemas. This adjustment addresses the issue mentioned in the PR objectives, where scalar types registered as references were incorrectly treated as non-nullable.

Consider extracting the type check into a separate function for better readability and reusability. For example:

+func isNullableObjectSchema(registry Registry, schema *Schema) bool {
+    return schema.Ref != "" && registry.SchemaFromRef(schema.Ref).Type == "object"
+}

 func SchemaFromField(registry Registry, f reflect.StructField, hint string) *Schema {
     // ... (existing code)
-    if fs.Nullable && fs.Ref != "" && registry.SchemaFromRef(fs.Ref).Type == "object" {
+    if fs.Nullable && isNullableObjectSchema(registry, fs) {
         // ... (existing code)
     }
     // ... (rest of the function)
 }

This refactoring would make the code more maintainable and easier to understand at a glance.

Let's verify if there are any other occurrences of similar checks in the codebase:

This will help us identify if this check needs to be applied consistently across the codebase.

✅ Verification successful

Verification successful: The nullable object schema check in schema.go is correctly implemented, and no similar patterns exist elsewhere in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar nullable object schema checks
rg --type go 'Nullable.*Ref.*Type.*"object"'

Length of output: 141

Copy link
Owner

@danielgtaylor danielgtaylor left a comment

Choose a reason for hiding this comment

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

Makes sense, thanks!

@danielgtaylor danielgtaylor merged commit f36c343 into danielgtaylor:main Oct 7, 2024
5 checks passed
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.

2 participants